The Nano's periodic full redraw blocked for ~0.8 s. A frame that arrived
meanwhile overflowed its 64-byte serial buffer; lost bytes in a bitmap
shift the rest of the image (picture moved up), and diff frames never
repaired it.
- Nano: all drawing (frames, effects, the dot refresh) runs from update()
in ~1 ms slices; the refresh pauses while a frame is received or shown
- New commands: C (reply with the image checksum) and I (refresh minutes,
EEPROM)
- ESP32: NanoMonitor asks for the checksum after frames and sends the whole
image again on a mismatch, missing answer or Nano restart; counts ERR
replies. Periodic whole-image resync.
- Settings: "Dot refresh" and "Image resync" intervals (settings v5,
v4 records are migrated)
- System tab: loop timing, free memory, frame check counts
- test_driver: the real driver against a simulated panel
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each rule has the blocks it acts in (none: always; without a running
schedule always). Stored as a block mask (schedule format 2, version 1
records are converted), in JSON as the blocks' start times. Version 2.7.1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Show as number (multiplier, decimals, auto unit W -> kW -> MW, without
minus sign), duration (seconds/minutes/hours as 2h 21m, 2:21:00, 2:21 or
smart), time (HA timestamps in local time, with weekday or date, or
relative: over 34 min / 12 min geleden) or own texts per state.
- Page format 3; version 2 pages are converted. Version 2.7.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Schedule: daily blocks from a start time, each with up to 6 rotating pages
or bitmaps (own duration), switching with the effect or instantly. A manual
pick (Show tab, HA Mode) lasts until the next block; Back to schedule ends it.
- Rules: an HA state (=, is not, above, below) shows a page while true, adds it
to the rotation while true, or shows it for N seconds when it becomes true.
Each rule is polled at its own interval, with a 60 s back-off on errors.
- PageSelector (pure logic, tested) decides the shown page; schedule stored in
NVS; HA sensor Shown page and button Back to schedule.
- Web UI: Schedule and Stopwatch tabs, bitmaps used by pages or rules in their
own subsection, schedule in export/import. Version 2.6.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frames were sent as F0 plus one s command per yellow dot (thousands of
characters). During scrolling that overran the Nano's serial buffer, and
half-received frames redrew the whole screen. showBitmap() now sends two B
commands over the changed rectangle (dots off, dots on), or one B command for
a full frame, and the pacing counts the characters sent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Text elements can scroll in a window at a set speed; clock, countdown and
stopwatch can show seconds. Moving pages are sent as instant frames (new
Nano command D1) and paced by the estimated flip time.
- Countdown to a date and time (smart, days, days + time, with seconds),
stopwatch (web UI, HA switch and reset button), image element (fixed or
by Home Assistant state), multiplier for HA values, degree sign (char 127)
in all fonts and the font converter.
- Page format version 2; version 1 pages are converted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hardcoded clock is now the built-in dynamic page "Clock". A dynamic page
has a background bitmap per time of day and up to 8 elements (clock, date,
Home Assistant value, text), each with position, alignment, font and overlay.
Pages are drawn on the ESP32, also for the web previews.
- Pages tab in the web UI: page editor with drag and drop, font upload
(TTF/OTF converted in the browser to the FDF1 dot font format)
- Built-in fonts Small, Normal, Large and Full height (generated by
tools/make_builtin_fonts.py), built-in bitmaps Pikachu and Pikachu yawning
- Home Assistant values over the REST API (URL and token in the HA tab)
- Settings version 4: evening settings removed (now time slots of the Clock
page), old records are converted
- HA Mode dropdown lists the dynamic pages and bitmaps
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Home Assistant: remove the Clock switch (its entity is deleted on connect),
add an IP sensor and Mode / Off image dropdowns whose options follow the
saved bitmaps. Remove the built-in "Nintendo border" bitmap.
Off image: a bitmap (or blank) left on the dots when the display switches off.
Nano: pause after each flip pulse and repeat pulses for changed dots, both
stored in EEPROM (G/N commands). Refresh effects (E command): instant,
sparkle, wipe, rain, from the middle out, diagonal, spiral, flip all then
reveal, push and random, optionally only for small changes. Effects run in
steps from loop() so serial input keeps flowing.
Web UI: flip settings, refresh effect and test patterns under Settings; the
System tab shows why the ESP32 last restarted. Settings records from
versions 1 and 2 are migrated. Firmware 2.3.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Web UI on port 80: live view of the display, choose the page (clock or
a bitmap), bitmaps (list, show, live, edit, delete), draw / image upload
with conversion in the browser, live mode, settings, Home Assistant
- Live view from a shadow buffer in the BROSE9323 driver (shownPixel)
- Settings in NVS with the original behaviour as defaults: schedule times,
quarter hours, yawning Pikachu, No WiFi screen, flip time, status LED,
Home Assistant broker (restart on change)
- Bitmaps on LittleFS; new 320 KB partition, ota slots now 1.31 MB
- DisplayController: pages, live mode, settings; app.cpp holds the objects
- README for the recovery app; docs for the web UI
- Tests: settings, time parsing, bitmap format and names, schedule with
settings, pages and live mode (95 total)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Clock firmware: 'Connecting to WiFi...' and 'Getting the time...' at
boot, 'Crashed!' in the OTA-only modes, and 'No WiFi' with the time
when WiFi is down for over a minute (new DisplayController screen)
- Recovery app: shows its reason, the setup WiFi name, or its IP address,
then powers the display off (flipdots keep the image)
- StatusScreen and drawTextLines shared by both apps
- Driver: ESP begin() no longer draws 'ESP Connected'
- Tests: No WiFi screen, short reason texts (70 total)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New flash layout (partitions.csv): factory = recovery app, ota_0/ota_1 =
clock firmware; esp32_usb uploads to ota_0 so it never overwrites it
- src/recovery: WiFi + OTA + web page only. Opens the password protected
'FlipDot-Setup' AP with a captive portal when WiFi fails, lets you pick
another WiFi (stored in NVS, used by both apps), start the clock or stay
- Recovery is started when the clock firmware can't connect to WiFi,
crashes 3 times, after 3 quick power cycles, from a new Home Assistant
button, or by the bootloader when no clock firmware works
- Shared NVS store and boot switching in src/esp32/system
- Clock firmware keeps working on the old flash layout (falls back to
OTA-only safe mode)
- README: recovery app, one-time USB installation; tests: 65 total
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rollback: override verifyRollbackLater() so a new OTA image stays
pending until it ran 60 s with WiFi; any earlier restart rolls back
(the Arduino core marked every image valid at boot before)
- Task watchdog (30 s) on loop(), paused during OTA uploads
- Crash counter in RTC memory: OTA-only window after a crash, 10 min
safe mode (WiFi + OTA only) after 3 crashes in a row
- Restart when WiFi stays down for 10 min
- Home Assistant sensor 'FlipDot Display Firmware' with version/state
- logic/boot_policy with tests (60 tests total)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Structure (src/esp32/):
- logic/: DisplayController, schedule, power timer, LED pattern; pure C++
- display/: FlipdotFrames and bitmaps
- nano_link/: NanoLink (relay, serial, READY, bootloader) and flash bridge
- net/: WiFi/OTA/panic window, Home Assistant
- config.h with all settings; libs.h (globals in a header) removed
Bug fixes:
- Home Assistant switches now report their state back and start as ON
- loop() no longer blocks: WiFi.waitForConnectResult() (up to 60 s
without WiFi) and getLocalTime() (5 s before NTP sync) were called
every iteration
- Power switch off now powers off after 1 s even when a 30 s countdown
is already running
- millis() overflow safe power-off timer; no frame catch-up bursts
- No garbage time string before NTP sync; 'Connecting...' stays instead
- Frames are only sent when the screen changes instead of every second
- Reset reasons are logged once instead of every 100 ms
- Removed dead code (unused fonts, WiFiMulti, rotated bitmap, ...)
Tests: new test_esp32 environment (26 tests); native renamed to test_nano
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New native environment: pio test -e native
- test/mocks: fake Arduino core, EEPROM and recording BROSE9323 driver
- test/test_nano: 25 tests covering every command, bitmaps, bad input,
flip time storage and the periodic full redraw
- Board environments ignore tests so they are never uploaded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>