Advanced Shot Stopper adds brew-by-weight to an espresso machine: a Bluetooth scale measures the drink, and an ESP32-S3 controller requests a stop near your recipe's target weight. You keep using the machine's physical brew switch.
This repository provides firmware and hardware guidance for a DIY installation. It is a work in progress, not a ready-to-install or certified kit. Development started on the La Marzocco Linea Micra; other switch types have different wiring and stop behavior.
Before installation, read Hardware and complete the applicable bench checks. The relay must remain open on startup, reset, power loss, and safety failure. Machine-specific wiring instructions are incomplete. See the Disclaimer.
| I want to… | Read |
|---|---|
| Check whether my equipment fits | Requirements and machine types |
| Prepare hardware and firmware | Hardware → Build and USB installation |
| Choose a controller and machine build | Hardware and machine build profiles |
| Connect and make the first shot | First setup and daily use |
| Change how brewing works | Features and settings |
| Integrate Advanced Shot Stopper with Home Assistant | Home Assistant setup |
| Understand an unexpected result | Troubleshooting |
| Update or recover the controller | OTA / Recovery |
| Develop or contribute | Contributing |
| Understand the test gates | Testing and validation |
| Look up a technical term | Technical glossary |
| Find a specific reference | Documentation index |
- Board: the current built-in hardware profiles use an ESP32-S3 N16R8 (16 MB flash / 8 MB PSRAM). The development relay board has a specific GPIO map; another board needs a complete reviewed profile.
- Scale: a supported Bluetooth model. Bookoo Themis Mini/Ultra were the primary development scales. Check the model and capability table; implemented protocols are not all equally tested.
- Machine: a compatible activation circuit connected only to isolated relay contacts, plus a physical switch input appropriate to the selected build.
- Installation skills: identify and verify your machine's electrical connections, build the firmware, and test the installation on a bench.
A printable enclosure is included in standard and thicker versions. PETG is recommended for the thick pair in warmer installations. Classic ESP32, boards without PSRAM, and Timemore scales are outside the current support scope.
The controller reads your brew switch separately from the machine's activation circuit. It can therefore interpret your gesture and stop brewing according to the scale, recipe guards, or time limits.
On paddle machines, the relay remains closed while brewing and opens to stop. On momentary machines, it copies button presses and sends a pulse to request a stop. Opening that relay alone does not necessarily stop a momentary machine. See the distinctions below before choosing a build.
Prefer a named hardware and machine build profile. The selected machine profile resolves the corresponding compile-time type; it is not a Web UI setting.
| Build | Value | How the controller knows the group is running |
|---|---|---|
| Paddle / latch | 0, default | Follows the maintained brew contact. Developed first for the Linea Micra. |
| Momentary | 1 | Infers flow from scale readings. Without confirmed flow, an automatic stop pulse may not be sent. |
| Momentary + reed | 2 | Uses an additional reed/hall input to observe the machine state. Preferred for momentary installations. |
Support for a switch model is not certification of a particular espresso machine. The firmware's 60 s relay limit and the ability to stop water flow are different on momentary machines; read the stop limitations.
This illustration shows the paddle-connector routing used during Micra development. It is not a wiring schematic or pinout; verify the actual machine circuit and follow the hardware safety guidance before installation.
| Need | Feature |
|---|---|
| Stop near a recipe weight | Brew by weight, with learned drip compensation |
| Use different recipes | Presets, including factory Single and Double |
| Place the cup after starting | Tare and retare |
| Handle cup removal or bumps | Cup protection |
| Handle unexpectedly fast or slow shots | Fast and Slow guards |
| Limit a shot after scale loss | A→M time guard |
| Rinse with a switch gesture | Quick rinse, with a machine-profile default |
| Hear local feedback | Alerts, subject to scale/buzzer capabilities |
| Review results | Shot history and statistics |
| Integrate with Home Assistant | Home Assistant |
| Update safely over Wi-Fi | Dual-slot OTA, with automatic rollback |
| Send events to another local receiver | Local HTTP webhooks |
Recipe settings live in Settings → Brew. Machine and scale settings are shared across recipes. Presets explains what is saved and what a Home Quick Settings change affects.
The documentation index links each settings group. Defaults are starting points; Fast and Slow guards can intentionally finish above or below the target weight.
Linear regression + offset correction (legacy in API/CSV) applies the
full final-weight error to its learned stop offset. Linear prediction +
adaptive EWMA applies a fraction, α, of that error. Both stop toward
target − learned offset; Baseline offset (g) is the saved reset value
for either method, not an additional compensation.
For an eligible EWMA shot, next offset = clamp(offset + α × (final − target), 0, 5).
For example, with target 36 g, final weight 36.20 g and offset 1.50 g:
| Current α | Next offset |
|---|---|
| 0.10 | 1.52 g |
| 0.30 | 1.56 g |
| 0.60 | 1.62 g |
A larger α responds faster to changes but also follows individual-shot noise more strongly. A smaller α smooths that noise but adapts more slowly. An underweight result reduces the offset so the next cutoff occurs later. α learns between shots; it does not filter live scale readings or guarantee better accuracy on a particular machine.
Baseline learning factor (α) is saved per preset, from 0.01 to 1.00 in steps of 0.01, initially 0.30. Saving either base preserves current learning. Reset learned stop offset to baseline resets only the selected offset; EWMA keeps its current α. Reset EWMA learning restores both saved bases, marks α initial and clears its evidence. Automatic learning may subsequently choose 0.10, 0.30, 0.50 or 1.00; a custom α remains active until a candidate earns a switch. See BBW learning for eligibility, comparison windows and persistence.
After installation and bench verification, follow First setup and daily use. It covers connecting to the controller's access point, opening the Web UI, joining home Wi-Fi, selecting a scale, and making the first shot. Factory network details are in AP → First connection.
Home and Settings use a browser claim; privileged actions also require Admin unlock with the device password. These are separate controls: Web access explains both.
- OTA updates use an inactive firmware slot and boot verification. A bootable previous image is required for rollback.
- Emergency recovery restores access or resets settings using the physical switch.
- Build and script reference covers supported ESP-IDF tooling.
- Hardware and machine build profiles defines the complete JSON profile, compatibility, override, and artifact contracts.
Remote start and rinse are disabled by default. A deliberate development build can enable them; they are not part of the default operating workflow. Remote Stop still requires Admin unlock. MQTT and persistent remote-control integrations are outside the project goals. The Web UI provides status and shot summaries, not a guaranteed real-time telemetry stream.
Use the complete index for user guides, settings, architecture, validation, and library integration. Read only the page needed for your task; parameter tables and protocol contracts have one canonical home.
A gate is the set of checks that a change must pass before it can be accepted. The project first classifies the changed files by risk, then runs the matching gate. Higher levels include the lower-level checks, so a safety-critical change receives much more scrutiny than a wording correction. The authoritative rules and exact commands are in Validation gates.
| Level | What it is for | What it checks | When it runs |
|---|---|---|---|
| R0 | Non-critical documentation and repository metadata | Documentation contracts, local links, headings, images, and known paths | For documentation-only changes and as the base of every higher gate |
| R1 | Web UI, tests, developer tooling, and isolated logic | R0 plus the normal host tests and any focused checks or generated assets relevant to the change | While developing these areas and before submitting the finished change |
| R2 | BLE, networking, saved data, OTA, and build changes | Full host coverage, ASan/UBSan, TSAN, architecture rules, Web contracts, and every built-in compatible profile pair | When a change can affect integration, concurrency, memory safety, or a firmware image |
| R3 | Relay and machine control, ISR, watchdog, boot, GPIO, partitions, remote control, or an unknown path | R2 plus stricter compiler warnings, cppcheck, build variants, and the applicable HIL/manual evidence | Before accepting any safety-critical or not-yet-classified change |
| Release | A firmware image intended for distribution or installation | The complete automated analysis plus resource budgets, applicable soak tests, HIL, and the manual test plan | For a release candidate; a passing automated R3 run alone is not release approval |
Host tests run on the developer computer or a CI runner, without an ESP32 or espresso machine. They are quick feedback for software behavior, but cannot prove that real wiring, timing, radio conditions, or machine stopping are safe. The focused profiles are:
| Profile | Purpose | When to use it |
|---|---|---|
normal |
Runs the broad functional host suite without a sanitizer | During ordinary development and in every R1-or-higher gate |
asan |
Runs the host suite with ASan and UBSan to expose memory errors and undefined operations | For R2/R3 changes and when investigating crashes or suspicious memory behavior |
tsan |
Runs concurrent host scenarios with TSAN to find unsafe access to shared data | For R2/R3 changes and whenever task or thread ownership changes |
web |
Verifies generated Web UI assets and browser-facing contracts | After changing the Web UI, its source assets, or asset generation |
ble |
Focuses the normal host suite on scale protocols and companion BLE behavior | After changing scale communication or BLE protocols |
ota |
Exercises OTA host logic plus command-line and Web resilience cases | After changing firmware-update behavior or its interfaces |
tooling |
Checks the developer command facade, risk classification, and validation contracts | After changing scripts, CI, or repository workflow rules |
In GitHub Actions, classification runs first. The fast job always performs
the R0 documentation checks. The host job then runs normal, asan, tsan,
tooling, and web for R1-R3 changes. Firmware builds run for R2/R3 pull
requests and on pushes to main, weekly scheduled runs, and manual workflow
runs. A final job checks that every job required by the classified risk passed.
These automated jobs do not flash a board, operate the relay, or replace required
HIL and manual evidence.
| Term | Meaning in this project |
|---|---|
| α (alpha) | The learning factor used by adaptive EWMA. It controls how much the latest eligible shot changes the learned stop offset. |
| A→M | Automatic-to-manual transition: brew-by-weight can no longer rely on the scale, so a time guard limits the remaining shot. |
| Admin unlock | A password-protected authorization required for privileged Web UI actions; it is separate from claiming the browser session. |
| AGPL-3.0 | GNU Affero General Public License version 3, the repository's current copyleft license. Network use of a modified version can require offering its corresponding source code. |
| AI | Artificial intelligence. The README notes its use during project development. |
| API | Application Programming Interface: a defined way for software components or external integrations to exchange commands and data. |
| ASan | AddressSanitizer, a host-test instrument that detects invalid memory access, such as reading past a buffer or using freed memory. |
| BLE | Bluetooth Low Energy, the wireless link used to communicate with supported scales. |
| BBW | Brew by weight, the feature that uses live scale measurements to request a stop near the recipe target. |
| Baseline | A saved starting value to which learned settings can be reset. It is not an extra correction added to the current value. |
| Boot / boot verification | Boot is the controller's startup process. After OTA, verification confirms that the new image started safely before it is kept. |
| Browser claim | The controller's way of assigning ordinary Home and Settings access to a browser session; it does not grant Admin privileges. |
| CI | Continuous Integration, the automated GitHub Actions checks run for pull requests, pushes to main, scheduled validation, and manual workflow runs. |
| Clamp | To restrict a calculated value to a minimum and maximum; the README's formula keeps the learned offset between 0 g and 5 g. |
| Compile-time setting | A choice fixed while firmware is built. Changing it requires a new build; it cannot be changed later in the Web UI. |
| CSV | Comma-Separated Values, a plain-text tabular format used when exporting or exchanging shot data. |
| Cutoff / stop offset | The point at which the controller requests a stop before the target weight, allowing for coffee that continues to drip. The learned offset is adjusted from previous eligible shots. |
| DIY | Do it yourself: the user assembles, installs, and validates the hardware rather than receiving a certified finished product. |
| Drip compensation | Stopping early enough to account for liquid that continues reaching the cup after the machine receives the stop request. |
| ESP32-S3 | The Espressif microcontroller family on which the controller firmware runs. |
| ESP-IDF | Espressif IoT Development Framework, the supported toolchain and software framework used to build the firmware. |
| EWMA | Exponentially Weighted Moving Average, the adaptive learning method that applies a chosen fraction of the latest final-weight error instead of the entire error. |
| Firmware | Software built to run on the ESP32-S3 controller rather than on the user's computer or browser. |
| Firmware slot | One of the flash-memory regions that can hold a bootable firmware image. OTA writes the inactive slot so the previous image remains available for rollback. |
| Flash memory | Non-volatile storage that keeps firmware when power is removed; it is distinct from working memory such as PSRAM. |
| Gate | The complete set of automated checks and, where required, physical evidence demanded for a change's risk level. |
| GPIO | General-Purpose Input/Output, a configurable electrical pin used to sense a switch or control a signal. Its assignment and polarity are safety-critical here. |
| Guard | A protective rule that stops or limits a shot when measured behavior is outside the recipe's expected conditions. |
| HIL | Hardware in the loop: testing with the real controller or representative electrical hardware so physical timing and I/O behavior can be observed. |
| Host test | A test compiled and run on a computer or CI runner, without operating the ESP32, relay, or espresso machine. |
| HTTP | Hypertext Transfer Protocol, used by the local Web UI, webhooks, and update-related interfaces. |
| ISR | Interrupt Service Routine, code that responds immediately to a hardware event and must obey stricter timing and concurrency rules. |
| K2 | The README's example name for an optional external safety relay or barrier; its actual design depends on the machine and jurisdiction. |
| MB | Megabyte, used here to describe flash and PSRAM capacity. |
| MIT License | The permissive license that applies to identified upstream portions and historical snapshots, not to the repository's current work as a whole. |
| Momentary machine / pulse | A momentary machine changes state after a brief button-like electrical signal, called a pulse, rather than following a maintained switch position. |
| MQTT | Message Queuing Telemetry Transport, a publish/subscribe messaging protocol; persistent MQTT remote control is outside this project's goals. |
| n8r4 / n16r8 | Supported memory variants: the number after n is flash capacity in MB and the number after r is PSRAM capacity in MB. |
| OTA | Over-the-air update: installing firmware over Wi-Fi into an inactive firmware slot, followed by boot verification and possible rollback. |
| Paddle / latch | A maintained physical switch: its state directly represents the requested brew state. |
| PETG | Polyethylene terephthalate glycol, a 3D-printing material recommended for the thicker enclosure parts in warmer installations. |
| Pinout / GPIO map | The documented assignment between board pins and electrical functions. A different board requires a reviewed assignment. |
| Preset | A saved recipe and its related brew settings, such as target weight and learned values. |
| PSRAM | Pseudostatic RAM, external working memory fitted to the supported ESP32-S3 boards. |
| Reed / Hall input | A magnetic sensor input used by the preferred momentary-machine configuration to observe whether the machine is running. |
| Regression | Here, a mathematical method that estimates drip behavior from previous eligible shots to improve the stop point. |
| Relay / isolated contacts | An electrically controlled switch whose contact side is kept separate from the controller side. The project uses it to request machine activation or stopping. |
| Rollback | Returning to the previous bootable firmware image when a new OTA image fails verification. |
| Sanitizer | A test-build instrument that watches a running program for classes of bugs that ordinary functional assertions may miss. |
| Soak test | A long or repeated run used to reveal resource leaks, timing drift, or failures that short tests may not expose. |
| Static analysis / cppcheck | Inspection of source code without executing it, used to detect suspicious constructs and rule violations. cppcheck is one analyzer used by the R3 gate. |
| Tare / retare | Setting the scale's current load to zero; retare repeats that action when the workflow needs a new zero reference. |
| Telemetry | Status or measurement data reported while a system runs. The Web UI is not promised as a guaranteed real-time telemetry channel. |
| TSAN | ThreadSanitizer, a host-test instrument that detects data races: unsafely coordinated simultaneous access to shared data by multiple threads. |
| UBSan | UndefinedBehaviorSanitizer, a host-test instrument that detects invalid C/C++ operations whose result the language does not define. |
| USB | Universal Serial Bus, used to install firmware, monitor serial output, and issue supported maintenance commands. |
| Watchdog | A timer that detects software that has stopped making required progress and triggers a controlled recovery. |
| Web UI | The browser-based user interface served by the controller. |
| Webhook | An HTTP request sent to another local service when a configured event occurs. |
| Wi-Fi / access point (AP) | Wi-Fi is the local wireless network technology; in access-point mode the controller creates its own temporary network for first setup or recovery. |
Use at your own risk. Anyone who builds, installs, configures, or operates firmware from this repository does so under their sole responsibility. The authors and contributors accept no liability for any harm, loss, or damage whatsoever — including but not limited to personal injury, death, property damage, equipment damage, business interruption, or psychological distress — arising from the use or misuse of this software, documentation, or any derivative work.
You are solely responsible for:
- Designing and building a correct, safe circuit — suitable relay or contact, electrical isolation, ratings, polarity, feedback, and any external safety barrier (e.g. K2) appropriate for your machine and jurisdiction.
- Installing and verifying that circuit on your equipment, including bench tests and the full manual test plan before connecting to a live espresso machine.
- Configuring the firmware correctly and safely — including GPIO assignment, compile-time pin maps, polarity, machine circuit limits, and workflow parameters — so that paddle readback, machine control, and automatic stop behavior match your hardware. GPIO and other safety-critical pin assignments are not configurable from the Web UI; they must be set in source and verified at build time (see Hardware and the FAQ).
Espresso machines are inherently hazardous. A machine such as the La Marzocco Linea Micra contains pressurized boilers, hot water, and steam at high temperature. Adding automatic or remote control — including brew-by-weight stop, relay actuation, Wi-Fi commands, and timer-based limits — can increase risk if wiring, isolation, configuration, or software behavior is wrong. Malfunction or misconfiguration could leave the brew circuit energized too long, defeat intended safety interlocks, or cause scalding, flooding, electrical hazard, or fire. Do not connect this firmware to mains-powered espresso equipment unless you understand these risks and have validated your entire system on the bench first.
This project provides software and documentation only. It does not certify, warrant, or guarantee safe operation on any machine. No statement in this repository should be interpreted as professional electrical, plumbing, or machinery safety advice.
This project was developed with substantial assistance from artificial intelligence tools. AI helped with design, implementation, documentation, and testing workflows; human review, hardware validation, and safety judgment remain the author’s responsibility. Use on real espresso equipment only after you have verified wiring, isolation, and behavior on your own setup.
Advanced Shot Stopper is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
You may use, modify, and distribute it freely, including for commercial purposes. If you distribute a modified version, or offer one to users over a network, you must provide the complete corresponding source code under the AGPL. Closed proprietary forks are not allowed.
Earlier snapshots of this repository were published under the MIT License. Those historical releases remain available to their recipients under MIT. Portions derive from tatemazer/AcaiaArduinoBLE (MIT); see LICENSE for the full terms and upstream notice.
Advanced Shot Stopper is maintained by Felipe Urzúa
(cheerpipe@gmail.com) —
Cheerpipe/AcaiaArduinoBLE.
It would not exist without tatemazer and tatemazer/AcaiaArduinoBLE. That project proved BLE brew-by-weight stop, shared the core scale protocol work, and shipped the original Shot Stopper as a plug-and-play kit. This application firmware, Web UI, paddle and momentary machine models, and safety workflow are new work on top of that foundation.
Thanks to AtomHeart-Lang for AtomHeart Eclair scale support, contributed upstream in tatemazer/AcaiaArduinoBLE#41. That work is vendored here and keeps Eclair in the supported scale set.
The vendored library also credits:
- LunarGateway (frowin)
- pyacaia (lucapinello)
- Felicita Arc: baettigp, A-TWJ
- Bookoo: philgood, same31
- AtomHeart Eclair: AtomHeart-Lang
- Decent, DiFluid, MyScale, Varia, Eureka, WeighMyBru: protocol knowledge from gaggimate/esp-arduino-ble-scales (jniebuhr et al.), reimplemented here without copying source
- Lunar 2019: jniebuhr
See the library acknowledgement.
Runtime and tooling:
