Files

142 lines
9.2 KiB
Markdown
Raw Permalink Normal View History

# 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 │
2026-10-13 00:07:35 +02:00
│ 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](src/nano/README.md) | 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. |
2026-10-20 17:52:25 +02:00
| **ESP32** | [src/esp32](src/esp32/README.md) | 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, text or Conway's Game of Life) or your own bitmaps, has a [web UI](docs/web-ui.md), exposes switches to Home Assistant over MQTT, switches display power with a relay and supports OTA updates. |
2026-10-13 01:52:34 +02:00
| **ESP32 recovery app** | [src/recovery](src/recovery/README.md) | Small second app: WiFi setup page and OTA, so the ESP32 stays reachable when the clock firmware fails. |
| **BROSE9323 driver** | [lib/BROSE9323](lib/BROSE9323/README.md) | 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](docs/web-ui.md) | The web page of the display, with screenshots of every tab |
| [Pages](docs/pages.md) | Pictures of the dynamic pages |
| [Home Assistant](docs/home-assistant.md) | Entities, values on pages, rules |
| [How it works](docs/how-it-works.md) | Boot, the clock and the schedule, the Nano |
| [Wiring](docs/wiring.md) | Between the ESP32, the Nano and the relay |
| [Flashing the Nano over WiFi](docs/nano-ota.md) | `nano_ota`, bootloaders, troubleshooting |
| [Recovery app](docs/recovery.md) | The ESP32's second app: WiFi setup and OTA, one-time USB installation |
| [Nano firmware](src/nano/README.md) | The serial protocol and the Nano's behaviour |
| [ESP32 firmware](src/esp32/README.md) | 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
2026-10-13 01:52:34 +02:00
web/ the web UI
src/recovery/ ESP32 recovery app: WiFi setup page + OTA
partitions.csv ESP32 flash layout (recovery app + two clock firmware slots)
2026-10-13 00:23:07 +02:00
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](https://platformio.org/) (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](docs/nano-ota.md). |
| `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](docs/recovery.md) |
```sh
pio run # build nano + esp32
2026-10-13 00:35:28 +02:00
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
```
2026-10-13 00:23:07 +02:00
The unit tests need `gcc`/`g++` on your PATH. On Windows you can get it from
[MSYS2](https://www.msys2.org/) 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
2026-10-13 00:35:28 +02:00
> fail. Flashing over WiFi (`nano_ota`) doesn't have this problem.
## TODO
- [X] **Install the recovery app** (one-time USB flash, see
[One-time installation](docs/recovery.md#one-time-installation-usb)). 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".
- [X] **Test the web UI on the real hardware**: live view, uploading and drawing a bitmap,
2026-10-13 01:52:34 +02:00
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.
- [X] **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](docs/wiring.md).
- [X] **Add the reply wire:** Nano D1 (TX) → voltage divider → ESP32 GPIO 16 (RX2). With it,
2026-10-13 00:35:28 +02:00
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`).
- [X] **Test flashing over WiFi on the real hardware.** The uploader is tested against a
2026-10-13 00:35:28 +02:00
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).