Commit Graph
8 Commits
Author SHA1 Message Date
jens 858249f1e7 pid: replace IDLE-as-cooling with a real COOL state
IDLE conflated two unrelated things: "controller disabled" and "needs
to cool, which this heat-only actuator fakes via zero output." Split
them apart:

- IDLE now means disabled only - the FSM forces it whenever
  enabled=False, regardless of the temperature gap, and process_pid()
  zeroes y for it same as before.
- New COOL state (mirroring HEAT) takes over the negative-diff case,
  with its own pid_cool (separate "Cool" gains, alongside Hold/Heat)
  instead of reusing pid_rate. Its output can legitimately be negative
  - it's the actuator (tasks/heater.py's actor(), already max(0, ...))
  that clamps it to 0 because *this* plant (Pot) can only heat. A
  plant with real cooling capability could one day honor it directly.
- Thresholds renamed accordingly (HoldIdle->HoldCool, HeatIdle->
  HeatCool, new CoolHold/CoolHeat); config.json/.sim/.templ and the
  hand-rolled ctrl_params in scripts/demos/pid/ and demo_sud.py
  updated with a "Cool" params section (mirrors "Heat" for now, since
  there's no real cooling actuator to tune against yet).

Also fixes a regression in demo_sud.py/the other PID demos: none of
them ever called set_enabled(True), so since enabled defaults to
False they never drove the heater at all - only caught because this
change's demo_sud.py re-run got stuck in RAMPING forever.
2026-06-21 13:41:20 +02:00
jens 2c25b8be50 Sud: server always starts empty; sude/*.json is purely a client concern
Several sude/*.json schedules can exist on disk; only one runs at a
time, chosen by a client Load. Sud no longer takes a path at all - it
starts empty (EMPTY_SUD) and the server config no longer names a
specific schedule file, removing the implicit link to sud_0010.json
in particular. Reading/writing sude/*.json is now entirely the
client's job (e.g. the GUI's Load/Save file dialogs); the server's Sud
only ever holds whatever was last Load()ed, in memory, until replaced
or the server restarts.

Updates demo_sud.py/demo_sud_save_load.py to construct an empty Sud()
and load() a schedule explicitly, matching the new constructor.
2026-06-21 10:24:45 +02:00
jensandClaude Sonnet 4.6 6fb85415eb Replace config.json's Model/Plant blocks with one shared default in brewpi.py
Both blocks were nearly identical (config.json.sim's differing M and
unused gain key being the only divergence), so there's no need for
per-config tuning here. DEFAULT_PLANT_PARAMS in brewpi.py now feeds
both the real Pot plant and the temperature controller's
Smith-predictor model. No demo scripts read these config keys, so
nothing else needed updating.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgR9tPaSzFkAwRAUyeaaCD
2026-06-20 08:06:31 +02:00
jensandClaude Sonnet 4.6 17ca79da1a Simplify Pot: drop gain/theta params, seed initial temp from ambient
[Pot] - removed the gain (efficiency) factor and the separate
"theta" initial-temperature param; Pot now starts at theta_amb and
set_power()/get_power() operate on p_in directly. Added
set_ambient_temperature() for live ambient updates. Dropped the
now-unused "theta"/"gain" keys from Model/Plant in config.json.sim
and config.json.templ (also fixes a JSON syntax error in
config.json.templ: trailing commas left over after removing "gain"
made Model/Plant fail to parse).
[Sensor] - fix missing space in TempSensorSim.temperature()'s
offset/variance expression (cosmetic, no behavior change).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 18:32:28 +02:00
jensandClaude Sonnet 4.6 7e131df4ca Add mash-schedule (Sud) automation: temp/rate sequencing + stirrer switching
brewpi.py had a dead -m/--model CLI arg and sude/*.json schedules were
pure data with no code to drive them, despite the README documenting
them as if they were already wired up.

Add components/sud.py's Sud, which loads a sude/*.json schedule and
steps through its Rasten (ramp -> hold -> optional wait-for-user ->
next rest), and tasks/sud.py's SudTask, which drives the temperature
controller's theta_soll/heatrate_soll from the current rest and
switches the stirrer between continuous (ramping) and intermittent
(holding, via stirrSpeedRast/stirrDutyRast/stirrCycleTime) operation.
Exposes progress on a new "Sud" WebSocket channel and accepts
Start/Confirm commands. Enabled via a new optional top-level "sud"
config key (see config.json.templ/.sim).

"Reached target" is detected via abs(theta_ist - theta_soll_set) <
tolerance rather than tc.state == HOLD, since the latter can still
read HOLD left over from the previous rest for one tick after a new
target is pushed (tc.state only updates on the controller's own,
independently-scheduled process() tick).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:54:42 +02:00
jensandClaude Sonnet 4.6 80da55da85 Make ambient temperature configurable and wire it everywhere it was hardcoded
brewpi.py passed a literal 20 as theta_amb to Pot, and
temp_controller_smith.py's two internal Pot models (and every demo's
Pot/TempController instantiation) silently relied on Pot's default of
20 instead. Add a top-level ambient_temperature key to
config.json(.templ/.sim), thread it through brewpi.py into both the
real plant and the Smith predictor's models via a new theta_amb param
on TempController, and make the demos pass it explicitly too.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:16:57 +02:00
jensandClaude Sonnet 4.6 2566140c2b Fix Python 3.10+/websockets compatibility, add CLI args and .gitignore
- ws/message.py, ws/server/ws_server_multi_user.py: drop the loop= kwarg
  and replace asyncio.wait(coroutines) with asyncio.gather(), both of
  which were removed/forbidden in Python 3.10+.
- brewpi/requirements.txt: pin websockets==10.4, the latest version that
  still supports this code's serve()/handler API.
- config.json.sim: move Model to the top level and add the missing gain
  field so it matches the schema brewpi.py and config.json.templ expect.
- brewpi/brewpi.py: add -c/-m/-s/-d CLI args for selecting config/model
  files, simulation mode, and debug.
- .gitignore: exclude __pycache__, logs, the local config.json, and
  editor/venv directories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:23:38 +02:00
jens bd63f2648c - refactored hendi_ctrl
- fixed hend_heater
- added workaround for typedDict and Python < v3.8
2021-10-19 06:32:45 +01:00