temp_controller.py, temp_controller_smith.py, and kalman.py imported matplotlib at module level just to support eyeballed-plot __main__ blocks, coupling the live server's import graph to a GUI plotting lib it never uses at runtime. Relocate those demos (and kalman_eval.py) to scripts/demos/pid/ and strip the now-unused imports/__main__ blocks from the production files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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) 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, andpyserial/spidevif using real hardware backends. - Client (
client/requirements.txt):PyQt5.
Install with:
pip install -r brewpi/requirements.txt
pip install -r client/requirements.txt
Running
-
Copy a config template to
config.jsonnext tobrewpi.pyand adjust it. Useconfig.json.simto run entirely in simulation (no hardware needed), orconfig.json.templas a starting point for real hardware (set the heater/stirrer serial ports and sensor type). -
Start the server:
cd brewpi ./brewpi.pyThis serves the WebSocket on
ws://0.0.0.0:8765. -
Start the GUI client and connect to the server's URI:
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.