Without this, devices upgrading from the old buggy fill-on-error firmware
get stuck on yellow forever: the new code reads NVS_KEY_ERR_BORDER == 0
(default — the old firmware never wrote that key), so the next 304 sees
no err flag and skips the redraw. NVS img_id matches what the server is
serving, so server says "you're current" indefinitely.
Add NVS_KEY_SCHEMA_V. On boot, if stored version is below
NVS_SCHEMA_VERSION (currently 1), treat errBorder as set for this cycle
and bump schema_v. The next 304 then redraws from LittleFS (the cached
.bin survives flashing) and clears the flag.
Tests: FW-06f locks in the upgrade path (schema_v missing → redraw on
304). FW-06g asserts the migration is one-shot (post-bump → no redraw
on steady-state 304). FW-06d updated to set schema_v explicitly so it
represents the post-migration steady state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously a 5xx / timeout / malformed response fired epd_fill(COLOR_YELLOW),
which writes the yellow nibble across the entire 800×480 framebuffer and
destroys the last good image — exactly what FR38 forbids ("Last image
persists ... yellow border signals state"). The device then got stuck on a
blank yellow screen because the next 304 didn't redraw.
Changes:
- New epd_draw_image_with_border streams the cached .bin row-by-row,
overwrites border-region pixels in the row buffer, and pushes a single
composited framebuffer (same pattern as the existing setup-QR overlay).
- normal_operation_impl else-branch now redraws the cached image with a
yellow border, falling back to epd_fill only when no cache exists
(first-boot error). Sets a new NVS_KEY_ERR_BORDER flag.
- 200 and 304 paths clear NVS_KEY_ERR_BORDER. The 304 branch now
triggers a clean repaint when the err flag is set, so the device
recovers from the stuck-yellow state on the next healthy poll
without waiting for rotation to advance.
- LittleFS read mock now returns invalid File when the file doesn't
exist (matches real LittleFS), so the no-cache fallback path is
actually exercisable in tests.
Tests:
- Replaces the old test_fw06_error_fills_yellow (which locked in the
buggy fill behavior) with FW-06a..e covering: error+cache draws
border (no fill), error+no-cache falls back to fill, 304 after
error repaints clean, steady-state 304 touches nothing (the
regression the user flagged), 200 after error clears the flag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three bugs fixed:
- NVS img_id now written before epd_init/draw; new draw_needed flag in NVS
survives power-loss mid-refresh so next boot re-draws from LittleFS instead
of showing stale content
- epd_sleep() now only called when display was initialized this cycle,
preventing a 60 s wait_busy() timeout on every 304 poll
- esp_task_wdt_reset() added to wait_busy() loop so the ~20 s 6-color
refresh no longer triggers the task watchdog
Also extracts normal_operation into operation.h template and adds
a native PlatformIO test suite (16 tests) covering the full response matrix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>