2026-10-13 01:52:34 +02:00
|
|
|
// 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.
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
#include <Arduino.h>
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
#include "app.h"
|
2026-10-13 00:56:32 +02:00
|
|
|
#include "config.h"
|
2026-10-13 01:52:34 +02:00
|
|
|
#include "display/bitmap_store.h"
|
2026-10-16 22:18:58 +02:00
|
|
|
#include "display/icon_store.h"
|
2026-10-16 22:13:23 +02:00
|
|
|
#include "display/page_store.h"
|
2026-10-13 00:56:32 +02:00
|
|
|
#include "logic/status_led.h"
|
|
|
|
|
#include "nano_link/nano_flash_bridge.h"
|
2026-10-15 19:43:59 +02:00
|
|
|
#include "net/ha_api.h"
|
2026-10-13 00:56:32 +02:00
|
|
|
#include "net/home_assistant.h"
|
|
|
|
|
#include "net/wifi_ota.h"
|
2026-10-13 01:21:04 +02:00
|
|
|
#include "system/boot_target.h"
|
2026-10-13 01:05:06 +02:00
|
|
|
#include "system/firmware_guard.h"
|
2026-10-13 01:52:34 +02:00
|
|
|
#include "web/web_ui.h"
|
2026-10-13 00:12:28 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
// 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(); });
|
2026-10-12 23:55:15 +02:00
|
|
|
|
|
|
|
|
void setup()
|
|
|
|
|
{
|
2026-10-13 00:56:32 +02:00
|
|
|
Serial.begin(115200);
|
|
|
|
|
pinMode(LED_PIN, OUTPUT);
|
2026-10-13 01:34:04 +02:00
|
|
|
nano.begin(); // display off until something is shown
|
2026-10-13 01:05:06 +02:00
|
|
|
|
|
|
|
|
// Decided first, before anything that could crash
|
|
|
|
|
BootMode boot_mode = firmwareGuardBegin();
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
bitmapStoreBegin();
|
2026-10-16 22:18:58 +02:00
|
|
|
iconStoreBegin(); // the Pikachus as icons that can be edited
|
2026-10-16 22:13:23 +02:00
|
|
|
pageStoreBegin(); // the Clock page as a page that can be edited
|
2026-10-13 01:52:34 +02:00
|
|
|
appLoadSettings();
|
|
|
|
|
|
2026-10-13 01:34:04 +02:00
|
|
|
if (boot_mode == BootMode::Normal)
|
|
|
|
|
status.show("Connecting", "to WiFi...", true);
|
|
|
|
|
wifiBegin(); // starts the recovery app when it fails
|
2026-10-13 00:56:32 +02:00
|
|
|
otaBegin();
|
2026-10-13 01:05:06 +02:00
|
|
|
|
|
|
|
|
if (boot_mode == BootMode::SafeMode)
|
|
|
|
|
{
|
2026-10-13 01:21:04 +02:00
|
|
|
// Crashed too often: the recovery app, or without it only WiFi and OTA here,
|
|
|
|
|
// then try the main code once more
|
2026-10-13 01:05:06 +02:00
|
|
|
firmwareGuardSafeModeEnded();
|
2026-10-13 01:21:04 +02:00
|
|
|
rebootToRecovery(RecoveryReason::Crash);
|
2026-10-13 01:34:04 +02:00
|
|
|
status.show("Crashed 3x", "OTA only");
|
2026-10-13 01:21:04 +02:00
|
|
|
otaOnlyMode(SAFE_MODE_TIME, LED_PIN);
|
2026-10-13 01:05:06 +02:00
|
|
|
ESP.restart();
|
|
|
|
|
}
|
|
|
|
|
if (boot_mode == BootMode::PanicWindow)
|
2026-10-13 01:34:04 +02:00
|
|
|
{
|
|
|
|
|
status.show("Crashed!", "OTA open 15s");
|
2026-10-13 01:05:06 +02:00
|
|
|
otaOnlyMode(OTA_PANIC_WINDOW, LED_PIN);
|
2026-10-13 01:34:04 +02:00
|
|
|
}
|
2026-10-13 01:05:06 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
flashBridge.begin();
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 01:34:04 +02:00
|
|
|
// Shown until the time is synced over NTP
|
|
|
|
|
status.show("Getting", "the time...", true);
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
configTzTime(TIMEZONE, NTP_SERVER);
|
2026-10-16 16:17:23 +02:00
|
|
|
haApiBegin(); // Home Assistant values are read in a task of their own
|
2026-10-13 01:52:34 +02:00
|
|
|
homeAssistantBegin(controller, appSettings().home_assistant);
|
|
|
|
|
webUiBegin();
|
2026-10-13 01:05:06 +02:00
|
|
|
|
|
|
|
|
firmwareGuardStartWatchdog();
|
2026-10-12 23:55:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void loop()
|
|
|
|
|
{
|
2026-10-13 01:05:06 +02:00
|
|
|
// OTA and the firmware guard come first, so they keep working whatever happens below
|
2026-10-13 00:56:32 +02:00
|
|
|
otaLoop();
|
2026-10-13 01:05:06 +02:00
|
|
|
firmwareGuardLoop(wifiConnected());
|
2026-10-16 15:20:26 +02:00
|
|
|
loopStats.tick(micros());
|
2026-10-13 01:05:06 +02:00
|
|
|
wifiWatchdog();
|
|
|
|
|
|
|
|
|
|
homeAssistantSetFirmwareStatus(firmwareGuardStatus());
|
2026-10-13 00:56:32 +02:00
|
|
|
homeAssistantLoop();
|
2026-10-13 01:52:34 +02:00
|
|
|
webUiLoop();
|
|
|
|
|
appLoop();
|
2026-10-16 22:03:18 +02:00
|
|
|
// Once the firmware can't be rolled back anymore (the old one doesn't know icons)
|
|
|
|
|
if (!firmwareGuardPending())
|
|
|
|
|
appMigrateIcons();
|
2026-10-16 16:17:23 +02:00
|
|
|
// Home Assistant values of the shown page (read by a task, never blocks)
|
2026-10-15 19:43:59 +02:00
|
|
|
if (haApiLoop(wifiConnected()))
|
|
|
|
|
controller.pageChanged();
|
2026-10-13 00:35:28 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
// While the Nano is being flashed, nothing else may talk to it
|
|
|
|
|
if (flashBridge.handle())
|
|
|
|
|
return;
|
2026-10-15 01:29:01 +02:00
|
|
|
status.loop(); // changed flip settings, once the display is on
|
2026-10-13 00:35:28 +02:00
|
|
|
|
2026-10-15 19:43:59 +02:00
|
|
|
LocalTime now;
|
|
|
|
|
bool time_valid = appLocalTime(now);
|
2026-10-15 21:36:56 +02:00
|
|
|
appPageLoop(time_valid, now);
|
2026-10-15 19:43:59 +02:00
|
|
|
controller.update(millis(), time_valid, now, wifiConnected());
|
2026-10-16 15:20:26 +02:00
|
|
|
// 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();
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 01:52:34 +02:00
|
|
|
bool led = appSettings().status_led && statusLedLevel(millis(), wifiConnected(), nano.isOn());
|
|
|
|
|
digitalWrite(LED_PIN, led);
|
2026-10-13 00:56:32 +02:00
|
|
|
}
|