docs: refresh README for this session's accumulated changes

- Architecture diagram: add client/user_config.py and
  components/sud_forecast.py; note the pid/ controllers' 4-state FSM
  (IDLE/HEAT/HOLD/COOL) and master enabled switch; expand the GUI
  client's description (ambient temp, controller enable, pot reset,
  Sud New/Load/Save/Start/Pause/Stop, forecast plot).
- Mash schedules: document Start's restart-from-finished behavior,
  Pause's freeze-and-resume semantics, and the temperature
  controller's enabled lifecycle (SudTask owns it during a run, off by
  default otherwise, GUI checkbox for manual mode).
- Fix sude/'s "heat"/"hold" wording to the actual "ramp"/"hold" keys;
  add matplotlib (demos-only) to the server requirements list.
- Logging & analysis: mention the text log alongside the .mat traces.
This commit is contained in:
2026-06-21 16:52:48 +02:00
parent c9d38e50db
commit bb818c71d4
+47 -10
View File
@@ -23,14 +23,33 @@ 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.
ambient temperature, and switch the heater/
stirrer/temperature controller on or off (plus
a "reset Pot to ambient" button, shown only
when the server's plant is simulated) - plus
Sud control (New/Load/Save/Start/Pause/Stop)
and its forecast plot on the Automatic tab.
client/user_config.py Small JSON-backed key/value store
(~/.config/brewpi/gui.json) for GUI preferences
that should survive restarts (last Sud file
dialog directory, last ambient temperature) -
distinct from the server's config.json and
from sude/*.json schedules.
components/ Pluggable building blocks behind factories:
pid/ temperature controllers: plain PID
("Normal") or PID + Smith predictor
("Smith", runs two internal pot models —
one with the plant's transport delay, one
without — to compensate for the dead time)
without — to compensate for the dead time).
Both are a 4-state FSM (IDLE/HEAT/HOLD/COOL)
with a master enabled switch - IDLE means
disabled (output forced to 0); COOL handles
a target below the current temperature with
its own gains, output forced to 0 only by
the actuator that can't act on it (e.g. a
heat-only heater), not by the controller
assuming it can't cool
plant/ pot thermal model (transport-delay line)
used in simulation
sensor/ temperature sensors: simulated, or a real
@@ -42,6 +61,11 @@ components/ Pluggable building blocks behind factories:
a client loads one of several sude/*.json
schedules onto it and it drives the
temperature controller from it
sud_forecast.py predicts how long a loaded schedule will
actually take by simulating it with the
same kind of plant/controller (and params)
as the real server - see "Forecast vs.
actual duration" below
tasks/ Async tasks (one per component) that poll
hardware/sim state at a fixed interval and
@@ -63,7 +87,7 @@ scripts/demos/ Standalone, eyeballed-plot demos (matplotlib)
`python -m scripts.demos.sud.demo_sud`.
sude/ Mash schedules ("Sud" = brew/wort), each a
JSON list of "heat"/"hold" steps with target
JSON list of "ramp"/"hold" steps with target
temperature/heat rate or hold duration,
per-step stirrer timing, and optional pause
for user confirmation.
@@ -92,8 +116,9 @@ commands back on (e.g. `{"TempCtrl": {"Soll": {"Temp": 65}}}`).
## Requirements
- Python 3.8+
- Server (`server/requirements.txt`): `numpy`, `scipy`, `websockets`, `dpath`,
and `pyserial`/`spidev` if using real hardware backends.
- Server (`server/requirements.txt`): `numpy`, `scipy`, `matplotlib` (only
for `scripts/demos/*`, not the running server itself), `websockets`,
`dpath`, and `pyserial`/`spidev` if using real hardware backends.
- Client (`client/requirements.txt`): `PyQt5`.
Install with:
@@ -183,11 +208,18 @@ controller's `theta_soll`/`heatrate_soll` from ramp steps (advancing once
`on_ratio` map directly onto the stirrer's cycle time/duty cycle). It
exposes progress (current step, remaining hold time, state, any
`user_message`) on the `"Sud"` WebSocket channel and accepts
`{"Sud": {"Start": true}}` to begin the schedule and
`{"Sud": {"Confirm": true}}` to acknowledge a pause and move to the next
step. With no schedule loaded (or all its steps run), it just sits idle -
manual `theta_soll`/`heatrate_soll` control via the GUI works the same as
ever.
`{"Sud": {"Start": true}}` to begin the schedule (also restarts one that's
already finished), `{"Sud": {"Pause": true}}` to freeze progress without
losing it, and `{"Sud": {"Confirm": true}}` to acknowledge a pause and move
to the next step.
The temperature controller has a master enabled switch (off by default -
see `components/pid/temp_controller_base.py`): `SudTask` enables it for as
long as a run is in progress (any state other than idle/finished) and
disables it again - forcing the heater output to 0 - the moment it stops or
finishes, handing control back to manual mode. The GUI's Manual tab has an
"Enabled" checkbox for driving this directly outside of a Sud run; while
disabled, its temperature/heat-rate setpoint controls are inactive.
### Forecast vs. actual duration
@@ -229,3 +261,8 @@ controller chatter in and out of `COOL` and never actually converge).
(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.
`server/brewpi.py` also mirrors everything it prints (every component's
`print()`-based status/debug output) to a plain text log,
`logs/brewpi.<timestamp>.log`, alongside the `.mat` traces - useful for
post-mortems without needing to have been watching the console live.