68 lines
4.8 KiB
Markdown
68 lines
4.8 KiB
Markdown
# 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": "<lvgl 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.
|
|
|
|
### XML Runtime Integration
|
|
The user provided documentation for the `LV_USE_XML` runtime feature. We must:
|
|
1. Call `lv_xml_register_component_from_data("current_device", dev->xml_layout)` to register the XML payload.
|
|
2. Check if the XML string contains `<screen>`. If it does, LVGL expects us to instantiate it as a full screen using `lv_obj_t * root = lv_xml_create_screen("current_device");`.
|
|
3. If it does not contain `<screen>`, it's just a regular component/widget, so we create it *on* the active screen using `lv_obj_t * root = lv_xml_create(scr, "current_device", NULL);`.
|
|
4. Fallback to a string label if the XML is empty or parsing fails.
|
|
|
|
### 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`.
|
|
- **Debug Features:** A collapsed section (e.g. `<details>`) in the UI will contain a button to "Register Debug Device" that triggers a POST to `/api/devices/register` with a random or hardcoded MAC (e.g., `00:11:22:33:44:55`).
|
|
- **Integration:** The `App.svelte` router will be updated to include the `'devices'` view state alongside Dashboard, Tasks, and Users.
|