Files
Flipdot-display/src/recovery/README.md
T
mangavalkandClaude Opus 5.5 2b1ac12ed7 Add a web UI to the clock firmware
- Web UI on port 80: live view of the display, choose the page (clock or
  a bitmap), bitmaps (list, show, live, edit, delete), draw / image upload
  with conversion in the browser, live mode, settings, Home Assistant
- Live view from a shadow buffer in the BROSE9323 driver (shownPixel)
- Settings in NVS with the original behaviour as defaults: schedule times,
  quarter hours, yawning Pikachu, No WiFi screen, flip time, status LED,
  Home Assistant broker (restart on change)
- Bitmaps on LittleFS; new 320 KB partition, ota slots now 1.31 MB
- DisplayController: pages, live mode, settings; app.cpp holds the objects
- README for the recovery app; docs for the web UI
- Tests: settings, time parsing, bitmap format and names, schedule with
  settings, pages and live mode (95 total)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:52:34 +02:00

4.8 KiB

ESP32: recovery app

PlatformIO environment: recovery (USB only)

A small second app on the ESP32 that only does WiFi, a setup web page and OTA. It lives in the factory partition (see partitions.csv), so OTA updates of the clock firmware never change it. Its job: the ESP32 stays reachable, whatever happens to the clock firmware, so it never needs USB again.

Installing it and using it as a user is described in the main README. This file is about how it works.

Files

File Contents
main.cpp setup() / loop(): WiFi (station + setup access point), OTA, status on the display, when to start the clock firmware again
web_portal.* Setup page on port 80 and the captive portal (DNS) of the setup WiFi
recovery_state.h State shared by main.cpp and the web portal

It also uses code from the clock firmware (see build_src_filter of the recovery environment in platformio.ini):

From src/esp32/ Used for
logic/boot_policy.* Recovery reasons, when to start the clock firmware again (tested in test/test_esp32)
system/system_store.* NVS: WiFi credentials, recovery reason, clock firmware slot and version
system/boot_target.* Starting the clock firmware in ota_0 / ota_1
nano_link/nano_link.* Relay and serial link to the Nano, for the status messages
display/status_screen.*, display/text.* Two-line status messages on the display

How it starts

The clock firmware calls rebootToRecovery(reason): it stores the reason in NVS, selects the factory partition (this erases otadata) and restarts. The bootloader also falls back to the recovery app by itself when neither ota_0 nor ota_1 holds a working firmware.

Reason Set by Back to the clock firmware
Wifi clock firmware: no WiFi at boot (30 s) or for 10 min when WiFi works, after at least 1 min
Crash clock firmware: 3 crashes in a row after 10 min
Manual Home Assistant button, web UI "Recovery app" after 10 min
PowerCycle clock firmware: 3 quick power cycles after 10 min
None bootloader fallback, or a fresh install after 10 min, if a clock firmware exists

"Stay in recovery" on the setup page turns the automatic start off; "Start the clock firmware now" starts it right away. The decision is recoveryShouldStartMain() in logic/boot_policy.cpp.

startMainFirmware() selects the slot that ran last (stored in NVS by the clock firmware), or the other one. esp_ota_set_boot_partition() checks the whole image first, so a broken slot is skipped. The clock firmware then starts as PENDING_VERIFY and must pass the 60 s check (see the ESP32 README), or the bootloader comes back here.

WiFi

  • Station: the WiFi from NVS (set on the setup page), or WIFI_SSID / WIFI_PASSWORD from src/esp32/secrets.h. Retried every 30 s; auto reconnect is off so the retries are under control.
  • Setup access point FlipDot-Setup (password SETUP_AP_PASSWORD): opened when the station hasn't connected for 20 s. While a phone is connected to it, the station retries pause, because they make the access point unstable. Closed 2 minutes after the station works again (if nobody is connected).
  • Captive portal: while the access point is open, a DNS server answers every name with 192.168.4.1, and unknown pages redirect to the setup page, so phones open it by themselves.

Setup page

GET / shows the reason, WiFi state, the clock firmware in both slots and when it starts again. POST /wifi stores new WiFi credentials and connects, POST /start starts the clock firmware, POST /stay turns the automatic start off. There is no login: on your own WiFi it has the same trust as OTA, on the setup WiFi the WiFi password protects it.

Display

StatusScreen shows Recovery + reason, WiFi setup: + the access point name, or Recovery + the IP address, whichever applies. After each message the display is switched off again; the dots keep the image. New messages at most every 30 s.

OTA

ArduinoOTA works on both networks, like in the clock firmware: pio run -e esp32 -t upload (add --upload-port 192.168.4.1 on the setup WiFi). An upload from here always goes to ota_0 (the first OTA slot after factory) and starts right after.

Safety

  • A task watchdog (60 s) restarts the app when loop() hangs. After that restart the bootloader starts the recovery app again (otadata still points to factory).
  • The watchdog is paused during an OTA upload.
  • Keep this app small and simple: it is the last line of defence, and changing it needs USB (pio run -e recovery -t upload). It must fit in the 1 MB factory partition (about 840 KB now).