Show status messages on the display

- Clock firmware: 'Connecting to WiFi...' and 'Getting the time...' at
  boot, 'Crashed!' in the OTA-only modes, and 'No WiFi' with the time
  when WiFi is down for over a minute (new DisplayController screen)
- Recovery app: shows its reason, the setup WiFi name, or its IP address,
  then powers the display off (flipdots keep the image)
- StatusScreen and drawTextLines shared by both apps
- Driver: ESP begin() no longer draws 'ESP Connected'
- Tests: No WiFi screen, short reason texts (70 total)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-13 01:34:04 +02:00
1 parent 4749f9e596
commit c9e0800e36
19 files changed
+332 -48

No files matched your search

+49 -5
View File
@@ -1,16 +1,19 @@
// Recovery app (factory partition): WiFi setup portal and OTA, nothing else. Started when
// the clock firmware can't connect to WiFi, crashes repeatedly, after quick power cycles,
// from the Home Assistant button, or by the bootloader when no clock firmware works.
// It never drives the display. See README.md "Recovery app".
// It only shows short status messages on the display. See README.md "Recovery app".
#include <Arduino.h>
#include <ArduinoOTA.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include "BROSE9323.h"
#include "config.h"
#include "credentials.h"
#include "display/status_screen.h"
#include "logic/boot_policy.h"
#include "nano_link/nano_link.h"
#include "recovery_state.h"
#include "system/boot_target.h"
#include "system/system_store.h"
@@ -21,9 +24,25 @@ static const uint32_t STATION_RETRY_TIME = 30000;
// Close the setup AP this long after the stored WiFi works again
static const uint32_t AP_CLOSE_DELAY = 120000;
static const uint32_t RECOVERY_WATCHDOG_S = 60;
// Show a new status at most this often (each message power cycles the display)
static const uint32_t STATUS_MIN_INTERVAL = 30000;
RecoveryState recovery;
static NanoLink nano(Serial2, NANO_RX_PIN, NANO_TX_PIN, RELAY_PIN, NANO_RESET_PIN);
static BROSE9323 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, PANEL_WIDTH);
static StatusScreen status(display, nano);
enum class Shown
{
Nothing,
Started, // "Recovery" + reason
SetupWifi, // name of the setup WiFi
Connected, // IP address
};
static Shown shown = Shown::Nothing;
static uint32_t last_status = 0;
static uint32_t station_lost_since = 0;
static uint32_t station_connected_since = 0;
static bool watchdog_running = false;
@@ -79,6 +98,32 @@ static void updateWifi()
connectStation();
}
// Show what is going on, so you know what to do without a computer. The display is
// switched off after each message; the dots keep showing it.
static void updateDisplay()
{
Shown wanted = WiFi.status() == WL_CONNECTED ? Shown::Connected
: recovery.ap_running ? Shown::SetupWifi
: Shown::Started;
if (wanted == shown || (shown != Shown::Nothing && millis() - last_status < STATUS_MIN_INTERVAL))
return;
shown = wanted;
last_status = millis();
switch (wanted)
{
case Shown::Connected:
status.show("Recovery", WiFi.localIP().toString().c_str());
break;
case Shown::SetupWifi:
status.show("WiFi setup:", SETUP_AP_SSID);
break;
default:
status.show("Recovery", recoveryReasonShort(recovery.reason));
break;
}
}
// Double blink: recovery app is running
static void updateLed()
{
@@ -91,11 +136,8 @@ void setup()
Serial.begin(115200);
Serial.println("FlipDot recovery app");
// Display off: relay off, and no high TX line that would power the Nano
digitalWrite(RELAY_PIN, HIGH);
pinMode(RELAY_PIN, OUTPUT);
pinMode(NANO_TX_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
nano.begin(); // display off between status messages
recovery.reason = takeRecoveryReason();
main_available = mainFirmwareAvailable();
@@ -123,6 +165,7 @@ void setup()
ArduinoOTA.begin();
portalBegin();
updateDisplay();
// A hang restarts the ESP32; the bootloader then starts this recovery app again
esp_task_wdt_init(RECOVERY_WATCHDOG_S, true);
@@ -138,6 +181,7 @@ void loop()
ArduinoOTA.handle();
portalLoop();
updateWifi();
updateDisplay();
updateLed();
bool wifi_connected = WiFi.status() == WL_CONNECTED;