From 46dfe825689a323b3acbf3009f1e128c28452282 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 15 Mar 2026 10:52:43 -0400 Subject: [PATCH] TDD for device and screen management. Authored by gemini 3.1 pro --- Provider/AGENTS.md | 1 + Provider/GEMINI.md | 1 + Provider/tdd/device_screens.md | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 Provider/tdd/device_screens.md diff --git a/Provider/AGENTS.md b/Provider/AGENTS.md index c807815..93acce2 100644 --- a/Provider/AGENTS.md +++ b/Provider/AGENTS.md @@ -80,6 +80,7 @@ Read the relevant TDD before any major architectural change: | `tdd/frontend_ota.md` | Touching frontend OTA upload or versioning | | `tdd/concurrent_requests.md` | Changing HTTP server socket/connection config | | `tdd/lvgl_image_generation.md` | Touching LVGL headless display or image gen | +| `tdd/device_screens.md` | Changing device registration, MAC routing, or XML layout logic | --- diff --git a/Provider/GEMINI.md b/Provider/GEMINI.md index 001d127..443ea47 100644 --- a/Provider/GEMINI.md +++ b/Provider/GEMINI.md @@ -77,6 +77,7 @@ Always read the relevant TDD before making major architectural changes: | [frontend_ota.md](tdd/frontend_ota.md) | Touching frontend OTA upload or versioning | | [concurrent_requests.md](tdd/concurrent_requests.md) | Changing HTTP server socket/connection config | | [lvgl_image_generation.md](tdd/lvgl_image_generation.md) | Touching LVGL headless display or image gen | +| [device_screens.md](tdd/device_screens.md) | Changing device registration, MAC routing, or XML layout logic | --- diff --git a/Provider/tdd/device_screens.md b/Provider/tdd/device_screens.md new file mode 100644 index 0000000..0c29aad --- /dev/null +++ b/Provider/tdd/device_screens.md @@ -0,0 +1,59 @@ +# Device Screens Management + +**Authored by Antigravity** +**Date:** 2026-03-15 + +--- + +## 1. What (Goal) + +The goal is to enable the Calendink Provider to act as a backend screen generator for dump clients (like e-ink devices) connected to the network. + +We need to implement a system that: +- Allows devices to register themselves via their MAC address. +- Provides a Frontend "Device Manager" interface where a user can upload and assign a custom LVGL XML string to each registered device. +- Generates a custom PNG image on the fly when a device requests its screen, by parsing its assigned XML string using the LVGL XML runtime. +- Temporarily stores this data in RAM (as per the current project architecture), deferring persistent storage (like SQLite) to a later phase. + +## 2. Why (Reasoning) + +Dumb e-ink clients (like the TRMNL) typically cannot run complex UI frameworks or parse rich data formats like JSON to render screens themselves. They simply download and display a static image buffer. + +By having the ESP32-S3 Provider generate these images using LVGL's headless rendering capabilities: +1. **Centralized Configuration:** The user can design and assign screens for all their devices from a single web dashboard. +2. **Dynamic UI:** Using the new `LV_USE_XML` feature in LVGL 9.4+, the layout is completely decoupled from the C++ firmware. Users can radically change what a display looks like by simply uploading a new XML string via the web interface, without needing to recompile or flash the ESP32. +3. **Payload Efficiency:** Returning a URL `{"image_url": "/api/devices/screen.png"}` in the JSON response instead of a base64 encoded binary prevents massive memory spikes and reduces transmission time for the constrained devices. +4. **Consistency:** Storing user settings in BSS static arrays aligns with the existing non-persistent data models (like Tasks). It avoids heap fragmentation risks on the ESP32 until a proper SQLite database is integrated. + +## 3. How (Implementation Details) + +### Backend Storage & State +Similar to the `Todo` app from `tdd/todo_list.md`, we use static arrays in the BSS segment to manage devices. The structure holds the device MAC, an `active` flag, and a statically allocated string buffer (2048 bytes) to store the uploaded LVGL XML. + +```cpp +struct Device { + char mac[18]; + bool active; + char xml_layout[2048]; +}; +extern Device g_Devices[8]; +``` + +### API Endpoints +The following REST endpoints handle the device lifecycle and image generation: + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/devices` | Returns a JSON array of all active devices, including whether they have a custom XML layout set. | +| `POST` | `/api/devices/register` | Accepts `{"mac": "..."}`. Claims a slot in `g_Devices` if not already registered. | +| `POST` | `/api/devices/layout` | Accepts `{"mac": "...", "xml": ""}`. Stores the XML string in the device's struct buffer. | +| `GET` | `/api/devices/screen?mac=XX` | Returns `{"image_url": "/api/devices/screen.png?mac=XX"}` (TRMNL API pattern). | +| `GET` | `/api/devices/screen.png?mac=XX` | Core rendering endpoint. Claims `g_LvglMutex`, clears the screen, parses the `xml_layout` buffer using `lv_xml_create()`, forces a refresh `lv_refr_now()`, encodes the buffer to PNG using `lodepng`, and streams the response. | + +### Subsystems Config +The ESP-IDF project configuration (`sdkconfig.defaults`) must be modified to enable the `CONFIG_LV_USE_XML=y` flag, which compiles the LVGL XML parser component into the firmware image. + +### Frontend +- **DeviceManager.svelte:** A new component accessible from the Sidebar. It fetches the device list on load. +- **XML Uploading:** For each device card, a text area allows the user to paste an LVGL XML string. Clicking "Save Layout" updates the device via `POST /api/devices/layout`. +- **Integration:** The `App.svelte` router will be updated to include the `'devices'` view state alongside Dashboard, Tasks, and Users.