Three bugs and two UX defects found on review, plus hardening:
- The control loop fed the task watchdog then slept exactly the watchdog
period (both 10s), tripping the TWDT fractionally late on every single
iteration. Sleep is now sliced 1s with a reset per slice. Also set
CONFIG_ESP_TASK_WDT_PANIC=y - the sdkconfig comment claimed a hung task
panics the chip, but nothing actually made that true.
- The LVGL flush called draw_bitmap (which only queues the framebuffer
switch) and released LVGL immediately, letting it render the next frame
into the buffer still being scanned out - the exact PRD 8.2 race the
double-buffer design exists to prevent. display_present_frame() now
blocks on a vsync handshake until the swap has latched.
- WiFi reconnect slept up to 30s inside the system event handler, stalling
all queued WiFi/IP events. Moved to an esp_timer.
- The setpoint label, arc, and mode/room lines rendered the control loop's
copy of state, which refreshes every 10s - every knob detent looked dead
for up to 10 seconds. The UI now reads settings live; when the fallback
ladder clamps the setpoint it shows both values. Room-select focus now
shows which room is being selected.
- z2m ISO_8601_local timestamps carry a UTC offset the parser ignored,
which would shift apparent reading age by the local offset (5-6h in the
US) and permanently trip the fallback ladder. Offset is now honoured.
- Suppressed the cosmetic "control loop stopped" fault in the window
between relay_init and the control task's first call-in at boot.
- Explicit pullups on the encoder A/B lines.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>