Files
Flipdot-display/src/recovery/README.md
T

95 lines
4.8 KiB
Markdown
Raw Normal View History

2026-10-13 01:52:34 +02:00
# 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](../../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](../../README.md#recovery-app). 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](../esp32/README.md#keeping-ota-alive)), 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).