Files
Flipdot-display/src/esp32/logic/boot_policy.h
T
mangavalkandClaude Opus 5.5 c9e0800e36 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>
2026-10-13 01:34:04 +02:00

75 lines
2.7 KiB
C

#pragma once
#include <stdint.h>
// Decides how the ESP32 starts after a reset, so OTA keeps working even when the firmware
// crashes. Pure logic, tested in test/test_esp32.
//
// Power on normal start, crash counter cleared
// Software reset normal start (e.g. after an OTA update)
// Crash (panic, watchdog) crash counter + 1, then:
// < SAFE_MODE_CRASHES OTA-only for OTA_PANIC_WINDOW, then normal
// >= SAFE_MODE_CRASHES safe mode: the recovery app, or OTA
// only for SAFE_MODE_TIME without it
// Other (brownout, ...) normal start, counter kept
//
// The recovery app (src/recovery) uses the same file to decide when to start the clock
// firmware again.
enum class ResetKind
{
PowerOn,
Software,
Crash,
Other,
};
enum class BootMode
{
Normal,
PanicWindow, // OTA only for a short time, then normal
SafeMode, // OTA only for a long time, then restart
};
struct BootDecision
{
BootMode mode;
uint8_t crash_count;
};
BootDecision decideBoot(ResetKind reset, uint8_t crash_count);
// Crash counter after safe mode ends: one more crash brings safe mode back
uint8_t crashCountAfterSafeMode();
// A new OTA firmware is accepted (no rollback) once it ran this long with WiFi
bool shouldMarkFirmwareValid(uint32_t running_ms, bool wifi_connected);
// Running this long without a crash clears the crash counter
bool shouldClearCrashCount(uint32_t running_ms);
// Why the recovery app was started (stored in NVS, shared by both apps)
enum class RecoveryReason : uint8_t
{
None = 0, // no firmware in ota_0/ota_1, or the bootloader fell back to recovery
Wifi = 1, // the clock firmware couldn't connect to WiFi
Crash = 2, // the clock firmware crashed SAFE_MODE_CRASHES times in a row
Manual = 3, // Home Assistant button
PowerCycle = 4, // switched on POWER_CYCLES_FOR_RECOVERY times quickly
};
const char *recoveryReasonText(RecoveryReason reason);
// At most 14 characters, for the flipdot display
const char *recoveryReasonShort(RecoveryReason reason);
// Power-on count: each power on within POWER_CYCLE_WINDOW of the previous one counts.
// True when the next start should be the recovery app.
bool powerCyclesRequestRecovery(uint8_t power_on_count);
// Recovery app: time to start the clock firmware again?
// Wifi reason: as soon as WiFi is connected again (but not before RECOVERY_MIN_TIME)
// other reasons: after RECOVERY_TIME (so a manual visit has time for an OTA upload)
// never when there is no clock firmware
bool recoveryShouldStartMain(RecoveryReason reason, uint32_t running_ms, bool wifi_connected,
bool main_available);