OTA steps 3-5: Update Service, Probation and Rollback, Update from SD

- EcdsaVerifier (mbedTLS, embedded public key) and EspOtaSink (writes
  the inactive app slot, esp_ota_end validates the image, then sets
  the boot partition)
- UpdateService: listens on TCP 3232 (and mDNS roro9stack-<id>) while
  Wi-Fi is Connected; streams into UpdateParser; replies OK/ERR to the
  sender; remembers the pending version so a Rollback is reported
  after the reboot
- Probation (host-tested): confirm after the first frame + 30 s + Wi-Fi
  (if configured); roll back if configured Wi-Fi never connects in 3 min
- Main loop: full-screen progress while receiving; restart once
  installed, waiting up to 60 s for Text Entry to end
- Settings > Firmware: version, Probation status, push address and
  name, and the .ota files in /updates on the SD card to install
- StorageService.runJob() runs work on the storage task (SD installs)
- wifi status prints IP and running version; RORO_TEST_CRASH test hook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-03 21:35:33 +02:00
co-authored by Claude Opus 5.5
parent 90cb6ef9b2
commit 21b3d9e422
19 changed files with 655 additions and 10 deletions
+1
View File
@@ -22,6 +22,7 @@ const RowDef kRows[] = {
{Row::OffTimeout, Kind::Choice, "Screen off after"}, {Row::Sound, Kind::Toggle, "Sound & LED"},
{Row::ProbeMacs, Kind::Toggle, "Probe MACs"}, {Row::Wifi, Kind::Page, "Wi-Fi"},
{Row::Storage, Kind::Page, "Storage"},
{Row::Firmware, Kind::Page, "Firmware"},
{Row::About, Kind::Page, "About"},
};
+1 -1
View File
@@ -11,7 +11,7 @@ namespace roro {
// values, choice lists and validation messages. Rendering and navigation live in the App.
class SettingsMenu {
public:
enum class Row { LongName, ShortName, Region, Timezone, Brightness, DimTimeout, OffTimeout, Sound, ProbeMacs, Wifi, Storage, About };
enum class Row { LongName, ShortName, Region, Timezone, Brightness, DimTimeout, OffTimeout, Sound, ProbeMacs, Wifi, Storage, Firmware, About };
enum class Kind { Text, Choice, Toggle, Slider, Page };
explicit SettingsMenu(Settings& settings) : settings_(settings) {}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
namespace roro {
// Decides when new firmware on Probation (see CONTEXT.md) has proven healthy, or has failed in a
// way that would leave no means to push a fix (Wi-Fi configured but never connecting).
class Probation {
public:
enum class Verdict { Wait, Confirm, RollBack };
static constexpr uint32_t kHealthyAfterMs = 30000;
static constexpr uint32_t kWifiDeadlineMs = 180000;
static Verdict judge(uint32_t uptimeMs, bool firstFrameDrawn, bool wifiConfigured, bool wifiConnected) {
if (wifiConfigured && !wifiConnected && uptimeMs >= kWifiDeadlineMs) return Verdict::RollBack;
if (uptimeMs < kHealthyAfterMs || !firstFrameDrawn) return Verdict::Wait;
if (wifiConfigured && !wifiConnected) return Verdict::Wait;
return Verdict::Confirm;
}
};
} // namespace roro