|
| 1 | +# ADR-045: AMOLED Display Support for ESP32-S3 CSI Node |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Proposed |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +The ESP32-S3 board (LilyGO T-Display-S3 AMOLED) has an integrated RM67162 QSPI AMOLED display (536x240) and 8MB octal PSRAM that were unused by the CSI firmware. Users want real-time on-device visualization of CSI statistics, vital signs, and system health without relying on an external server. |
| 10 | + |
| 11 | +### Constraints |
| 12 | + |
| 13 | +- Binary was 947 KB in a 1 MB partition — needed 8MB flash + custom partition table |
| 14 | +- SPIRAM was disabled in sdkconfig despite hardware having 8MB PSRAM |
| 15 | +- Core 1 is pinned to DSP (edge processing) — display must use Core 0 |
| 16 | +- Existing CSI pipeline must not be affected |
| 17 | + |
| 18 | +### Available APIs |
| 19 | + |
| 20 | +Thread-safe edge APIs already exist (`edge_get_vitals()`, `edge_get_multi_person()`) — the display task only reads from these, no new synchronization needed. |
| 21 | + |
| 22 | +## Decision |
| 23 | + |
| 24 | +Add optional AMOLED display support with the following architecture: |
| 25 | + |
| 26 | +### Hardware Abstraction Layer |
| 27 | + |
| 28 | +- `display_hal.c/h`: RM67162 QSPI panel driver + CST816S capacitive touch via I2C |
| 29 | +- Auto-detect at boot: probe RM67162 and check SPIRAM; log warning and skip if absent |
| 30 | + |
| 31 | +### UI Layer |
| 32 | + |
| 33 | +- `display_ui.c/h`: LVGL 8.3 with 4 swipeable views via tileview widget |
| 34 | +- Dark theme (#0a0a0f) with cyan (#00d4ff) accent for three.js-like aesthetic |
| 35 | +- Views: Dashboard (CSI amplitude chart + stats), Vitals (breathing + HR line graphs), Presence (4x4 occupancy grid), System (CPU, heap, PSRAM, WiFi, uptime, FPS) |
| 36 | + |
| 37 | +### Task Layer |
| 38 | + |
| 39 | +- `display_task.c/h`: FreeRTOS task on Core 0, priority 1 (lowest) |
| 40 | +- LVGL pump loop at configurable FPS (default 30) |
| 41 | +- Double-buffered draw buffers allocated in SPIRAM |
| 42 | + |
| 43 | +### Compile-Time Control |
| 44 | + |
| 45 | +- `CONFIG_DISPLAY_ENABLE=y` (default): compiles display code, auto-detects hardware at boot |
| 46 | +- `CONFIG_DISPLAY_ENABLE=n`: zero-cost — no display code compiled |
| 47 | +- `CONFIG_SPIRAM_IGNORE_NOTFOUND=y`: boots fine on boards without PSRAM |
| 48 | + |
| 49 | +### Flash Layout |
| 50 | + |
| 51 | +8MB partition table (`partitions_display.csv`): |
| 52 | +- Dual OTA partitions: 2 x 2MB (supports larger binaries with LVGL) |
| 53 | +- SPIFFS: 1.9MB (for future font/asset storage) |
| 54 | +- NVS + otadata + phy: standard sizes |
| 55 | + |
| 56 | +### Core/Task Layout |
| 57 | + |
| 58 | +| Task | Core | Priority | Impact | |
| 59 | +|------|------|----------|--------| |
| 60 | +| WiFi/LwIP | 0 | 18-23 | unchanged | |
| 61 | +| OTA httpd | 0 | 5 | unchanged | |
| 62 | +| **display_task** | **0** | **1** | **NEW — lowest priority** | |
| 63 | +| edge_task (DSP) | 1 | 5 | unchanged | |
| 64 | + |
| 65 | +### Dependencies |
| 66 | + |
| 67 | +- LVGL ~8.3 (via ESP-IDF managed components) |
| 68 | +- espressif/esp_lcd_touch_cst816s ^1.0 |
| 69 | +- espressif/esp_lcd_touch ^1.0 |
| 70 | + |
| 71 | +## Consequences |
| 72 | + |
| 73 | +### Positive |
| 74 | + |
| 75 | +- Real-time on-device stats without network dependency |
| 76 | +- Zero impact on CSI pipeline (display reads thread-safe APIs, runs at lowest priority) |
| 77 | +- Graceful degradation: works on boards without display or PSRAM |
| 78 | +- SPIRAM enabled for all boards (benefits WASM runtime too) |
| 79 | +- 8MB flash + dual OTA 2MB partitions give headroom for future features |
| 80 | + |
| 81 | +### Negative |
| 82 | + |
| 83 | +- Binary size increase (~200-300 KB with LVGL) |
| 84 | +- SPIRAM + 8MB flash config is specific to T-Display-S3 AMOLED boards |
| 85 | +- Boards with only 4MB flash need `CONFIG_DISPLAY_ENABLE=n` and the old partition table |
| 86 | + |
| 87 | +### Risks |
| 88 | + |
| 89 | +- RM67162 init sequence is board-specific; other AMOLED panels may need different commands |
| 90 | +- QSPI bus conflicts if other peripherals use SPI2_HOST (currently unused) |
| 91 | + |
| 92 | +## New Files |
| 93 | + |
| 94 | +| File | Purpose | |
| 95 | +|------|---------| |
| 96 | +| `main/display_hal.c/h` | RM67162 QSPI + CST816S touch HAL | |
| 97 | +| `main/display_ui.c/h` | LVGL 4-view UI | |
| 98 | +| `main/display_task.c/h` | FreeRTOS task, LVGL pump | |
| 99 | +| `main/lv_conf.h` | LVGL compile config | |
| 100 | +| `partitions_display.csv` | 8MB partition table | |
| 101 | +| `idf_component.yml` | Managed component deps | |
| 102 | + |
| 103 | +## Modified Files |
| 104 | + |
| 105 | +| File | Change | |
| 106 | +|------|--------| |
| 107 | +| `sdkconfig.defaults` | 8MB flash, SPIRAM, custom partitions | |
| 108 | +| `main/CMakeLists.txt` | Conditional display sources + deps | |
| 109 | +| `main/main.c` | +1 include, +5 lines guarded init | |
| 110 | +| `main/Kconfig.projbuild` | "AMOLED Display" menu | |
0 commit comments