Commit Graph
222 Commits
Author SHA1 Message Date
jensandClaude Sonnet 4.6 19dc7a95bb Fix Sud hold duration units: seconds, not minutes
_advance() multiplied "duration" by 60, treating it as minutes;
stirrer_speed (percent) and stirrer_time_on/off (seconds) were already
handled correctly with no conversion. Also fix the README's "duration
minutes" wording to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 19:17:42 +02:00
jensandClaude Sonnet 4.6 b33f89a569 Adapt Sud/SudTask/demo to the new flat heat/hold schedule schema
sude/sud_0010.json moved from a list of combined ramp+hold+wait
"Rasten" with global stirrer constants to a flat "schedule" list of
explicit {"type": "heat"|"hold", ...} steps, each carrying its own
stirrer_speed/stirrer_time_on/stirrer_time_off and an optional
user_wait_for_continue (+ user_message).

- components/sud.py: Sud now tracks the current `step` instead of
  `rast`; "heat" steps enter RAMPING (advanced externally via
  temp_reached()), "hold" steps enter HOLDING and count down
  `duration` minutes (0 if omitted). Either step type can pause in
  WAIT_USER via user_wait_for_continue, surfaced through the new
  user_message attribute.
- tasks/sud.py: SudTask only pushes theta_soll/heatrate_soll on
  "heat" steps, converts each step's stirrer_time_on/off into a
  duty_cycle/cycle_time on every step change (replacing the old
  state-based RAMPING/HOLDING stirrer switching), and broadcasts
  user_message changes. Stirring is now fully schedule-driven instead
  of implicitly stopped on WAIT_USER/DONE (DONE still stops it, since
  there's no step left to read settings from).
- scripts/demos/sud/demo_sud.py: mirrors the same step-driven wiring.
- README's Mash schedules section rewritten for the new schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 19:09:43 +02:00
jensandClaude Sonnet 4.6 2845051350 Refresh design-backlog docs after Pot's gain/theta cleanup
Drop the now-stale "config.json.templ/.sim still carry dead
Model.kn/Plant.kn keys" note (they were deleted), note that Pot now
always starts at theta_amb (no separate initial-temp param) and that
dropping the gain (efficiency) factor entirely is a partial realism
regression, not just a fix. Point the config-validation item at the
gain key that's still dead in config.json.sim. Also update demo_sud.py
to match the Pot params Pot.__init__ actually reads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 18:37:03 +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 1e89a58370 Replace Pid.scale()'s mutable gain factor with a stateless process() arg
Pid.scale(k) set self.k as persistent state, mutated from outside the
class and never reset in reset(), so a stale scale factor could survive
a state-transition reset. Replace it with a plain scale=1.0 argument on
Pid.process(), and have temp_controller.py/temp_controller_smith.py
compute the heat-rate-overshoot compensation factor themselves and pass
it through process_pid() each tick instead of mutating pid_hold's state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:37:55 +02:00
jensandClaude Sonnet 4.6 519bbd485f Check off theta_amb backlog item now that it's config-driven
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:27:47 +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 6711ab2200 Remove leftover debug prints from Pot.process()
The unconditional p_loss/theta_amb/temp prints (left over from the
heat-loss-units fix) ran every tick, spamming stdout in both the
server and every demo script. Drop them and check off the matching
TODO.md item.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:03:48 +02:00
jensandClaude Sonnet 4.6 40d57dc68a Refresh README and design-backlog docs against current code
Both TODO.md backlogs and the README's architecture section still
described the Kalman-filter/heat-diffusion design that's since been
replaced by the delay-line Pot model and Kalman-free Smith predictor.
Check off the items that rewrite already fixed (heat-loss units,
transport delay vs. single-pole lag, three-Kalman-tuning), note how
the kn/sensor-noise item was resolved differently than proposed, and
add newly-spotted issues: brewpi.py wiring set_model_power
unconditionally (breaks pid_type "Normal"), kalman.py being dead code
in production, stale Kalman/kn keys in the config templates, and
debug print()s left in Pot.process().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 17:02:04 +02:00
jensandClaude Sonnet 4.6 fef0f1e2a3 Consolidate States/DEFAULT_THRESHOLDS into temp_controller_base.py
tc_constants.py only held States and DEFAULT_THRESHOLDS, both used
exclusively by temp_controller_base.py and its subclasses; fold them
into temp_controller_base.py directly and drop the now-empty module.
Also drop heat_diffusion.py, unused since pot.py switched to the
delay-line model, and the matching dead import in pot.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 16:57:35 +02:00
jensandClaude Sonnet 4.6 ac8de9da55 Add sensor variance config and smooth heat-rate estimate
[Sensor] - replace TempSensorSim's hardcoded k_noise with configurable
temp_offset/variance, accept **kwargs on Max31865 too so both sensors
share a constructor signature usable from TempSensorFactory.create()
[Temp Controller] - low-pass filter the backward-difference heat-rate
estimate (alpha=0.1) in both temp_controller.py and
temp_controller_smith.py instead of using the raw, noisy derivative
[Pot] - drop kn from demo_pot.py's params, matching the unused-field
removal already made to pot.py itself

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 15:47:24 +02:00
jensandClaude Sonnet 4.6 d02b7cbe72 Clean up after Smith predictor rewrite and drop unused/stale config
[brewpi] - drop trace vars for the dtheta_ist_* attributes removed
when temp_controller_smith.py dropped Kalman filtering
[Pot] - drop unused kn field (never read since the noise-injection
code that used it was dropped)
[GUI] - default WS connection to ws://localhost:8765

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 15:09:36 +02:00
jensandClaude Sonnet 4.6 95eacabe28 Rewrite Smith predictor controller and demo for the new Pot/no-Kalman design
temp_controller_smith.py still relied on Kalman filtering and Pot's old
get_temperature_intermediate(), both removed by the recent Pot/Kalman
simplification. Rebuild it using two Pot model copies (zero-delay and
delayed) and the same backward-difference heat rate as temp_controller.py,
combined into the classic Smith correction:
theta_ist = theta_model_fast + (theta_plant - theta_model_delay).

Also fix set_model_power() never being called, so the internal model
actually receives the controller's power output, and fill in
demo_temp_controller_smith.py to exercise it end-to-end with an extra
plot of plant vs. fast-model vs. delayed-model temperature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:42:10 +02:00
jens 5c7cffa3a7 [Pot]
- remove HeatDiffusion
- use delay line for heat propagation modeling

[Temp Controller]
- remove Kalman
2026-06-19 14:21:23 +02:00
jensandClaude Sonnet 4.6 81e66dbcd1 Move PID matplotlib demo harnesses out of production modules
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>
2026-06-19 08:21:47 +02:00
jensandClaude Sonnet 4.6 9713d66e18 Add design-flaw backlog for components/plant
Review of pot.py found a likely root cause for sim-vs-real control
mismatches: the heat-loss term carries the wrong units, masking
ambient cooling in simulation. Also notes the dropped sensor-noise
term, hardcoded ambient temp, and lag-vs-dead-time modeling gap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 23:15:32 +02:00
jensandClaude Sonnet 4.6 4256622e9d Make FSM hysteresis thresholds configurable
THRESH_HOLD_IDLE/HOLD_HEAT/IDLE_HEAT/IDLE_HOLD/HEAT_HOLD/HEAT_IDLE in
tc_constants.py were hardcoded module-level constants shared by every
controller instance, so tuning the IDLE/HOLD/HEAT hysteresis required
a code change.

Replace them with DEFAULT_THRESHOLDS plus an optional
TempCtrl.Thresholds config section, merged at construction time in
TempControllerBase.__init__ so configs that omit the section keep
behaving exactly as before. Documented the new section in
config.json.templ; left config.json.sim without it to exercise the
default-fallback path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:09:39 +02:00
jensandClaude Sonnet 4.6 b2a5652290 Add design-flaw backlog for components/pid
Tracks open issues from a review that weren't fixed as part of this
branch's Smith-predictor and dedup work: non-configurable FSM
thresholds, Pid.scale()'s gain-scheduling hack, shared Kalman tuning
across the Smith controller's 3 filters, matplotlib imported at
module level in production code, no automated tests, and unchecked
config access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:31:23 +02:00
jensandClaude Sonnet 4.6 1bf3929b60 Drop model_params from base, enable Smith predictor delay correction
TempControllerBase no longer threads model_params through (only the
Smith subclass needs it, for its own Pot model and Kalman filters).

Enable the Smith predictor's actual delay-compensated error term
(theta_err now uses theta_ist_plant - theta_ist_model_delay +
theta_ist_model instead of the plain plant reading), which is the
correction this controller is named for.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:06:35 +02:00
jensandClaude Sonnet 4.6 78ee80f96d Deduplicate TempController/TempController_smith into a shared base
Both controllers had nearly identical process_fsm(), process_pid(),
and all getters/setters; only Kalman/model setup and process()
genuinely differed, and the duplication had already drifted (the
Smith variant resets model state on entering HEAT, the plain one
didn't).

Add TempControllerBase with the shared logic and three small hooks
(init_kalman, on_state_entered, post_pid) subclasses use to plug in
their own Kalman/model behavior. Each subclass now contains only what
makes it different.

Also fixes a latent crash: TempController's constructor only accepted
(dt, params), but PidFactory/brewpi.py always call it with a third
model_params arg, so pid_type "Normal" would have raised TypeError.
The shared base's model_params=None default fixes this. Also
initializes the Smith controller's trace attributes in __init__
instead of leaving them undefined until the first process() call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:50:01 +02:00
jens 879fa77ed3 - improved plot 2022-06-28 20:19:38 +02:00
jens fd99c548e1 - improved tempsensor exception 2021-10-19 11:50:27 +02:00
jens 04d93e0f98 - improved tempsensor exception 2021-10-19 11:48:34 +02:00
jens 3a5278c6b5 - improved tempsensor exception 2021-10-19 11:43:47 +02:00
jens a267e4004e - improved tempsensor exception 2021-10-19 11:42:52 +02:00
jens e07f9b4802 - preinitialize digits 2021-10-19 11:36:09 +02:00
jens 6e7f8c2c59 - removed AtemperatureSensor::process
- task is AttributeChange
- hareden against Tempsensor exceptions
2021-10-19 11:29:11 +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
jens b8ed02eb3a - put calculated power from heater into tc model 2021-10-18 21:32:37 +02:00
jens 7f66084082 - removed debug log 2021-10-18 17:58:36 +02:00
jens 54f2322c07 - added generic tracer class
- create tracer for TC
- tracer task runs TC-Tracer
2021-10-18 17:57:40 +02:00
jens 9b997b8348 - generalized heaters 2021-10-18 14:32:56 +02:00
jens 4a26e84c6e - TC export some vars
- cleaned up test
- added model vars to test
2021-10-13 18:29:33 +02:00
jens e8fa90e753 - PID/TC goes Idle state on first theat_ist measurement 2021-10-12 17:13:19 +02:00
jens bf7ec2beaf - PID: introduced INIT State
- refactored
2021-10-12 16:47:05 +02:00
jens 55315e268e - TempratureSensor: Sim ignores temp_offset 2021-10-12 16:36:53 +02:00
jens 7eb9d16f4d - updatd constants 2021-10-12 16:19:21 +02:00
jens 3c010d7bfd - updatd constants 2021-10-12 13:24:19 +02:00
jens b4a6c70591 - init kalman_model_delay 2021-10-12 12:07:36 +02:00
jens 16cb3d3b3c - use heat diffusion for water model instead of delay line 2021-10-12 12:01:40 +02:00
jens 89ac2b8ea8 - updated tc params for test 2021-10-12 10:20:55 +02:00
jens 425567f3db - refactored
- increased Hold.Pid.kp
- decrease state change HEAT TO HOLD windows to 1°C
2021-10-12 09:11:07 +02:00
jens ff90c6d90c - added initial for filling Delay line
- added gain parameter for Pot sim
- from state change to heat init model temp with plant temp
- updated config template
2021-10-11 19:54:03 +02:00
jens 20b0363e26 - reset kalman on change to HEAT state 2021-10-11 19:04:32 +02:00
jens 9b20276012 - fixed setting model correct power in idle 2021-10-11 18:28:25 +02:00
jens 132081eeb1 - added debig 2021-10-11 16:54:58 +01:00
jens c5c39732cf - do data signal wiring at one central place
- fixed initializations
2021-10-11 13:05:18 +02:00
jens 80fa983c96 - fixed kalman test for different dt 2021-10-10 13:56:48 +02:00
jens dce3caa1eb - refactored 2021-10-09 12:27:36 +02:00