Restructure into brewpi/client/ws packages and add README
Splits the server, GUI client, and websocket layer into their own packages with dedicated requirements.txt files, and adds a README documenting the project's architecture and usage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# 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` (and `PyQt5.Qwt` for plotting).
|
||||
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
pyqt5
|
||||
Reference in New Issue
Block a user