diff --git a/README.md b/README.md index 6eca2b7..578a324 100644 --- a/README.md +++ b/README.md @@ -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..log`, alongside the `.mat` traces - useful for +post-mortems without needing to have been watching the console live.