Gaggimate Documentation

repository·master·Indexed 19 days ago

https://github.com/jniebuhr/gaggimate

A hardware and software upgrade kit for Gaggia espresso machines providing smart controls, temperature monitoring, and a user interface. Includes technical documentation on the NanoPbComm UART transport protocol, the NayrodPID Autotune module for boiler temperature optimization using SIMC rules, and ESP32 core dump analysis tools.

Tokens
21.6K
Snippets
65
Records
102
Agent score
75%

What's inside Gaggimate

  1. Overview of Gaggimate features

    master

    Gaggimate is a smart control upgrade for Gaggia espresso machines. It adds a display and custom electronics to provide the following capabilities:

    • Temperature Control: Real-time monitoring of the boiler temperature for optimal brewing.
    • Brew Timer: Ability to set a target duration for brewing.
    • Steam and Hot Water Mode: Dedicated control for the pump and valve to manage steam and hot water tasks.
    • Safety Features: Includes automatic shutoff mechanisms in case of system unresponsiveness or overheating.
    • User Interface: An intuitive display for machine monitoring and control.
  2. Compute PID gains using the SIMC rule

    master

    Once the plant parameters ($L$, $k'$, and $\tau_2$) are identified, the module uses Skogestad's Simple Internal Model Control (SIMC) tuning rule to calculate the parallel-form PID gains.

    The primary tuning knob is $\tau_c$ (the desired closed-loop time constant). The default is $\tau_c = 1.5 \cdot L$, which provides a robust balance between responsiveness and stability.

    Formulas used:

    • $\tau_c = 1.5 \cdot L$
    • $K_p = \frac{1}{k' \cdot (\tau_c + L)}$ (Stiffness)
    • $T_i = 4 \cdot (\tau_c + L)$ (Integral time)
    • $T_d = \tau_2$ (Derivative time)
    • $K_i = \frac{K_p}{T_i}$ (Integral gain)

    Note: $K_i$ is derived from the clamped $K_p$ to ensure the SIMC invariant $K_i = K_p/T_i$ holds even at the safety limits.

  3. Simulator limitations and caveats

    master

    While the simulator is highly capable for UI iteration, be aware of the following limitations:

    • Feature Stripping: The following features are compiled out for the desktop version: MQTT/HomeAssistant, HomeKit, mDNS, WiFi watchdogs, BLE scales, and firmware OTA.
    • Mock Accuracy: The MockController is a plausible mathematical model, not a physical one. Temperature and pressure readings are illustrative and not calibrated.
    • Single-Threaded: On macOS, the simulator runs as a single cooperative loop on the main thread to satisfy SDL requirements.
  4. Calculate feedforward gain ($K_{ff}$)

    master

    The feedforward gain $K_{ff}$ compensates for known heat loss (e.g., when cold water flows through the boiler during a shot). It is derived from the user-supplied heater wattage using the following formula:

    Kff = 1000 / heaterWattage

    Where 1000 is the TUNER_OUTPUT_SPAN (the controller's full-scale duty output). If no wattage is supplied, $K_{ff}$ remains 0 and no feedforward is applied.

  5. Manage connection status and keepalives in UART transport

    master

    Connection tracking in UART is based on activity rather than a physical link state:

    • isConnected(): Returns true if a valid frame has been received within the last LINK_TIMEOUT_MS (defaulting to 1000ms). This indicates the remote side is actively communicating.
    • Keepalives: The transport.loop() method automatically sends a keepalive (an empty datagram) every KEEPALIVE_INTERVAL_MS (defaulting to 250ms). This ensures the Endpoint can transition from an idle state to a connected state by providing liveness signals.

    Both LINK_TIMEOUT_MS and KEEPALIVE_INTERVAL_MS are defined at the top of UartTransport.h.

  6. How the Desktop simulator works

    master

    The simulator runs the real firmware from src/display/ using a native PlatformIO environment (platform = native). It uses several host shims to replace hardware-specific dependencies:

    • sim/platform/: Provides host shims for Arduino/ESP32 APIs, including String, Print, Stream, FreeRTOS (where xTaskCreate* is a no-op), FS/LittleFS/SPIFFS/SD_MMC, Preferences (NVS), WiFi, and esp_* headers. The firmware loop is driven cooperatively on the main thread.
    • sim/comms/: Mocks the GaggiMateClient BLE facade and includes a MockController that simulates thermal/hydraulic behavior (reacting to boiler/pump/relay commands and emitting telemetry like temperature, pressure, flow, and scale weight).
    • sim/driver/: Uses SdlDriver to map an SDL2 window to the LVGL display and the mouse to touch input.
    • sim/web/: Implements a non-blocking HTTP/1.1 and WebSocket server to host the WebUI, shimmed from ESPAsyncWebServer and AsyncWebSocket.
    • sim/main.cpp: The entry point that initializes the Controller and runs the cooperative loop (Controller + UI + Web Server + SDL) on the main thread.
  7. How the PhaseEndStop Algorithm determines phase stops

    master

    The PhaseEndStop algorithm identifies which parameter from a profile (pressure cp, flow fl, weight v, or cumulative pumped volume) triggered the end of a phase. It follows a hierarchical 4-step check process, prioritizing time-based stops first because they are static.

    The 4-Step Detection Process

    1. Step 1 (Immediate): Checks if the values at the exact end of the phase match the target. If they match, the parameter is identified with an estimatedDelay of 0 ms.
    2. Step 2 (First Sample): Checks the first sample of the following phase. It uses either the actual value or a calculated prediction (based on the direction of value change) to see if it matches the target. If it matches, estimatedDelay is set to the sampleInterval.
    3. Step 3 (Second Sample): Repeats Step 2 using the second sample of the following phase. If a match is found here, the algorithm sets the exitReason and triggers a delayReviewHint, which displays a REVIEW PHASE N badge to the user.
    4. Step 4 (Predictive Extrapolation): If no match is found in steps 1-3, the algorithm performs linear extrapolation up to 4 seconds (LAST_PHASE_ESTIMATED_DELAY_MAX_MS) into the future to find the target. If this fallback is required, a delayReviewHint is also set.
  8. Compare Auto vs. Manual PhaseEndStop modes

    master

    The algorithm operates in two distinct modes depending on whether the stop is being automatically adjusted.

    Auto Mode (isAutoAdjusted = true)

    Used when the system automatically determines the stop. It follows the 4-step check described in the detection process. It uses sampleInterval (from shotData.sampleInterval, defaulting to 250 ms) to calculate delays and predictions.

    Manual Mode (isAutoAdjusted = false)

    Used when a fixed delay is provided by the user. Instead of a multi-step search, it takes the last sample of the phase and performs a single prediction using the user-provided scaleDelayMs or sensorDelayMs:

    • Weight: lastW + getRegressionWeightRate() * scaleDelay / 1000
    • Pressure/Flow: lastValue + slope * sensorDelay / 1000
    • Pumped: pumpedTotal + lastFlow * sensorDelay / 1000

    If these predicted values match the targets, the exitReason is set.

  9. Identify plant parameters ($L$, $k'$, and $\tau_2$)

    master

    The autotuner identifies three key physical parameters to describe the boiler's behavior:

    SymbolMeaningDescription
    LDead timeThe delay from turning the heater on to the moment temperature visibly rises.
    k'Process gainThe maximum rate of temperature rise (in °C/s) while the heater is at 100% power.
    \tau_2Parasitic lagThe time constant representing the thermometer's delay in catching up to the water temperature.

    These are found through a four-phase state machine in Autotune.cpp:

    1. primeBaselineSlope: Establishes the noise floor.
    2. handlePreReactionSample: Detects the start of the reaction to find L.
    3. handlePostReactionSample: Captures the peak slope k'.
    4. finalizeInflection: Uses exponential peeling to recover $\tau_2$ from the curve shape.
  10. UART Frame Format and Error Handling

    master

    The UART transport uses a framed protocol to ensure data integrity over serial links. The frame structure is:

    COBS( datagram || crc16 ) || 0x00

    • COBS: Consistent Overhead Byte Stuffing is used to keep the body zero-free, allowing 0x00 to act as a frame delimiter. If a frame is garbled, the receiver skips to the next 0x00 to recover.
    • CRC-16: A CRC-16 checksum is appended to the datagram to detect bit flips.
    • Validation: Frames that have a bad CRC, are oversized, or are malformed are automatically dropped by the receiver.
  11. How the Autotune module works

    master

    The Autotune module automatically calculates four PID gains—Kp, Ki, Kd, and Kff—to optimize temperature control for a boiler. It works by applying full power to the heater (a step response) and analyzing the resulting temperature curve.

    Unlike standard autotuners that assume a simple tank model (FOPDT), this module models the boiler as an integrator with lag. This accounts for two physical realities:

    1. Integrator behavior: The heater acts as a constant power source, causing temperature to rise continuously rather than settling.
    2. Parasitic lag ($\tau_2$): The delay caused by the thermometer being mounted on the outside of the boiler shell.

    By correctly identifying this model, the autotuner can produce much more accurate derivative gains (Kd) than off-the-shelf solutions.