ESP32 firmware that weighs coffee as it grinds, stops at a target dose,
then tops up with short pulses to close the gap. main is a ground-up
FreeRTOS rewrite of the original project, which is preserved on the
legacy branch. Rationale, current status and design docs live in
.agent/; start with .agent/AGENTS.md.
Seven FreeRTOS tasks -- Scale, Dosing, Input, Display, Settings, Network,
Telemetry -- communicating over queues/mailboxes instead of one
loop()/state machine. Full design: .agent/design/rtos-architecture.md.
Deployment values are kept out of the repository. Copy include/secrets.h.example to
include/secrets.h, platformio_local.ini.example to platformio_local.ini and
webapp/.env.example to webapp/.env.local, then fill in your own PostgREST URL, device
name and ports. All three copies are gitignored.
- Web app:
http://<device-host>/(mDNS, no OTA password -- trusted home LAN only). A small React SPA (webapp/) with four tabs: Live (current weight/target, a session chart, manual dose request), Settings (calibration factor, target doses, top-up margins, button debounce, WiFi reset/reboot), History (past sessions, queried straight from PostgREST in the browser -- bypasses the device), and Advanced (debug sub-pages: tare-baseline consistency, live raw-ADC calibration, and the dosing models' fitted state/formulas). Replaces the old/consolepage and the local-onlydev/graph/dev/settingsmock tooling. - Realtime channel: one WebSocket at
/ws(typed JSON envelope) -- what the SPA's Live/Settings tabs actually talk to, and connectable directly from any other client. - Flashing: over WiFi with
pio run -t upload -e esp_wroom_02_ota(the SPA image withpio run -t uploadfs -e esp_wroom_02_ota), or over serial withpio run -t upload -e esp_wroom_02. - History data is also queryable directly against PostgREST without
going through the device at all -- see
.agent/design/postgrest-deployment.md.
lib/*Task/-- the seven FreeRTOS tasks, one per directory.lib/Messaging/-- shared message structs, queues/mailboxes, task config.lib/DosingModel/-- the main-grind, coast and topup models and the landing learner, unit-tested on the host.lib/ADS1232/-- the load-cell ADC driver, a ring-buffered fork of jousis' library.webapp/-- the SPA; seewebapp/README.mdto build and develop it.tools/display_sim/-- renders the display code on the host, with a golden-frame regression check.test/-- host unit tests (pio test -e native)..agent/-- the rewrite's working docs:AGENTS.md(rules),STATUS.md(current state),DECISIONS.md(why),ARS.md(findings log),design/(the design docs this was built from).
Built on the hardware from jousis' espresso-scale (https://gitlab.com/jousis/espresso-scale). Portable to other ESP32-based systems as long as they also use the ADS1232. Open an issue with questions.
Grinder: Eureka Mignon, modded per this Tech Dregs video. The 220V version's power supply can't run the ESP32, hence an external wall plug. The case is basic but works; the buttons (https://www.amazon.de/gp/product/B0BF51N8CK) needed some debounce filtering in software -- worth trying different buttons/pull-ups if you build this yourself.
