┌─────────────────────────────────────────┐
│ main (product / demo wiring, Kconfig) │
├─────────────────────────────────────────┤
│ cogitor_iot / alicloud_* / thingsboard │
│ (alicloud_oss: OssService + upload) │
├─────────────────────────────────────────┤
│ embed_extra (camera, mjpeg, led strip) │
│ embed_core (wifi, mqtt, metrics) │
├─────────────────────────────────────────┤
│ embed (registry, event loop, signal) │
└─────────────────────────────────────────┘
Dependencies point downward only. Cloud providers implement embed::MqttCredentials and consume MqttService signals; they must not become required by embed itself.
main/ selects a cloud path (default: Cogitor). Credentials are loaded from NVS (fctry, Kconfig seed on first boot), kept static, and passed into MqttService + the vendor service.
| Concern | Mechanism |
|---|---|
| Auth | MQTT username/client_id = {product}.{device}, password = access token |
| Online | MQTT session present (platform tracks CONNECT) |
| Offline | LWT on status topic (up/status / v1/s), retained |
| Reported attrs | Device → attributes/report / v1/a (flat JSON) |
| Desired attrs | Platform → attributes/update / v1/a/upd; device may request on connect |
| Correlation | JSON field "id" (not in topic path) |
Protocol details: iot-platform-mqtt-spec.md.
| Path | Loop | Use |
|---|---|---|
Framework Signal / Slot |
Custom embed_evt loop |
Service-to-service POD messages |
| WiFi / IP events | Default ESP-IDF loop | Handled inside WifiService, then translated to Signals |
| MQTT client events | MQTT task / default loop | Handled inside MqttService, then translated to Signals |
Keep Slot handlers short. Blocking HTTP, TLS downloads, or long flash writes must run on a dedicated FreeRTOS task. embed_evt stack is EMBED_EVENT_TASK_STACK_SIZE (default 8192) — MQTT RPC + cJSON + LED RMT do not fit in 4 KB.
Services are placement-new'd into fixed slots (EMBED_SERVICE_SIZE, default 512). Large state (Alink modules, buffers) should live on the heap (std::unique_ptr) while the registry-resident object stays small — see AlicloudService.
With EMBED_THREAD_SAFE=1 (default):
ServiceRegistrycreate/get are mutex-protectedConnectionPoolallocate / release / count are mutex-protectedEventLoopregister/unregister take the loop mutex
Signal::emit posts with a bounded timeout (EMBED_EVENT_POST_TIMEOUT_MS); on failure the message is dropped and logged.
| Object | Owner |
|---|---|
| Service instances | ServiceRegistry pool (process lifetime) |
| MQTT credentials | Caller (static in app_main) |
| Slot connection | Slot → Connection → pool entry |
| Camera frame buffers | Camera pipeline (do not redesign without an explicit ownership plan) |
| Alink modules | AlicloudService via unique_ptr |
| Lives in | Examples |
|---|---|
NVS fctry (on device) |
WiFi password, Cogitor access token, broker LAN IP — survives OTA / idf.py flash |
| Config portal | SoftAP + HTTP (and optional STA HTTP) to write fctry; GPIO/RPC factory reset |
sdkconfig (local, gitignored) |
First-boot seed for fctry (skipped after factory reset / portal flag) |
sdkconfig.defaults (committed) |
Non-secret defaults, feature toggles |
| Source | Service graph, topic builders, protocol logic |