8.8 KiB
LCD Expressions
This document describes how LCD expressions are owned, requested, played, and extended.
Ownership Model
LCD expressions are split into semantic intent and low-level playback:
control_planedecides when an expression should be requested.domainowns semantic expression IDs and maps them to playback requests.platformowns LCD presentation primitives and LVGL playback.
Do not bypass this path from upper layers. The expected flow is:
control_plane condition
-> domain_runtime_request_expression()
-> platform_lcd_request_presentation()
-> LCD render task / LVGL
control_plane must not call platform_lcd_* APIs directly. platform must
not expose domain expression IDs.
Current Expressions
| Domain expression | Trigger owner | Playback kind | Behavior |
|---|---|---|---|
DOMAIN_EXPRESSION_IDLE |
LCD startup / restore path | PLATFORM_LCD_PRESENTATION_IDLE |
Shows the default cat idle screen. |
DOMAIN_EXPRESSION_SERVER_ERROR |
Multimodal server failure events | PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE |
Plays server_error for 5 cycles, then returns to idle. |
DOMAIN_EXPRESSION_SLOT_REELS |
Slot feature switch worker | PLATFORM_LCD_PRESENTATION_SLOT_REELS |
Plays the procedural slot reels presentation. |
DOMAIN_EXPRESSION_WIFI_DISCONNECTED |
Wi-Fi state monitor worker | PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE |
Loops wifi_disconnected while STA is unavailable; saved credentials are retried with backoff, then idle is restored after reconnect. |
DOMAIN_EXPRESSION_WEALTH_SPIN |
Model-command direction worker | PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE |
Plays spin exactly twice as a fixed intro while the cat stays still; the worker blocks until it completes, then the reveal and motor rotation start together. |
DOMAIN_EXPRESSION_WEALTH_DIRECTION |
Model-command direction worker | PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE |
Plays the matched direction's reveal once, then returns to idle. Parametric on direction_index; the asset directory is resolved from the domain direction catalog order. |
Server error currently triggers only from:
CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILEDCONTROL_PLANE_MULTIMODAL_EVENT_OUTPUT_ERROR
Server error expression requests are suppressed while Wi-Fi STA is disconnected; the Wi-Fi disconnected expression owns that state because server reachability is not meaningful without Wi-Fi connectivity.
Transport disconnect/error events are not server error expressions.
Asset Management
Expression assets live under:
components/platform/assets/lvgl_animations/<asset_dir>/
The platform build copies all .bin files under this tree into the gifpack
LittleFS partition while preserving relative directories. Runtime LVGL paths are
resolved as:
A:/littlefs/<asset_dir>/<file>.bin
Asset rules:
- Use LVGL
.binimages compatible with the active LCD color configuration. - RGB565 LZ4 compressed assets are supported by the existing decoder path when
LV_BIN_DECODER_RAM_LOADandLV_USE_LZ4are enabled. - File discovery is lexicographic. Name files with zero-padded numeric suffixes.
- If an expression needs a non-linear playback sequence, define
frame_orderin the domain catalog instead of duplicating files. - Keep
asset_dirnames short enough forPLATFORM_LCD_PRESENTATION_ASSET_DIR_MAX.
The current server error expression uses:
components/platform/assets/lvgl_animations/server_error/
Sorted source frames:
TQ_Cat_Server_Error_0000.bin
TQ_Cat_Server_Error_0001.bin
TQ_Cat_Server_Error_0002.bin
Playback order is declared in the domain catalog as:
{0, 1, 2, 1, 0}
The Wi-Fi disconnected expression uses:
components/platform/assets/lvgl_animations/wifi_disconnected/
Sorted source frames:
TQ_Cat_wifi_disconnected_0000.bin
TQ_Cat_wifi_disconnected_0001.bin
TQ_Cat_wifi_disconnected_0002.bin
TQ_Cat_wifi_disconnected_0003.bin
Playback order is declared in the domain catalog as:
{0, 1, 2, 3, 2, 1, 0}
The wealth-direction reveal uses one shared spin intro plus one body per direction:
components/platform/assets/lvgl_animations/spin/ (fixed two-cycle spin intro)
components/platform/assets/lvgl_animations/south/
components/platform/assets/lvgl_animations/southwest/
components/platform/assets/lvgl_animations/west/
components/platform/assets/lvgl_animations/northwest/
components/platform/assets/lvgl_animations/north/
components/platform/assets/lvgl_animations/northeast/
components/platform/assets/lvgl_animations/east/
components/platform/assets/lvgl_animations/southeast/
These play all frames in sorted order (frame_order = NULL). The direction body
asset is resolved from direction_index via s_wealth_direction_asset_dirs[] in
components/domain/src/runtime/domain_runtime_adapters.c, whose order must match
the direction catalog (components/domain/src/direction/domain_direction_catalog.c).
Requesting Expressions
Use domain_runtime_request_expression() from orchestration code:
const domain_expression_request_t request = {
.id = DOMAIN_EXPRESSION_SERVER_ERROR,
.trigger = DOMAIN_EXPRESSION_TRIGGER_SERVER_TASK_FAILED,
};
(void)domain_runtime_request_expression(&request);
Trigger IDs should describe why the expression was requested. They are useful for logs and future policy decisions, but the playback mapping is still owned by the domain catalog.
Slot reel playback uses the same expression path:
domain_runtime_request_expression() with DOMAIN_EXPRESSION_SLOT_REELS.
Playback Scheduling
The platform LCD presentation scheduler is non-blocking:
- Requests are copied at API entry.
- LVGL objects are created, deleted, and mutated only inside the LCD render task.
- Same active or pending requests are coalesced.
- Higher-priority interrupting requests replace the active presentation.
- Lower-priority requests are ignored while a higher-priority presentation is active or pending.
- Asset-backed expressions are cached after their frames are decoded. The LCD
task preheats the
spinasset after startup and preheats the selected wealth direction reveal while the fixed spin intro is playing. - Preheat requests do not change the active display. They decode incrementally during the render loop, and playback finishes any remaining decode work synchronously if a reveal is requested before preheat completes.
Current priorities:
| Presentation | Priority | Interrupts current |
|---|---|---|
| Wi-Fi disconnected / idle restore | 120 |
Idle restore interrupts Wi-Fi disconnected. |
| Wealth spin / wealth-direction reveal | 110 |
Yes (spin interrupts current; the reveal follows after the spin completes). |
| Server error | 100 |
Yes |
| Slot reels | 20 |
No |
The wealth spin/reveal pair is requested by the model-command direction worker
(control_plane_model_command_task): the spin plays exactly twice as a blocking
intro while the selected reveal is preheated, then the reveal is requested and
the motor rotation starts together. At priority 110 a Wi-Fi-disconnected
screen (120) still wins, so the reveal is suppressed while STA is down —
consistent with the server-error suppression rule above.
Adding a New Expression
Follow this checklist for every new expression:
- Add a semantic ID to
domain_expression_id_tincomponents/domain/include/domain.h. - Add a trigger reason to
domain_expression_trigger_tif the new expression needs a new trigger source. - Add a catalog entry in
components/domain/src/runtime/domain_runtime_adapters.c. - For asset-backed expressions, place
.binframes undercomponents/platform/assets/lvgl_animations/<asset_dir>/. - If the expression needs a custom frame order, define a static
uint8_tframe-order array in the domain adapter and reference it from the catalog. - Add or update the
control_planerule that createsdomain_expression_request_t. - Build with ESP-IDF v5.5.3 and confirm the LittleFS image includes the asset directory.
Do not add one-off APIs such as domain_runtime_show_happy() or
platform_lcd_show_error(). Keep extension through the expression catalog and
the unified presentation request path.
Validation Checklist
Before merging expression changes:
- Run
idf.py buildthrough the repository-approved ESP-IDF activation path. - Confirm new asset files appear in the
gifpackbuild output. - Verify
control_planedoes not includeplatform.hor callplatform_lcd_*. - Verify platform code does not refer to
DOMAIN_EXPRESSION_*. - For finite asset sequences, verify completion returns to idle when
return_to_idleis set. - For interrupting expressions, verify the active lower-priority presentation is cancelled cleanly.
- For wealth-direction playback, verify default -> spin and spin -> reveal transitions use cached/preheated assets without visible transition stalls.