# BrewPi A Python-based controller for automating the mash/brewing process of beer: it holds a pot of liquid at target temperatures (or ramps it at a target heating rate) according to a configurable mash schedule, drives a heater and stirrer, and exposes live control/telemetry over a WebSocket so a desktop GUI (or any other client) can monitor and steer the brew. The project began as a simulation/control-theory playground (Kalman filter + Smith-predictor temperature control, pot heat-diffusion model, see [`docs/NonLinMPC.pdf`](docs/NonLinMPC.pdf)) and has grown real-hardware backends for an induction hob and an RTD temperature probe. ## Architecture ``` brewpi/brewpi.py Server entry point: wires sensor, pot/plant, heater, temperature controller and stirrer together, runs them as asyncio tasks, and serves state/commands over a WebSocket. client/brewpi_gui.py PyQt5 desktop client (brewpi.ui) that connects to the server's WebSocket, displays live temperature/power/state, and lets the user set target temperature, heat rate, stirrer speed, and switch the heater/stirrer on or off. components/ Pluggable building blocks behind factories: pid/ temperature controllers (PID, Smith predictor + Kalman filter) and the math model used for prediction plant/ pot heat-diffusion model used in simulation sensor/ temperature sensors: simulated, or a real MAX31865 RTD amplifier over SPI actor/ heater and stirrer drivers: simulated, a Hendi induction hob (serial protocol), or a Pololu 1376 stirrer motor controller tasks/ Async tasks (one per component) that poll hardware/sim state at a fixed interval and publish changes through the message dispatcher. ws/ Minimal WebSocket pub/sub layer: server (single- and multi-user), client, and a keyed message dispatcher (e.g. "Sensor", "Heater", "TempCtrl", "Stirrer", "Pot"). tracer.py Logs traced variables to .mat files (for offline analysis/tuning in MATLAB/Octave, see results.m / results_tc.m). sude/ Mash schedules ("Sud" = brew/wort), each a JSON list of temperature rests ("Rasten") with target temperature, heat rate, and optional pause for user confirmation. config.json.templ Configuration template (real hardware). config.json.sim Configuration template for simulation mode. ``` ### Data flow The server (`brewpi/brewpi.py`) loads `config.json`, builds the configured sensor/heater/stirrer/controller via factories (`*Factory.create(name, ...)`) based on the `Controller` section (`sensor_name`, `heater_name`, `stirrer_name`, `pid_type`, or `"sim"` for any of them), and connects their outputs to each other's inputs via `set_on_changed` callbacks, e.g.: - sensor temperature → temperature controller's `theta_ist` - temperature controller output `y` → heater power - heater effective power → pot model power (simulation only) Each component runs inside its own `ATask` at a configurable interval (`Controller.dt`, scaled by `sim_warp_factor`) and pushes state changes onto a keyed WebSocket channel that any connected client can subscribe to and send commands back on (e.g. `{"TempCtrl": {"Soll": {"Temp": 65}}}`). ## Requirements - Python 3.8+ - Server (`brewpi/requirements.txt`): `numpy`, `scipy`, `websockets`, `dpath`, and `pyserial`/`spidev` if using real hardware backends. - Client (`client/requirements.txt`): `PyQt5`. Install with: ```bash pip install -r brewpi/requirements.txt pip install -r client/requirements.txt ``` ## Running 1. Copy a config template to `config.json` next to `brewpi.py` and adjust it. Use `config.json.sim` to run entirely in simulation (no hardware needed), or `config.json.templ` as a starting point for real hardware (set the heater/stirrer serial ports and sensor type). 2. Start the server: ```bash cd brewpi ./brewpi.py ``` This serves the WebSocket on `ws://0.0.0.0:8765`. 3. Start the GUI client and connect to the server's URI: ```bash cd client ./brewpi_gui.py ``` `brewpi/brewpi.sh` shows how this is wired up to run under a `virtualenvwrapper` environment (`$WORKON_HOME`/`$BREWPI_HOME`) on a Raspberry Pi-style deployment. ## Mash schedules Files under `sude/` describe a brew's mash schedule: pot weight, malt/water weights, stirrer speed/duty, and a list of temperature rests, each with a target temperature, heating rate, and whether to pause for user confirmation before continuing (e.g. to add malt or check gravity). ## Logging & analysis `tracer.py` (and `tasks/tracer.py`) periodically dump traced signals (temperatures, heat rates, heater power) to `logs/*.mat` files, which can be loaded in MATLAB/Octave — see `results.m` and `results_tc.m` — to evaluate and tune the controller.