Skip to content

fix(render): clear display and reset graphs on new telemetry session - #31

Merged
ololonly merged 1 commit into
mainfrom
fix/render-degradation-session-reset
Jun 24, 2026
Merged

fix(render): clear display and reset graphs on new telemetry session#31
ololonly merged 1 commit into
mainfrom
fix/render-degradation-session-reset

Conversation

@ololonly

@ololonly ololonly commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Closes #30.

Problem

The render loop relied solely on Ratatui's diff rendering and only cleared the terminal twice (entering Debug from loading, and on the first telemetry frame). Any display pixel corrupted by a transient SPI glitch (EMI / ground noise, most likely when the machine switches its relay/heater) was therefore never repaired — Ratatui's previous-buffer still considered the cell correct — so artifacts accumulated until reboot.

Approach

Introduce a session model: when telemetry resumes after the machine has been offline (no UART frame for MACHINE_OFFLINE_TIMEOUT = 30s) or on the first frame, treat it as a new session and request a full terminal.clear(), which heals accumulated artifacts at the natural boundary (machine turned back on).

The clear is now driven by a needs_terminal_clear flag on GlobalAppState that the state machine sets and the render loop drains, replacing the scattered terminal.clear() calls in both the device and simulator loops (also clears on Debug toggle).

MachineState::reset_session() clears the graph buffers (so the chart restarts instead of jumping down from stale data) and drops last_frame/shot_started_at, so the resuming frame is treated as a fresh start and cannot emit phantom mode/shot events (which would otherwise reach MQTT and miscount cups in Home Assistant).

Bundled UX fixes

  • Wake-only first press: when the backlight has timed out, the first button press only wakes the backlight and no longer also switches screens.
  • Offline waiting screen: telemetry staleness now drives the UI via a new GlobalAppState::machine_online() helper used by draw() and render_image(). Once the machine is offline the waiting screen (rat + "waiting for machine...") is shown again, instead of a frozen Dashboard.

Tests

  • test_press_while_dark_only_wakes_backlight — first press wakes, second switches.
  • test_telemetry_resume_starts_new_session — buffers reset to a single fresh sample and no phantom events on resume.
  • Updated test_handle_button_press_short for the new backlight gating.

All 37 tests pass; cargo clippy clean on the simulator build.

Verification in simulator (cargo sim)

  1. Start → waiting screen (rat + "waiting for machine..."). Press Down → Dashboard, terminal cleared.
  2. Left/Right switch screens; Up starts a pump shot, graph fills.
  3. Wait >30s with no input → UI falls back to the offline waiting screen.
  4. Press Down again → new session: clean terminal, graphs restart from empty.
  5. After >10s idle, the first Left/Right only wakes (no screen switch).

Note: the SPI/EMI corruption itself can't be reproduced in the simulator, but the session-start self-healing (the core fix) is exercised there.

Addresses #30. The render loop relied solely on Ratatui's diff rendering
and only cleared the terminal twice (entering Debug from loading, and on
the first telemetry frame). Any display pixel corrupted by a transient SPI
glitch was therefore never repaired, so artifacts accumulated until reboot.

Introduce a session model: when telemetry resumes after the machine has
been offline (no UART frame for MACHINE_OFFLINE_TIMEOUT = 30s) or on the
first frame, treat it as a new session and request a full terminal clear,
which heals accumulated artifacts. The clear is now driven by a
`needs_terminal_clear` flag on GlobalAppState that the state machine sets
and the render loop drains, replacing the scattered `terminal.clear()`
calls in both the device and simulator loops.

`MachineState::reset_session()` also clears the graph buffers (so the chart
restarts instead of jumping down from stale data) and drops
`last_frame`/`shot_started_at` so the resuming frame is treated as a fresh
start and cannot emit phantom mode/shot events (which would otherwise reach
MQTT and miscount cups in Home Assistant).

UX fixes bundled in:
- When the backlight has timed out, the first button press only wakes the
  backlight and no longer also switches screens.
- Telemetry staleness now drives the UI: once the machine is offline the
  waiting screen (rat + "waiting for machine...") is shown again, via a new
  `GlobalAppState::machine_online()` helper used by draw() and render_image().

Tests: add coverage for wake-on-first-press and session reset (buffer clear
plus absence of phantom events); update the existing short-press test for
the new backlight gating.
@ololonly
ololonly merged commit b32f005 into main Jun 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display rendering degrades over time: stale pixels and accumulating artifacts until reboot

1 participant