fix: schema migration forces clean repaint on first err-border-aware boot
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>
This commit is contained in:
@@ -31,6 +31,11 @@
|
|||||||
#define NVS_KEY_IMG_ID "img_id"
|
#define NVS_KEY_IMG_ID "img_id"
|
||||||
#define NVS_KEY_DRAW_NEEDED "draw"
|
#define NVS_KEY_DRAW_NEEDED "draw"
|
||||||
#define NVS_KEY_ERR_BORDER "err" // set when display is showing a sync-fail border; force a clean redraw on next 200/304
|
#define NVS_KEY_ERR_BORDER "err" // set when display is showing a sync-fail border; force a clean redraw on next 200/304
|
||||||
|
#define NVS_KEY_SCHEMA_V "schema_v"
|
||||||
|
|
||||||
|
// Bump when introducing a schema migration. Each new value can force a one-shot
|
||||||
|
// recovery action on first boot of the new firmware.
|
||||||
|
#define NVS_SCHEMA_VERSION 1
|
||||||
|
|
||||||
// Width of the sync-fail / no-WiFi border, in pixels.
|
// Width of the sync-fail / no-WiFi border, in pixels.
|
||||||
#define BORDER_THICKNESS_PX 16
|
#define BORDER_THICKNESS_PX 16
|
||||||
|
|||||||
@@ -67,8 +67,22 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre
|
|||||||
int32_t currentImgId = prefs.getInt(NVS_KEY_IMG_ID, -1);
|
int32_t currentImgId = prefs.getInt(NVS_KEY_IMG_ID, -1);
|
||||||
bool drawNeeded = prefs.getInt(NVS_KEY_DRAW_NEEDED, 0) != 0;
|
bool drawNeeded = prefs.getInt(NVS_KEY_DRAW_NEEDED, 0) != 0;
|
||||||
bool errBorder = prefs.getInt(NVS_KEY_ERR_BORDER, 0) != 0;
|
bool errBorder = prefs.getInt(NVS_KEY_ERR_BORDER, 0) != 0;
|
||||||
|
int schemaV = prefs.getInt(NVS_KEY_SCHEMA_V, 0);
|
||||||
prefs.end();
|
prefs.end();
|
||||||
|
|
||||||
|
// Schema migration: on first boot under err-border-aware firmware, the
|
||||||
|
// display may be holding a stale full-screen yellow from the old buggy
|
||||||
|
// epd_fill(YELLOW) path. The old firmware never wrote NVS_KEY_ERR_BORDER,
|
||||||
|
// so we'd have no other signal that a clean repaint is needed. Force one
|
||||||
|
// by treating this boot as if errBorder were set, then bump schema_v so
|
||||||
|
// it doesn't fire again.
|
||||||
|
if (schemaV < NVS_SCHEMA_VERSION) {
|
||||||
|
errBorder = true;
|
||||||
|
prefs.begin(NVS_NAMESPACE, false);
|
||||||
|
prefs.putInt(NVS_KEY_SCHEMA_V, NVS_SCHEMA_VERSION);
|
||||||
|
prefs.end();
|
||||||
|
}
|
||||||
|
|
||||||
if (currentImgId >= 0) {
|
if (currentImgId >= 0) {
|
||||||
http.addHeader("X-Current-Image-Id", String(currentImgId));
|
http.addHeader("X-Current-Image-Id", String(currentImgId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,8 +189,9 @@ void test_fw06c_304_after_error_repaints_clean() {
|
|||||||
// regression the user reported: 304 was suspected of triggering yellow fill.
|
// regression the user reported: 304 was suspected of triggering yellow fill.
|
||||||
void test_fw06d_304_steady_state_does_not_fill_yellow() {
|
void test_fw06d_304_steady_state_does_not_fill_yellow() {
|
||||||
g_http_get_code = 304;
|
g_http_get_code = 304;
|
||||||
prefs.ints[NVS_KEY_IMG_ID] = 7;
|
prefs.ints[NVS_KEY_IMG_ID] = 7;
|
||||||
LittleFS.files[IMAGE_PATH] = "IMGDATA";
|
prefs.ints[NVS_KEY_SCHEMA_V] = NVS_SCHEMA_VERSION; // post-migration steady state
|
||||||
|
LittleFS.files[IMAGE_PATH] = "IMGDATA";
|
||||||
// err_border = 0, draw_needed = 0 (defaults)
|
// err_border = 0, draw_needed = 0 (defaults)
|
||||||
|
|
||||||
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||||
@@ -214,6 +215,39 @@ void test_fw06e_200_after_error_clears_flag() {
|
|||||||
TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1));
|
TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FW-06f: First boot of err-border-aware firmware (NVS schema_v missing) on a
|
||||||
|
// device that may be displaying a stale full-screen yellow from the previous
|
||||||
|
// buggy build → migration forces a clean redraw on the next 304 and bumps
|
||||||
|
// schema_v so it doesn't fire again. Locks in the upgrade-path recovery.
|
||||||
|
void test_fw06f_schema_migration_forces_redraw_on_first_boot() {
|
||||||
|
g_http_get_code = 304;
|
||||||
|
prefs.ints[NVS_KEY_IMG_ID] = 3;
|
||||||
|
LittleFS.files[IMAGE_PATH] = "IMGDATA";
|
||||||
|
// No NVS_KEY_SCHEMA_V set → defaults to 0, triggers migration
|
||||||
|
|
||||||
|
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(1, g_epd_init_count);
|
||||||
|
TEST_ASSERT_EQUAL(1, g_epd_draw_image_count);
|
||||||
|
TEST_ASSERT_EQUAL(NVS_SCHEMA_VERSION, prefs.getInt(NVS_KEY_SCHEMA_V, -1));
|
||||||
|
TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FW-06g: Second boot under same firmware → migration does NOT fire again,
|
||||||
|
// 304 is a no-op as in the steady-state case.
|
||||||
|
void test_fw06g_schema_migration_does_not_fire_again() {
|
||||||
|
g_http_get_code = 304;
|
||||||
|
prefs.ints[NVS_KEY_IMG_ID] = 3;
|
||||||
|
prefs.ints[NVS_KEY_SCHEMA_V] = NVS_SCHEMA_VERSION;
|
||||||
|
LittleFS.files[IMAGE_PATH] = "IMGDATA";
|
||||||
|
|
||||||
|
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, g_epd_init_count);
|
||||||
|
TEST_ASSERT_EQUAL(0, g_epd_draw_image_count);
|
||||||
|
TEST_ASSERT_EQUAL(0, g_epd_fill_count);
|
||||||
|
}
|
||||||
|
|
||||||
// FW-07: NVS has saved img_id → X-Current-Image-Id header sent
|
// FW-07: NVS has saved img_id → X-Current-Image-Id header sent
|
||||||
void test_fw07_current_image_id_sent_when_saved() {
|
void test_fw07_current_image_id_sent_when_saved() {
|
||||||
prefs.ints["img_id"] = 99;
|
prefs.ints["img_id"] = 99;
|
||||||
@@ -311,6 +345,8 @@ int main(int argc, char** argv) {
|
|||||||
RUN_TEST(test_fw06c_304_after_error_repaints_clean);
|
RUN_TEST(test_fw06c_304_after_error_repaints_clean);
|
||||||
RUN_TEST(test_fw06d_304_steady_state_does_not_fill_yellow);
|
RUN_TEST(test_fw06d_304_steady_state_does_not_fill_yellow);
|
||||||
RUN_TEST(test_fw06e_200_after_error_clears_flag);
|
RUN_TEST(test_fw06e_200_after_error_clears_flag);
|
||||||
|
RUN_TEST(test_fw06f_schema_migration_forces_redraw_on_first_boot);
|
||||||
|
RUN_TEST(test_fw06g_schema_migration_does_not_fire_again);
|
||||||
RUN_TEST(test_fw07_current_image_id_sent_when_saved);
|
RUN_TEST(test_fw07_current_image_id_sent_when_saved);
|
||||||
RUN_TEST(test_fw08_no_current_image_id_when_default);
|
RUN_TEST(test_fw08_no_current_image_id_when_default);
|
||||||
RUN_TEST(test_fw09_server_interval_honored);
|
RUN_TEST(test_fw09_server_interval_honored);
|
||||||
|
|||||||
Reference in New Issue
Block a user