# 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).