2026-10-12 23:55:15 +02:00
# Arduino Nano: flipdot driver
PlatformIO environment: `nano`
The Nano is connected directly to the Brose panel electronics. It keeps an 84 × 19 frame
buffer and takes drawing commands over serial (115200 baud). It has no clock or network
logic.
2026-10-13 00:19:15 +02:00
## Files
| File | Contents |
|------|----------|
| `main.cpp` | `setup()` / `loop()` , feeds serial bytes to the parser, periodic full redraw |
| `config.h` | All settings: debug options, display size, flip time limits, timings |
| `command_parser.h/.cpp` | `CommandParser` : the serial protocol, one method per command |
| `settings.h/.cpp` | Flip time stored in EEPROM |
The display driver itself is in [lib/BROSE9323 ](../../lib/BROSE9323/README.md ).
2026-10-13 00:23:07 +02:00
## Tests
2026-10-13 00:56:32 +02:00
`pio test -e test_nano` runs the unit tests in [test/test_nano ](../../test/test_nano/test_main.cpp )
2026-10-13 00:23:07 +02:00
on the PC. They feed serial data to the firmware and check what it asks the display to do:
every command, bitmaps, bad input, flip time storage and the full-redraw timing. The
Arduino core, EEPROM and the display driver are replaced by fakes in
[test/mocks ](../../test/mocks ).
2026-10-12 23:55:15 +02:00
## Behaviour
2026-10-15 01:29:01 +02:00
- **Boot:** the Nano loads the flip settings from EEPROM, sets up the pins and sends `READY` .
2026-10-13 00:12:28 +02:00
It doesn't flip any dots at boot. The relay powers the Nano, so it boots every time the
display is switched on.
- **First `display()` after boot** redraws every dot, because the Nano doesn't know which
state the dots were left in.
2026-10-12 23:55:15 +02:00
- **`display()` :** compares the new buffer with the last one that was shown and strobes
2026-10-15 01:29:01 +02:00
only the dots that changed, each with `N` pulses (default 2: the second one catches
dots that missed the first). Every pulse is followed by a pause (`G` , default 200 µs)
so the coil supply can recover.
- **Refresh effect** (`E` ): with `0` (default) a frame appears column by column as fast
as possible. The other effects (sparkle, wipe, rain, from the middle out, diagonal,
spiral, flip all then reveal, push, random) spread the flips over the effect time.
Push shifts the whole picture one column per step, so it flips many dots and can take
longer than the effect time. With flag 1 only small changes (at most 1/10 of the dots,
like the clock's minutes) get the effect. After boot the dot state is unknown, so push
falls back to a wipe. They run in small steps from `loop()` , so serial data keeps
being read; while a new frame is being drawn into the buffer the effect pauses, and the
next `D` starts it again. The orders come from `DotSequence` in
[lib/BROSE9323/dot_sequence.h ](../../lib/BROSE9323/dot_sequence.h ).
2026-10-13 00:06:49 +02:00
- **Every 2 minutes** (`REFRESH_TIME` ) it does a full redraw to fix dots that got stuck.
A full redraw blocks for about half a second, which is long enough to overflow the
64-byte serial buffer. So it only runs right after a `D` (the end of a frame, when the
2026-10-15 01:29:01 +02:00
ESP is quiet) or when no serial data has arrived for 2 seconds, and never while an
effect runs. It is always instant, with one pulse per dot.
2026-10-13 00:06:49 +02:00
- **Flip pulse length** (µs, 50– 2000) is set with the `T` command and stored in EEPROM,
2026-10-15 01:29:01 +02:00
so it is kept after a reboot. The default is 200 µs. `G` , `N` and `E` are stored the
same way (addresses in `settings.cpp` ; values that were never written fall back to the
defaults in `config.h` ).
2026-10-13 00:06:49 +02:00
### Debug options
2026-10-13 00:19:15 +02:00
Uncomment these in `config.h` :
2026-10-13 00:06:49 +02:00
| Define | Effect |
|--------|--------|
| `DEBUG` | Print unknown command bytes on the serial port |
| `MEASURE_REFRESH` | At boot, time a full redraw and print the duration in ms |
2026-10-12 23:55:15 +02:00
## Serial protocol
2026-10-13 00:06:49 +02:00
Each command is one ASCII line that **ends with `\n`** (`\r` is ignored). The Nano
collects a whole line before it runs the command. If a line is broken or unknown, it is
skipped and the next line starts clean. Coordinates are 2-digit **hex** . Color is `0`
2026-10-15 01:29:01 +02:00
(black) or `1` (yellow). The ESP32 driver (`ESPSERIAL` ) only uses `s` , `S` , `F` , `D` , `T` ,
`G` , `N` and `E` .
2026-10-12 23:55:15 +02:00
| Cmd | Format | Meaning |
|-----|--------|---------|
| `s` | `sXXYYc` | Set pixel in the buffer |
| `S` | `SXXYYc` | Set pixel and flip it right away (direct mode) |
| `F` | `Fc` | Fill the whole buffer |
| `D` | `D` | Push the buffer to the display (changed dots only) |
2026-10-13 00:06:49 +02:00
| `P` | `PXXYYcsTEXT` | Print text at (XX,YY), color `c` , size `s` . A literal `\n` (backslash, n) in TEXT starts a new line. The whole line can be at most 64 characters. |
| `B` | `BXXYYWWHHcHEX...` | Draw a bitmap. WW must be a multiple of 4. Pixel data is one hex digit per 4 pixels, row by row. The data is drawn while it arrives, so it is not limited to 64 characters. |
2026-10-12 23:55:15 +02:00
| `R` | `R` | Print the buffer as ASCII art on the serial port |
2026-10-13 00:06:49 +02:00
| `T` | `Tnnn` | Set the flip pulse length in µs (decimal, 50– 2000), stored in EEPROM |
2026-10-15 01:29:01 +02:00
| `G` | `Gnnn` | Pause after each pulse in µs (decimal, 0– 5000), stored in EEPROM |
| `N` | `Nn` | Pulses for each changed dot (1– 3), stored in EEPROM |
| `E` | `Ee,nnnn[,f]` | Refresh effect `e` (0 instant, 1 sparkle, 2 wipe, 3 rain, 4 from the middle out, 5 diagonal, 6 spiral, 7 flip all then reveal, 8 push, 9 random), its time in ms (100– 10000) and flags `f` (1 = only small changes, default 0), stored in EEPROM |
2026-10-13 00:06:49 +02:00
2026-10-13 00:12:28 +02:00
Replies on the Nano's TX pin:
- `READY` after booting. The ESP waits for it before sending the first frame.
- `ERR: ...` for invalid commands.
2026-10-12 23:55:15 +02:00
Example (what the ESP32 sends for one pixel and a refresh):
```
s0a051
D
```
## Wiring (default pinout)
The pins are defined in [lib/BROSE9323/BROSE9323.h ](../../lib/BROSE9323/BROSE9323.h ).
If you use the PLCC adapter board, build with `-D FLIPDOT_PLCC_ADAPTER` to switch to its
pinout.
| Signal | Nano pin |
|--------|----------|
| ENABLE | D7 |
| ADDR_0 / 1 / 2 (panel select) | D3 / D5 / D6 |
| COL_DATA | D4 |
| COL_0 … COL_4 | D8, D10, D9, D11, D12 |
| ROW_0 … ROW_4 | A3, A4, A2, A0, A1 |
| ROW_RESET | D13 |
| ROW_SET | A5 |
2026-10-13 00:16:24 +02:00
| Serial RX | D0 (from ESP32 GPIO 17, TX2) |
| Serial TX | D1 (to ESP32 GPIO 16, RX2, through a 5 V → 3.3 V voltage divider). Optional: needed for `READY` . Without it, the ESP waits the full 3 s timeout. |