Files
Calendink/Provider/tdd/device_screens.md
Patedam ebb0ccecf4 Moved to lvgl 9.4 because 9.5 has removed runtime xml support.
Made basic example of editing xml layout from backend.
2026-03-15 14:47:32 -04:00

4.8 KiB

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.

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.