2026-10-13 00:56:32 +02:00
|
|
|
// ESP32 firmware: decides what the flipdot display shows (clock, Home Assistant switches)
|
|
|
|
|
// and sends it to the Nano, which drives the panels. See README.md.
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
#include <Arduino.h>
|
|
|
|
|
#include <time.h>
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
#include "BROSE9323.h"
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "display/frames.h"
|
|
|
|
|
#include "logic/display_controller.h"
|
|
|
|
|
#include "logic/status_led.h"
|
|
|
|
|
#include "nano_link/nano_flash_bridge.h"
|
|
|
|
|
#include "nano_link/nano_link.h"
|
|
|
|
|
#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 00:12:28 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
NanoLink nano(Serial2, NANO_RX_PIN, NANO_TX_PIN, RELAY_PIN, NANO_RESET_PIN);
|
|
|
|
|
BROSE9323 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, PANEL_WIDTH);
|
|
|
|
|
FlipdotFrames frames(display);
|
|
|
|
|
DisplayController controller(nano, frames);
|
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:05:06 +02:00
|
|
|
nano.begin(); // display off until the main code runs
|
|
|
|
|
|
|
|
|
|
// Decided first, before anything that could crash
|
|
|
|
|
BootMode boot_mode = firmwareGuardBegin();
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
wifiBegin();
|
|
|
|
|
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);
|
|
|
|
|
otaOnlyMode(SAFE_MODE_TIME, LED_PIN);
|
2026-10-13 01:05:06 +02:00
|
|
|
ESP.restart();
|
|
|
|
|
}
|
|
|
|
|
if (boot_mode == BootMode::PanicWindow)
|
|
|
|
|
otaOnlyMode(OTA_PANIC_WINDOW, LED_PIN);
|
|
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
flashBridge.begin();
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
nano.on();
|
|
|
|
|
display.begin(&nano.stream());
|
|
|
|
|
display.setTiming(FLIP_TIME);
|
|
|
|
|
// Shown until the time is synced
|
|
|
|
|
frames.message("Connecting...");
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
configTzTime(TIMEZONE, NTP_SERVER);
|
|
|
|
|
homeAssistantBegin(controller);
|
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());
|
|
|
|
|
wifiWatchdog();
|
|
|
|
|
|
|
|
|
|
homeAssistantSetFirmwareStatus(firmwareGuardStatus());
|
2026-10-13 00:56:32 +02:00
|
|
|
homeAssistantLoop();
|
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-13 00:35:28 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
struct tm now = {};
|
|
|
|
|
bool time_valid = getLocalTime(&now, 0);
|
|
|
|
|
controller.update(millis(), time_valid, now.tm_hour, now.tm_min);
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-13 00:56:32 +02:00
|
|
|
digitalWrite(LED_PIN, statusLedLevel(millis(), wifiConnected(), nano.isOn()));
|
|
|
|
|
}
|