- 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>
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_PASSWORDfromsrc/esp32/secrets.h. Retried every 30 s; auto reconnect is off so the retries are under control. - Setup access point
FlipDot-Setup(passwordSETUP_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 tofactory). - 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 MBfactorypartition (about 840 KB now).