147 lines
6.7 KiB
Markdown
147 lines
6.7 KiB
Markdown
# 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_plane` decides when an expression should be requested.
|
|
- `domain` owns semantic expression IDs and maps them to playback requests.
|
|
- `platform` owns LCD presentation primitives and direct ST7789 drawing.
|
|
|
|
Do not bypass this path from upper layers. The expected flow is:
|
|
|
|
```text
|
|
control_plane condition
|
|
-> domain_runtime_request_expression()
|
|
-> platform_lcd_request_presentation()
|
|
-> LCD direct render task
|
|
```
|
|
|
|
`control_plane` must not call `platform_lcd_*` APIs directly. `platform` must
|
|
not expose domain expression IDs.
|
|
|
|
## Current Backend State
|
|
|
|
The current LCD backend is `components/platform/src/lcd_direct.c`. It initializes
|
|
the 240x320 ST7789P3 panel directly through SPI using the
|
|
`CONFIG_TQ_SCREEN_*` pin and orientation settings, starts one render task, and
|
|
accepts the existing platform presentation requests. The 2026-07-24 board uses
|
|
MOSI GPIO18, clock GPIO8, CS GPIO15, D/C GPIO16, reset GPIO17, and backlight
|
|
GPIO7. The default X/Y swap exposes a 320x240 landscape drawing surface so the
|
|
existing LCD assets retain their current geometry.
|
|
|
|
The direct backend currently draws deterministic full-screen test patterns for
|
|
presentation requests. Real image/resource playback is the next migration step.
|
|
The previous widget-based display source files, display middleware dependency,
|
|
and legacy animation assets have been removed from the build.
|
|
|
|
## Current Expressions
|
|
|
|
| Domain expression | Trigger owner | Playback kind | Current behavior |
|
|
| --- | --- | --- | --- |
|
|
| `DOMAIN_EXPRESSION_IDLE` | LCD startup / restore path | `PLATFORM_LCD_PRESENTATION_IDLE` | Draws the direct backend idle test pattern. |
|
|
| `DOMAIN_EXPRESSION_SERVER_ERROR` | Multimodal server failure events | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Draws a direct backend pattern selected from the mapped asset name. |
|
|
| `DOMAIN_EXPRESSION_SLOT_REELS` | Slot feature switch worker | `PLATFORM_LCD_PRESENTATION_SLOT_REELS` | Draws a direct backend slot-pattern placeholder. |
|
|
| `DOMAIN_EXPRESSION_WIFI_DISCONNECTED` | Wi-Fi state monitor worker | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Draws a direct backend pattern selected from the mapped asset name. |
|
|
| `DOMAIN_EXPRESSION_WEALTH_SPIN` | Model-command direction worker | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Draws a direct backend pattern selected from `spin`. |
|
|
| `DOMAIN_EXPRESSION_WEALTH_DIRECTION` | Model-command direction worker | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Draws a direct backend pattern selected from the mapped direction asset name. |
|
|
| `DOMAIN_EXPRESSION_OTA_UPDATE` | OTA update worker | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Loops the three-frame `ota` sequence during firmware download and installation. |
|
|
| `DOMAIN_EXPRESSION_LCD_BOUNDARY_TEST` | Factory CLI `lcd_test` | `PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE` | Measures six preheated cache-resident frames for about 5 seconds, then measures all 16 frames under cache churn for about 5 seconds, using a 1 ms hold between full-screen draws before returning to idle. |
|
|
|
|
Server error currently triggers only from:
|
|
|
|
- `CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILED`
|
|
- `CONTROL_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 Migration Direction
|
|
|
|
The image playback path is a platform-owned direct LCD asset pipeline. Runtime
|
|
PNG files are embedded in each OTA application image, decoded through
|
|
`lodepng`, converted to RGB565 in PSRAM, and written to the ST7789 panel through
|
|
the existing bounded row-transfer buffer.
|
|
|
|
- PNG decode, the bounded four-entry LRU cache, and ST7789 writes stay inside
|
|
`platform`.
|
|
- Domain continues to map semantic expressions to short asset IDs or names.
|
|
- Upper layers continue to use `domain_runtime_request_expression()`.
|
|
- Runtime LCD assets must be added to the explicit PNG allowlist in
|
|
`components/platform/CMakeLists.txt`; files that only exist in the source
|
|
directory are not packaged automatically.
|
|
|
|
## Requesting Expressions
|
|
|
|
Use `domain_runtime_request_expression()` from orchestration code:
|
|
|
|
```c
|
|
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.
|
|
|
|
## Playback Scheduling
|
|
|
|
The platform LCD presentation scheduler is non-blocking:
|
|
|
|
- Requests are copied at API entry.
|
|
- Panel drawing is serialized by 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.
|
|
|
|
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 |
|
|
| Server error | `100` | Yes |
|
|
| Slot reels | `20` | No |
|
|
|
|
## Adding a New Expression
|
|
|
|
Follow this checklist for every new expression:
|
|
|
|
1. Add a semantic ID to `domain_expression_id_t` in
|
|
`components/domain/include/domain.h`.
|
|
2. Add a trigger reason to `domain_expression_trigger_t` if the new expression
|
|
needs a new trigger source.
|
|
3. Add a catalog entry in `components/domain/src/runtime/domain_runtime_adapters.c`.
|
|
4. Add or map the platform direct LCD asset in the platform LCD backend.
|
|
5. Add or update the `control_plane` rule that creates
|
|
`domain_expression_request_t`.
|
|
6. Build with the repository-approved ESP-IDF path and verify LCD logs on
|
|
hardware.
|
|
|
|
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 build` through the repository-approved ESP-IDF activation path.
|
|
- Verify `control_plane` does not include `platform.h` or call `platform_lcd_*`.
|
|
- Verify platform code does not refer to `DOMAIN_EXPRESSION_*`.
|
|
- For finite asset sequences, verify completion returns to idle when
|
|
`return_to_idle` is set.
|
|
- For interrupting expressions, verify the active lower-priority presentation is
|
|
cancelled cleanly.
|