10 KiB
10 KiB
Architecture And Flow Diagrams
This document captures the current project architecture and major runtime flows in Mermaid format. The diagrams reflect the implementation in the repository, not older planning notes.
Layered Architecture
flowchart TB
Main["main/app_main"]
App["app_composition\nstartup sequencing and wiring"]
CP["control_plane\npolicy, lifecycle, workers, retry/backoff"]
Domain["domain\nbusiness contracts, adapters, semantic mapping"]
Platform["platform\nhardware, transport, storage, display, audio"]
HW["ESP32-S3 hardware\nLCD, audio codec, GPIO, Wi-Fi, flash"]
Main --> App
App --> CP
CP --> Domain
Domain --> Platform
Platform --> HW
Component Responsibility Map
flowchart LR
subgraph AppComposition["app_composition"]
Boot["Plan A boot sequence"]
Wire["Runtime wiring"]
end
subgraph ControlPlane["control_plane"]
Connectivity["Connectivity bootstrap and Wi-Fi policy"]
Workers["Runtime worker ownership"]
Events["System event dispatch"]
MultimodalPolicy["Multimodal session policy"]
Fortune["Daily fortune policy"]
RemoteLog["Remote log service"]
FactoryCli["Factory CLI diagnostics"]
end
subgraph Domain["domain"]
ExpressionMap["LCD expression semantic mapping"]
RuntimeAdapters["Runtime facade adapters"]
Direction["Direction and calibration model"]
FortuneDomain["Birth profile and fortune contracts"]
UserConfig["LAN user config routes"]
end
subgraph Platform["platform"]
LCD["ST7789 direct LCD backend"]
Audio["ES8311 audio pipeline"]
Network["Wi-Fi, BLE provisioning, HTTP, WebSocket"]
Storage["NVS, LittleFS, LogFS"]
GPIO["GPIO, stepper, console UART"]
end
Boot --> Connectivity
Boot --> Workers
Wire --> Workers
Connectivity --> RuntimeAdapters
Workers --> Events
Workers --> RemoteLog
MultimodalPolicy --> RuntimeAdapters
Fortune --> FortuneDomain
Events --> ExpressionMap
FactoryCli --> RemoteLog
RuntimeAdapters --> LCD
RuntimeAdapters --> Audio
RuntimeAdapters --> Network
RuntimeAdapters --> Storage
RuntimeAdapters --> GPIO
Current Boot Sequence
sequenceDiagram
participant Main as app_main
participant App as app_composition
participant CP as control_plane
participant Domain as domain
participant Platform as platform
Main->>App: app_composition_start_default_runtime()
App->>CP: control_plane_prepare_runtime_storage()
CP->>Domain: prepare storage through runtime adapters
Domain->>Platform: NVS / storage initialization
App->>CP: control_plane_play_boot_sound(timeout)
CP->>Domain: domain_runtime_multimodal_audio_init()
Domain->>Platform: ES8311 / I2S / I2C audio init
CP->>Domain: domain_runtime_behavior_play_prompt_file()
Domain->>Platform: play prompt file
App->>CP: control_plane_init_lcd_device()
CP->>Domain: domain_runtime_init_lcd_device()
Domain->>Platform: LCD GPIO/SPI/ST7789 init
App->>CP: control_plane_start_lcd_render_worker()
CP->>CP: create cp_lcd_render task
CP->>Domain: domain_runtime_start_lcd_render_loop()
Domain->>Platform: lcd_direct_start_render_loop()
App->>CP: control_plane_init()
CP->>Domain: platform/domain runtime initialization
Domain->>Platform: stepper, storage, behavior adapters
App->>CP: control_plane_request_boot_expression()
CP->>Domain: request DOMAIN_EXPRESSION_IDLE
Domain->>Platform: queue LCD idle asset sequence
opt boot turntable calibration enabled
App->>CP: control_plane_calibrate()
CP->>Domain: direction calibration policy
Domain->>Platform: stepper + calibration switch
end
App->>CP: control_plane_bootstrap_connectivity()
CP->>Domain: Wi-Fi init / STA connect / BLE provisioning fallback
Domain->>Platform: Wi-Fi and provisioning primitives
App->>CP: control_plane_start_runtime_workers()
CP->>CP: start model, websocket wait, LCD, Wi-Fi state, events, keys, remote log, fortune
Runtime Worker Topology
flowchart TB
Start["control_plane_start_runtime_workers()"]
Start --> ModelQueue["ensure model command queue"]
Start --> ModelTask["cp_model_cmd"]
Start --> WsWait["WebSocket wait task"]
Start --> LcdTask["cp_lcd_render"]
Start --> WifiTask["cp_wifi_state"]
Start --> EventTask["cp_system_event"]
Start --> KeyTask["cp_key_input"]
Start --> RemoteLogTask["cp_remote_log"]
Start --> FortuneTask["cp_fortune\nif FORTUNE_ENABLE"]
KeyTask --> EventTask
EventTask --> LcdTask
WsWait --> Multimodal["multimodal client/runtime"]
Multimodal --> ModelTask
Multimodal --> Audio["audio capture/playback"]
WifiTask --> LcdTask
RemoteLogTask --> LogServer["remote log server"]
FortuneTask --> FortuneApi["daily fortune API"]
Connectivity Bootstrap Flow
flowchart TD
Start["control_plane_bootstrap_connectivity"]
InitWiFi["domain_runtime_network_wifi_init(seed credentials)"]
HasCreds{"saved Wi-Fi credentials?"}
StaConnect["connect STA with feedback"]
StaOk{"STA connected?"}
KeepCreds{"error keeps credentials?"}
ClearCreds["clear saved Wi-Fi credentials"]
ResolvePop["resolve provisioning PoP\nserial number preferred"]
HasPop{"PoP available?"}
BleProv["start BLE provisioning"]
Done["return ESP_OK"]
Error["return error"]
Start --> InitWiFi --> HasCreds
HasCreds -- yes --> StaConnect --> StaOk
StaOk -- yes --> Done
StaOk -- no --> KeepCreds
KeepCreds -- yes --> Done
KeepCreds -- no --> ClearCreds --> ResolvePop
HasCreds -- no --> ResolvePop
ResolvePop --> HasPop
HasPop -- yes --> BleProv --> Done
HasPop -- no --> Error
LCD Expression Flow
flowchart LR
CP["control_plane policy/event"]
DomainReq["domain_runtime_request_expression()"]
Resolve["domain_resolve_expression_platform_request()"]
Catalog["domain expression catalog\nIDLE, WIFI_DISCONNECTED,\nSERVER_ERROR, MUYU, CHOUQIAN, WUXING"]
PlatformReq["platform_lcd_request_presentation()"]
Pending["lcd_direct pending request"]
Render["cp_lcd_render loop"]
Draw["draw RLE asset frame\nfrom /littlefs/lcd"]
Panel["ST7789 panel"]
CP --> DomainReq --> Resolve --> Catalog --> PlatformReq --> Pending --> Render --> Draw --> Panel
Draw --> Delay["vTaskDelay(frame_ms)"]
Delay --> Render
Key And Local Event Flow
flowchart TD
GPIO["GPIO edge interrupt\nPTT GPIO15 / SW2 GPIO45"]
IsrQueue["key IRQ queue"]
KeyTask["cp_key_input\ndebounce and click state machine"]
SystemQueue["system event queue"]
EventTask["cp_system_event"]
PttState["PTT active state"]
Chouqian["SW2 short press\nDOMAIN_EXPRESSION_CHOUQIAN"]
Wuxing["SW2 double click\nDOMAIN_EXPRESSION_WUXING"]
Muyu["PTT double click\nDOMAIN_EXPRESSION_MUYU"]
GPIO --> IsrQueue --> KeyTask --> SystemQueue --> EventTask
EventTask --> PttState
EventTask --> Chouqian
EventTask --> Wuxing
EventTask --> Muyu
Multimodal Runtime Flow
sequenceDiagram
participant CP as control_plane
participant Domain as domain
participant Platform as platform
participant WS as WebSocket server
participant Audio as ES8311 audio path
participant LCD as LCD expression
CP->>Domain: prepare multimodal start
CP->>Domain: register event callback
CP->>Domain: register downstream audio callback
CP->>CP: start multimodal watchdog task
CP->>Domain: control_plane_multimodal_start()
Domain->>Platform: open WebSocket transport
Platform->>WS: connect and send Start directive
WS-->>Platform: connected / task events
Platform-->>Domain: multimodal events
Domain-->>CP: event callback
WS-->>Platform: downstream audio binary
Platform-->>Domain: downstream audio callback
Domain-->>CP: control_plane_downstream_audio_bridge()
CP->>Audio: play downstream audio
alt transport/task/output error while STA connected
CP->>LCD: request DOMAIN_EXPRESSION_SERVER_ERROR
end
alt server responding starts
CP->>Audio: stop uplink if running
end
Remote Logging Flow
flowchart TD
EspLog["ESP_LOGx / printf log"]
Hook["esp_log_set_vprintf hook"]
Filter["filter by CONFIG_TQ_REMOTE_LOG_MIN_LEVEL"]
RamQueue["RAM queue\nCONFIG_TQ_REMOTE_LOG_QUEUE_LEN"]
FlushTimer["periodic flush\nCONFIG_TQ_REMOTE_LOG_FLUSH_INTERVAL_MS"]
LogFS["/logfs/remote_logs\nLittleFS persistent queue"]
StaReady{"STA connected\nand realtime path idle?"}
Upload["HTTP POST JSON batch\nCONFIG_TQ_REMOTE_LOG_ENDPOINT"]
Server["remote_log_receiver.py\n/opt/logTools/logs"]
Backoff["exponential retry backoff"]
DropOld["trim old files\nCONFIG_TQ_REMOTE_LOG_MAX_FILES"]
CoreDump["detect flash coredump\nupload metadata report"]
EspLog --> Hook --> Filter --> RamQueue
RamQueue --> FlushTimer --> LogFS --> DropOld
LogFS --> StaReady
CoreDump --> StaReady
StaReady -- yes --> Upload --> Server
StaReady -- no --> Backoff --> StaReady
Upload -- fail --> Backoff
Upload -- success --> Delete["delete uploaded local file"]
Daily Fortune Flow
flowchart TD
Task["cp_fortune task"]
WaitTime["wait for local time / next midnight / refresh request"]
WaitNetwork["wait for STA and multimodal readiness"]
Date["resolve local date"]
Fetch["control_plane_refresh_daily_fortune(date)"]
DomainFortune["domain fortune APIs\nbirth profile + fortune result"]
Http["HTTPS fortune API"]
Persist["persist fortune result"]
Notify["notify observers / UI configuration server"]
Retry["retry delay on failure"]
Task --> WaitTime --> WaitNetwork --> Date --> Fetch
Fetch --> DomainFortune --> Http
Http -- success --> Persist --> Notify --> WaitTime
Http -- failure --> Retry --> WaitTime
Factory CLI Diagnostics Flow
flowchart LR
UART["UART0 serial input"]
CLI["factory CLI parser"]
Identity["identity / auth commands"]
LogStatus["log status"]
LogFlush["log flush"]
LogClear["log clear"]
RemoteLog["control_plane_remote_log_*"]
UARTOut["UART0 JSON/OK/ERROR output"]
UART --> CLI
CLI --> Identity --> UARTOut
CLI --> LogStatus --> RemoteLog --> UARTOut
CLI --> LogFlush --> RemoteLog --> UARTOut
CLI --> LogClear --> RemoteLog --> UARTOut