// ESP32 firmware: decides what the flipdot display shows (clock, bitmaps, web UI, Home // Assistant) and sends it to the Nano, which drives the panels. See README.md. // The objects and the actions on them are in app.cpp; this file only sets the order. #include #include "app.h" #include "config.h" #include "display/bitmap_store.h" #include "logic/status_led.h" #include "nano_link/nano_flash_bridge.h" #include "net/ha_api.h" #include "net/home_assistant.h" #include "net/wifi_ota.h" #include "system/boot_target.h" #include "system/firmware_guard.h" #include "web/web_ui.h" // Flashing the Nano over WiFi: restart it into its bootloader, switch it off afterwards. // The controller then powers it on again with the new firmware. NanoFlashBridge flashBridge( NANO_FLASH_PORT, Serial2, [](uint32_t baud) { nano.startBootloader(baud); }, []() { nano.off(); }); void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); nano.begin(); // display off until something is shown // Decided first, before anything that could crash BootMode boot_mode = firmwareGuardBegin(); bitmapStoreBegin(); appLoadSettings(); if (boot_mode == BootMode::Normal) status.show("Connecting", "to WiFi...", true); wifiBegin(); // starts the recovery app when it fails otaBegin(); if (boot_mode == BootMode::SafeMode) { // Crashed too often: the recovery app, or without it only WiFi and OTA here, // then try the main code once more firmwareGuardSafeModeEnded(); rebootToRecovery(RecoveryReason::Crash); status.show("Crashed 3x", "OTA only"); otaOnlyMode(SAFE_MODE_TIME, LED_PIN); ESP.restart(); } if (boot_mode == BootMode::PanicWindow) { status.show("Crashed!", "OTA open 15s"); otaOnlyMode(OTA_PANIC_WINDOW, LED_PIN); } flashBridge.begin(); // Shown until the time is synced over NTP status.show("Getting", "the time...", true); configTzTime(TIMEZONE, NTP_SERVER); haApiBegin(); // Home Assistant values are read in a task of their own homeAssistantBegin(controller, appSettings().home_assistant); webUiBegin(); firmwareGuardStartWatchdog(); } void loop() { // OTA and the firmware guard come first, so they keep working whatever happens below otaLoop(); firmwareGuardLoop(wifiConnected()); loopStats.tick(micros()); wifiWatchdog(); homeAssistantSetFirmwareStatus(firmwareGuardStatus()); homeAssistantLoop(); webUiLoop(); appLoop(); // Home Assistant values of the shown page (read by a task, never blocks) if (haApiLoop(wifiConnected())) controller.pageChanged(); // While the Nano is being flashed, nothing else may talk to it if (flashBridge.handle()) return; status.loop(); // changed flip settings, once the display is on LocalTime now; bool time_valid = appLocalTime(now); appPageLoop(time_valid, now); controller.update(millis(), time_valid, now, wifiConnected()); // Checks the frames just sent; a wrong image on the Nano (and every resync interval): // the whole image again with the next update if (nanoMonitor.loop(millis(), appSettings().refresh.image * 60000UL)) controller.pageChanged(); bool led = appSettings().status_led && statusLedLevel(millis(), wifiConnected(), nano.isOn()); digitalWrite(LED_PIN, led); }