M0 step 7: Settings, About, Storage, first-boot setup

- lib/apps_model (host-tested): SettingsMenu (rows, readable values,
  choices, validation messages) and SetupWizard (names, Region
  confirmation, timezone; saves only when finished)
- AppManager: modal Apps that Home/Back can't leave (setup wizard)
- SettingsApp: all settings plus Storage (usage, erase SD behind a
  dialog) and About (version, node id, battery, memory, uptime) pages,
  replacing the temporary Diagnostics App
- SetupApp: first-boot wizard, opened modally until SetupDone
- Node id and default names derived from the MAC like Meshtastic
- Widget demo is now hidden (About, then w)
- lib_ldf_mode = deep+ so libraries see each other's headers

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-02 13:47:03 +02:00
co-authored by Claude Opus 5.5
parent 05c9d9cc56
commit 51698a8011
23 changed files with 1126 additions and 154 deletions
+8 -6
View File
@@ -3,8 +3,9 @@
#include "app_manager.h"
#include "apps/demo_app.h"
#include "apps/diagnostics_app.h"
#include "apps/launcher_app.h"
#include "apps/settings_app.h"
#include "apps/setup_app.h"
#include "event_bus.h"
#include "key_mapper.h"
#include "platform/nvs_store.h"
@@ -54,8 +55,7 @@ static RawKeys readKeys() {
static StatusInfo currentStatus() {
StatusInfo s;
s.title = kProductName;
for (auto* info : apps->visibleApps())
if (info->app == &apps->foreground()) s.title = info->title;
if (apps->foregroundTitle()) s.title = apps->foregroundTitle();
if (battery->estimator().hasReading()) s.batteryPercent = battery->estimator().percent();
s.clock = clockService->displayTime();
auto storageState = storage->state();
@@ -87,15 +87,17 @@ void setup() {
apps = new AppManager(launcher);
launcher.setManager(*apps);
apps->registerApp({"demo", "Widget demo", false, new DemoApp(bus)});
apps->registerApp({"diagnostics", "Diagnostics", false,
new DiagnosticsApp(settings, *battery, *storage, *clockService)});
apps->registerApp({"settings", "Settings", false,
new SettingsApp({settings, bus, *apps, *battery, *storage, *clockService})});
apps->registerApp({"demo", "Widget demo", true, new DemoApp(bus)});
apps->registerApp({"setup", "Setup", true, new SetupApp(settings, *apps)});
bus.subscribe(EventType::Notification, [](const Event& e) { Serial.printf("notification: %s\n", e.text); });
if (!screen.begin()) Serial.println("frame buffer allocation failed");
services.startAll(millis());
apps->begin();
if (!settings.getBool(Setting::SetupDone)) apps->openModal("setup");
Serial.printf("%s %s ready, free heap %u\n", kProductName, versionString(), ESP.getFreeHeap());
}