fix(provisioning): stop redrawing the QR on every poll, add WiFi-fail retry screen

Two related fixes that together let the post-WiFi-setup window be quiet:

1. operation.h 204/404: skip the panel redraw entirely. The panel already
   holds the right thing — setup QR if no image has ever been painted
   (img_id == -1), or a real photo if img_id >= 0. Redrawing the QR every
   15s during the bootstrap claim window put the e-ink into a perpetual
   ~20s mid-refresh loop and risked ghosting. Tests updated to assert
   no redraw on either sub-case.

2. main.cpp WiFi-fail path: drop the epd_fill(RED) + 3s delay + AP
   re-redraw sequence (~43s of e-ink work that destroyed the QR mid-flow)
   and replace with a single repaint of a new "Connection Failed — try
   again" Step 1/2 screen with red accents. gen_screens.py grows a
   gen_ap_retry() variant that recolors yellow → red and swaps the
   header/QR labels; the result is shipped as ap_bg_retry.bin alongside
   ap_bg.bin in LittleFS. epd.h exposes epd_draw_ap_screen_retry().
This commit is contained in:
2026-05-08 23:43:59 -04:00
parent e7f0a11ad3
commit fb4c5ff5d3
9 changed files with 105 additions and 44 deletions
+30 -8
View File
@@ -130,20 +130,41 @@ void test_fw03_304_no_redraw() {
TEST_ASSERT_EQUAL_UINT64(30000ULL * 1000ULL, g_sleep_us);
}
// FW-04: 204 — show_setup_qr called exactly once
void test_fw04_204_shows_setup_qr() {
// FW-04: 204 with no prior image — panel already shows the setup QR from
// provisioning; the firmware MUST NOT redraw it on every 15s bootstrap poll
// or the e-ink panel sits in a perpetual mid-refresh loop.
void test_fw04_204_no_prior_image_does_not_redraw() {
g_http_get_code = 204;
// currentImgId defaults to -1 from prefs.clear() in reset_state()
normal_operation_impl(String("mac"), http, String("url"), prefs);
TEST_ASSERT_EQUAL(1, g_show_setup_qr_count);
TEST_ASSERT_EQUAL_MESSAGE(0, g_show_setup_qr_count,
"204 must not redraw the QR — panel already holds it from provisioning");
TEST_ASSERT_EQUAL(0, g_epd_init_count);
TEST_ASSERT_EQUAL(0, g_epd_draw_image_count);
TEST_ASSERT_TRUE(g_deep_sleep_started);
}
// FW-05: 404 — show_setup_qr called exactly once
void test_fw05_404_shows_setup_qr() {
// FW-04b: 204 after a real image was previously displayed — panel holds the
// photo; firmware MUST NOT paint the setup QR over it.
void test_fw04b_204_with_prior_image_does_not_redraw() {
g_http_get_code = 204;
prefs.ints[NVS_KEY_IMG_ID] = 7; // device has previously painted image #7
normal_operation_impl(String("mac"), http, String("url"), prefs);
TEST_ASSERT_EQUAL_MESSAGE(0, g_show_setup_qr_count,
"204 must not paint the setup QR over a real photo");
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-05: 404 — same logic as 204; panel keeps whatever's there.
void test_fw05_404_does_not_redraw() {
g_http_get_code = 404;
normal_operation_impl(String("mac"), http, String("url"), prefs);
TEST_ASSERT_EQUAL(1, g_show_setup_qr_count);
TEST_ASSERT_EQUAL(0, g_show_setup_qr_count);
TEST_ASSERT_EQUAL(0, g_epd_init_count);
TEST_ASSERT_EQUAL(0, g_epd_draw_image_count);
TEST_ASSERT_TRUE(g_deep_sleep_started);
}
// FW-06a: 5xx error WITH a cached image → preserve last image and overlay a
@@ -517,8 +538,9 @@ int main(int argc, char** argv) {
RUN_TEST(test_fw01_200_response_happy_path);
RUN_TEST(test_fw02_headers_read_before_end_regression);
RUN_TEST(test_fw03_304_no_redraw);
RUN_TEST(test_fw04_204_shows_setup_qr);
RUN_TEST(test_fw05_404_shows_setup_qr);
RUN_TEST(test_fw04_204_no_prior_image_does_not_redraw);
RUN_TEST(test_fw04b_204_with_prior_image_does_not_redraw);
RUN_TEST(test_fw05_404_does_not_redraw);
RUN_TEST(test_fw06a_error_with_cache_draws_border_not_fill);
RUN_TEST(test_fw06b_error_without_cache_falls_back_to_fill);
RUN_TEST(test_fw06c_304_after_error_repaints_clean);