fix: preserve last image and overlay yellow border on sync failure
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>
This commit is contained in:
+23
-5
@@ -66,6 +66,7 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
|
||||
prefs.begin(NVS_NAMESPACE, true);
|
||||
int32_t currentImgId = prefs.getInt(NVS_KEY_IMG_ID, -1);
|
||||
bool drawNeeded = prefs.getInt(NVS_KEY_DRAW_NEEDED, 0) != 0;
|
||||
bool errBorder = prefs.getInt(NVS_KEY_ERR_BORDER, 0) != 0;
|
||||
prefs.end();
|
||||
|
||||
if (currentImgId >= 0) {
|
||||
@@ -107,16 +108,19 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
|
||||
File r = LittleFS.open(IMAGE_PATH, "r");
|
||||
if (r) { epd_draw_image_from_file(r); r.close(); }
|
||||
|
||||
// Draw complete — clear the pending flag.
|
||||
// Draw complete — clear pending and error-border flags. The fresh
|
||||
// image fully overwrites any prior border, so error state is gone.
|
||||
prefs.begin(NVS_NAMESPACE, false);
|
||||
prefs.putInt(NVS_KEY_DRAW_NEEDED, 0);
|
||||
prefs.putInt(NVS_KEY_ERR_BORDER, 0);
|
||||
prefs.end();
|
||||
|
||||
} else if (code == 304) {
|
||||
http.end();
|
||||
// If a previous draw was interrupted (power loss mid-refresh), the image
|
||||
// file is in LittleFS and the ID is correct in NVS — just re-draw it.
|
||||
if (drawNeeded) {
|
||||
// Redraw from LittleFS if either: a previous draw was interrupted
|
||||
// (drawNeeded), or a sync-fail border is currently on screen and the
|
||||
// server is healthy again (errBorder) — repaint clean to clear it.
|
||||
if (drawNeeded || errBorder) {
|
||||
File r = LittleFS.open(IMAGE_PATH, "r");
|
||||
if (r) {
|
||||
displayInitialized = true;
|
||||
@@ -125,6 +129,7 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
|
||||
r.close();
|
||||
prefs.begin(NVS_NAMESPACE, false);
|
||||
prefs.putInt(NVS_KEY_DRAW_NEEDED, 0);
|
||||
prefs.putInt(NVS_KEY_ERR_BORDER, 0);
|
||||
prefs.end();
|
||||
}
|
||||
}
|
||||
@@ -139,10 +144,23 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
|
||||
epd_init();
|
||||
show_setup_qr(mac);
|
||||
} else {
|
||||
// Sync failed (5xx, timeout, malformed). Per FR38, the last-good image
|
||||
// must persist; only the border indicates the error. epd_draw_image_with_border
|
||||
// falls back to a full fill if the cached file is missing or wrong size,
|
||||
// so first-boot error still gets a visible signal.
|
||||
http.end();
|
||||
displayInitialized = true;
|
||||
epd_init();
|
||||
epd_fill(COLOR_YELLOW);
|
||||
File r = LittleFS.open(IMAGE_PATH, "r");
|
||||
if (r) {
|
||||
epd_draw_image_with_border(r, COLOR_YELLOW, BORDER_THICKNESS_PX);
|
||||
r.close();
|
||||
} else {
|
||||
epd_fill(COLOR_YELLOW);
|
||||
}
|
||||
prefs.begin(NVS_NAMESPACE, false);
|
||||
prefs.putInt(NVS_KEY_ERR_BORDER, 1);
|
||||
prefs.end();
|
||||
}
|
||||
|
||||
// Only power off the display if it was initialized this cycle. Calling
|
||||
|
||||
Reference in New Issue
Block a user