Add a web UI to the clock firmware

- 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>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-13 01:52:34 +02:00
1 parent c9e0800e36
commit 2b1ac12ed7
34 files changed
+1989 -117

No files matched your search

+31 -2
View File
@@ -23,7 +23,8 @@ too, and it boots again each time the relay switches on. Only the ESP32 runs all
| 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. |
| **ESP32** | [src/esp32](src/esp32/README.md) | Connects to WiFi, gets the time over NTP, shows a clock with Pikachu bitmaps, exposes switches to Home Assistant over MQTT, switches display power with a relay and supports OTA updates. |
| **ESP32** | [src/esp32](src/esp32/README.md) | Connects to WiFi, gets the time over NTP, shows a clock with Pikachu bitmaps or your own bitmaps, has a [web UI](#web-ui), exposes switches to Home Assistant over MQTT, switches display power with a relay and supports OTA updates. |
| **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
@@ -38,6 +39,7 @@ 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)
@@ -85,6 +87,31 @@ In VS Code you can also pick the environment in the PlatformIO status bar.
> 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.
## Web UI
Open `http://<ESP32 IP>/` (e.g. <http://10.1.1.198/>) in a browser. At the top is a live
view of the display: it shows what the dots show right now, updated every 1.5 s.
| Tab | What you can do |
|-----|-----------------|
| **Show** | Choose what the display shows during the day: the clock (Pikachu with the time) or any bitmap. The schedule still decides when the display is on. |
| **Bitmaps** | All bitmaps with a preview: show one as the page, send it live, open it in the editor, delete it. Two built-in ones (Nintendo) can't be deleted. |
| **Draw / Live** | Draw on an 84 x 19 canvas (click or drag), or load an image: it is converted in the browser (threshold, invert, dithering, fit / fill / stretch). "Show on display now" sends it to the display right away (*live mode*); with "live while drawing" every change appears on the dots. Save it as a bitmap with a name. |
| **Settings** | Schedule on/off, when the display is on, the late-evening quarter-hour mode, when it switches off; yawning Pikachu and its time; the "No WiFi" screen; the flip time; the status LED. |
| **Home Assistant** | On/off, broker, port, user, password. Saving restarts the ESP32. An empty broker uses `secrets.h`. |
| **System** | Firmware status, uptime, WiFi, storage; restart; start the recovery app. |
**Live mode** shows the image at once, also at night or before the time is known. It
ends with "Back to normal", or 30 minutes after the last image. The Home Assistant Power
switch still wins: when it is off, nothing is shown.
**Bitmaps** are stored on the ESP32 (LittleFS, 320 KB, room for well over a thousand). They
need the new flash layout from the [one-time installation](#one-time-installation-usb);
before that, only the built-in ones are available.
Settings are stored in the ESP32's NVS. The defaults are the original behaviour. There is
no login: anyone on your WiFi can use the page, as with OTA.
## Flashing the Nano over WiFi
The ESP32 can flash the Nano for you, so you don't need a USB cable at the display:
@@ -167,7 +194,7 @@ is broken. It shows what is going on as a short message on the display, and the
LED double-blinks.
```
flash (4 MB): bootloader | nvs | otadata | factory: recovery app | ota_0: clock | ota_1: clock
flash (4 MB): bootloader | nvs | otadata | factory: recovery | ota_0: clock | ota_1: clock | bitmaps
```
**When it starts**
@@ -282,6 +309,8 @@ The serial protocol is described in [src/nano/README.md](src/nano/README.md).
[One-time installation](#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".
- [ ] **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 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.