mangavalkandClaude Opus 5.5 a6b946e17c Weather forecast page, 16 elements per page, attribute paths, examples from the display
- Home Assistant attributes can be paths into lists (forecast.1.condition).
- Date elements can show a day ahead (day offset 0-7).
- Pages hold 16 elements (page format 6; the old formats are frozen at 8).
  Pages are reset in place (clearPage), not through a 4 KB temporary.
- Verwachting page: today (icon, temperature, max/min) and the next 2
  days with 7 x 7 icons (weer7 <condition>), from a template sensor
  (docs/home-assistant.md).
- examples/home-pages.json is exported from the display with all pages
  and their icons, private entity id parts replaced by placeholders
  (tools/export_examples.py replaces tools/make_example_pages.py).

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

Flipdot Display

Firmware for an 84 × 19 Brose flipdot display (three 28 × 19 panels) that runs as a Home Assistant–controlled clock.

The system uses two microcontrollers:

 ┌──────────────┐   WiFi / MQTT   ┌────────────────┐   UART 115200   ┌──────────────┐   GPIO   ┌──────────────┐
 │ Home         │ ◄─────────────► │ ESP32          │ ──────────────► │ Arduino Nano │ ───────► │ Brose        │
 │ Assistant    │                 │ (controller)   │  text commands  │ (driver)     │          │ flipdot      │
 └──────────────┘                 └───────┬────────┘                 └──────▲───────┘          │ panels 84x19 │
                                          │ GPIO 13                         │                  └──────▲───────┘
                                          ▼                                 │     power               │
                                   ┌─────────────┐                          │                         │
                                   │ Relay       │ ─────────────────────────┴─────────────────────────┘
                                   └─────────────┘

The relay powers both the panels and the Nano. When the relay is off, the Nano is off too, and it boots again each time the relay switches on. Only the ESP32 runs all the time.

Part Folder What it does
Arduino Nano src/nano Drives the panel's row and column electronics directly. Keeps a frame buffer and flips only the dots that changed. Takes simple text commands over serial.
ESP32 src/esp32 Connects to WiFi, gets the time over NTP, shows dynamic pages (a background per time of day with a clock, the date, Home Assistant values or text) or your own bitmaps, has a web UI, exposes switches to Home Assistant over MQTT, switches display power with a relay and supports OTA updates.
ESP32 recovery app src/recovery Small second app: WiFi setup page and OTA, so the ESP32 stays reachable when the clock firmware fails.
BROSE9323 driver lib/BROSE9323 Shared Adafruit_GFX display driver. On the Nano it toggles GPIO pins. On the ESP32 (-D ESPSERIAL) it turns every drawing call into a serial command for the Nano.

Because the driver is an Adafruit_GFX subclass on both boards, the ESP32 can use the normal GFX API (print, drawBitmap, fillScreen, ...). Each call is sent to the Nano and drawn there.

Documentation

Page About
Web UI The web page of the display, with screenshots of every tab
Pages Pictures of the dynamic pages
Home Assistant Entities, values on pages, rules
How it works Boot, the clock and the schedule, the Nano
Wiring Between the ESP32, the Nano and the relay
Flashing the Nano over WiFi nano_ota, bootloaders, troubleshooting
Recovery app The ESP32's second app: WiFi setup and OTA, one-time USB installation
Nano firmware The serial protocol and the Nano's behaviour
ESP32 firmware The files, keeping OTA alive, tests

Repository layout

platformio.ini          one PlatformIO project, multiple environments
lib/BROSE9323/          shared flipdot driver (used by both targets)
src/nano/               Arduino Nano firmware
src/esp32/              ESP32 clock firmware
  secrets.example.h     template for WiFi / MQTT credentials
  web/                  the web UI
src/recovery/           ESP32 recovery app: WiFi setup page + OTA
partitions.csv          ESP32 flash layout (recovery app + two clock firmware slots)
test/test_nano/         unit tests for the Nano firmware (run on the PC)
test/test_esp32/        unit tests for the ESP32 logic (run on the PC)
test/test_driver/       unit tests for the display driver with a simulated panel
test/mocks/             fake Arduino, EEPROM and display driver for the Nano tests
test/driver_mocks/      fake Arduino and Adafruit GFX for the driver tests
tools/                  uploader for flashing the Nano over WiFi, font and picture scripts
docs/                   documentation (see below)
Pictures/               photos, pictures of the pages and screenshots of the web UI
examples/               example pages to import in the web UI
archive/                old, unused sketches kept for reference
CLAUDE.md               project notes for Claude Code

Building and flashing

Install PlatformIO (the VS Code extension is easiest) and open this folder.

  1. Copy src/esp32/secrets.example.h to src/esp32/secrets.h and fill in your WiFi and MQTT details. secrets.h is gitignored.
  2. Check the COM ports and the OTA IP in platformio.ini.
  3. Build or upload:
Environment Board Upload method
nano Arduino Nano (ATmega328P) USB (COM10)
nano_ota Arduino Nano (ATmega328P) Over WiFi through the ESP32 (10.1.1.198). See Flashing the Nano over WiFi.
esp32 ESP32 DevKit v1 OTA over WiFi (10.1.1.198)
esp32_usb ESP32 DevKit v1 USB (COM11), into ota_0. Normally not needed.
recovery ESP32 DevKit v1 USB (COM11), one-time install of the recovery app
pio run                          # build nano + esp32
pio run -e nano -t upload        # flash the Nano over USB
pio run -e nano_ota -t upload    # flash the Nano over WiFi, through the ESP32
pio run -e esp32 -t upload       # flash the ESP32 over the air
pio run -e esp32_usb -t upload   # flash the ESP32 over USB
pio device monitor -e nano       # serial monitor
pio test -e test_nano -e test_esp32 -e test_driver   # run the unit tests on the PC

The unit tests need gcc/g++ on your PATH. On Windows you can get it from MSYS2 or Strawberry Perl.

In VS Code you can also pick the environment in the PlatformIO status bar.

Tip: The ESP32 → Nano wire goes to the Nano's RX pin, which the Nano's USB chip also uses. Disconnect that wire before you flash the Nano over USB, or the upload may fail. Flashing over WiFi (nano_ota) doesn't have this problem.

TODO

  • Install the recovery app (one-time USB flash, see One-time installation). Until then the ESP32 runs the old flash layout: the clock firmware works, but without the recovery app; the Home Assistant sensor shows "no recovery app".

  • Test the web UI on the real hardware: live view, uploading and drawing a bitmap, live mode, choosing a page, settings, Home Assistant settings (restart).

  • Test the dynamic pages on the real hardware: the Clock page after the update (same as before, yawning from 17:00), a new page with date and Full height clock, an uploaded font, a Home Assistant value (token in the Home Assistant tab).

  • Test the recovery app on the real hardware: the Home Assistant button, the setup WiFi with a wrong WiFi password, 3 quick power cycles, and an OTA upload into it.

  • Rewire the Nano link to UART2. The firmware now uses Serial2 (GPIO 16/17) instead of Serial (GPIO 1/3). Move the ESP32 TX wire from GPIO 1 to GPIO 17 before you flash this firmware, or the display gets no data. See Wiring between ESP32 and Nano.

  • Add the reply wire: Nano D1 (TX) → voltage divider → ESP32 GPIO 16 (RX2). With it, the ESP sends the first frame as soon as the Nano is ready, instead of after 3 s, and the Nano can be flashed over WiFi (nano_ota).

  • Test flashing over WiFi on the real hardware. The uploader is tested against a simulated bootloader, not yet against the real Nano. Check especially that the old bootloader starts after the relay power cycle.

  • Tune the flip settings on the real hardware. Flash both the Nano and the ESP32 (the old Nano ignores the new G/N/E commands). With the test patterns (Settings), try flip gap 0/200/500 µs and 1 or 2 pulses, and check that the refresh effects look right and that the off image is complete before the power is cut.

  • Check the boot timing. Build the Nano with MEASURE_REFRESH to see how long a full redraw takes. If the reply wire isn't fitted, check that 3 s (NANO_BOOT_TIMEOUT) is enough for the Nano to boot.

  • Watch the frame check. After a few days, look at System, "Frame check": any "wrong" or "no answer" counts mean bytes still get lost somewhere; "Nano restarted" means power dips (e.g. while many dots flip).

S
Description
No description provided
Readme
41 MiB
0 Stars 1 Watchers 0 Forks
Languages
C++ 71.6%
C 23.4%
Python 5%