Files
Calendink/Provider/tdd/device_screens.md

3.9 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.

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.