Keep ESP32 OTA reachable whatever the firmware does

- Rollback: override verifyRollbackLater() so a new OTA image stays
  pending until it ran 60 s with WiFi; any earlier restart rolls back
  (the Arduino core marked every image valid at boot before)
- Task watchdog (30 s) on loop(), paused during OTA uploads
- Crash counter in RTC memory: OTA-only window after a crash, 10 min
  safe mode (WiFi + OTA only) after 3 crashes in a row
- Restart when WiFi stays down for 10 min
- Home Assistant sensor 'FlipDot Display Firmware' with version/state
- logic/boot_policy with tests (60 tests 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:05:06 +02:00
1 parent 683a47c055
commit ddbdf7e65f
14 files changed
+517 -42

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
#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: OTA only for SAFE_MODE_TIME
// Other (brownout, ...) normal start, counter kept
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);