Files
Flipdot-display/src/esp32/main.cpp
T
mangavalkandClaude Opus 5.5 51b8d87e86 Rules can leave a manual pick alone, Clock is an editable page, fix HA attributes
- Rules: "also over a page chosen by hand" (on by default); off, a rule
  waits until the manual pick ends. Schedule store version 3, version 2
  records are read with the option on.
- The Clock page is stored at boot from the built-in one and can be
  edited; Reset (deleting it) brings back the original.
- Home Assistant attributes were never found: ArduinoJson 7.4 takes a
  char array key as a literal of its full array length.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:13:23 +02:00

110 lines
3.4 KiB
C++

// 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 <Arduino.h>
#include "app.h"
#include "config.h"
#include "display/bitmap_store.h"
#include "display/page_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();
pageStoreBegin(); // the Clock page as a page that can be edited
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();
// Once the firmware can't be rolled back anymore (the old one doesn't know icons)
if (!firmwareGuardPending())
appMigrateIcons();
// 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);
}