From 5a2d86e2534803bd06ae2ab3377db562783e51e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20SAILLANT?= <108685187+electron-rare@users.noreply.github.com> Date: Mon, 23 Feb 2026 21:33:08 +0100 Subject: [PATCH 1/2] Refactor audio engine and command dispatcher; update ESP-NOW handling - Updated AudioEngine to integrate AudioTools and improve WAV playback handling. - Modified A252ConfigStore to change default SLIC pin configurations. - Enhanced CommandDispatcher to provide clearer error messages for unsupported commands. - Removed unused Bluetooth and WiFi functionalities from main application logic. - Implemented payload size and peer limits in EspNowBridge to improve stability. - Added documentation for ESP-NOW contract to clarify message structure and supported commands. --- docs/espnow_api_v1.md | 110 ++++----- docs/espnow_contract.md | 73 ++++++ platformio.ini | 35 ++- src/audio/AudioEngine.cpp | 435 ++++++++++++++------------------- src/audio/AudioEngine.h | 31 +-- src/config/A252ConfigStore.h | 11 +- src/core/CommandDispatcher.cpp | 6 +- src/main.cpp | 397 ++++++------------------------ src/props/EspNowBridge.cpp | 26 ++ 9 files changed, 462 insertions(+), 662 deletions(-) create mode 100644 docs/espnow_contract.md diff --git a/docs/espnow_api_v1.md b/docs/espnow_api_v1.md index 24b9a99..ac9665a 100644 --- a/docs/espnow_api_v1.md +++ b/docs/espnow_api_v1.md @@ -1,92 +1,88 @@ -# ESP-NOW API v1 (enveloppe `msg_id/seq/type/payload/ack`) +# ESP-NOW API v1 -Date: 2026-02-21 -Scope: contrat d'échange entre `RTC_BL_PHONE` (A252) et la seconde carte. +Date: 2026-02-23 -## 1. Objectif +Ce document est synchronisé avec le contrat opérationnel: `docs/espnow_contract.md`. -Normaliser les trames ESP-NOW pour: -- corréler requête/réponse, -- conserver compatibilité legacy, -- simplifier l'intégration du second repo. +## Source de vérité -## 2. Requête v1 (nouveau format recommandé) +- Canonique: `docs/espnow_contract.md` +- Ce fichier doit rester cohérent avec ce contrat. + +## Reprise du contrat (v1) + +- Trame recommandée: `type: "command"`, `ack: true`. +- Corrélation: `msg_id` + `seq`. +- Corps: objet JSON dans `payload`, ou texte compatible selon parser legacy. + +Exemple de demande: ```json { - "proto": "rtcbl/1", "msg_id": "req-001", - "seq": 1, + "seq": 7, "type": "command", "ack": true, "payload": { - "cmd": "STATUS", - "args": {} + "cmd": "WIFI_STATUS" } } ``` -Règles: -- `proto=rtcbl/1` recommandé (toléré absent pour compat). -- `msg_id` sert à corréler la réponse. -- `seq` est un compteur local de trame (recommandé monotone par source). -- `type=command|request|cmd` déclenche l'exécution côté firmware. -- `ack=true` demande une réponse corrélée. -- `payload.cmd` obligatoire pour une commande dispatcher. -- `payload.args` optionnel; sérialisé puis passé au dispatcher. - -## 3. Réponse v1 (ack corrélée) +Réponse attendue: ```json { - "proto": "rtcbl/1", "msg_id": "req-001", - "seq": 1, + "seq": 7, "type": "ack", "ack": true, "payload": { "ok": true, - "code": "STATUS", - "data": {}, - "error": "" + "code": "WIFI_STATUS", + "error": "", + "data": {} } } ``` -Règles: -- `msg_id` et `seq` reprennent la requête. -- `payload.ok=false` => `payload.error` non vide. -- `payload.data` contient le JSON de la commande si disponible. -- fallback possible `payload.data_raw` si la réponse n'est pas JSON. +## Compatibilité parser (legacy) -## 4. Compatibilité legacy +- `cmd`, `command`, `action` (root ou `payload`). +- format texte: `"CMD arg"`. +- ancien champ `id` au lieu de `msg_id` côté trames historiques. -Le firmware continue d'accepter les formats existants: -- `{"cmd":"..."}` -- `{"raw":"..."}` -- `{"command":"..."}` -- `{"action":"..."}` -- variantes imbriquées via `event`, `message`, `payload` -- format historique `rtcbl/1`: - - `{"proto":"rtcbl/1","id":"...","cmd":"...","args":{}}` - -## 5. Commandes recommandées v1 +## Commandes supportées - `STATUS` -- `RING` -- `CALL` -- `HOOK` -- `LIST_FILES` -- `PLAY_FILE` - `WIFI_STATUS` -- `MQTT_STATUS` - `ESPNOW_STATUS` -- `BT_STATUS` +- `UNLOCK` +- `NEXT` +- `WIFI_DISCONNECT` +- `WIFI_RECONNECT` +- `ESPNOW_ON` +- `ESPNOW_OFF` +- `STORY_REFRESH_SD` +- `SC_EVENT` +- `RING` +- `SCENE ` -## 6. Intégration second repo +## Erreurs connues -Checklist minimum côté seconde carte: -1. Émettre `msg_id` unique + `seq` monotone. -2. Positionner `type=command` et `ack=true` pour obtenir une réponse. -3. Implémenter timeout de réponse (2-5s) et corréler sur `msg_id`. -4. Prévoir fallback legacy (`rtcbl/1` + formats `cmd/raw/command/action`) pour compat. +- `unsupported_command` +- `missing_scene_id` +- `scene_not_found` +- erreurs réseau: `peer`, `payload` vide, trame > 240 + +## Limites runtime + +- Trame brute max: `240` +- Peers: `16` +- RX queue: `6` + +## Réception firmware + +- `type=command` -> `executeEspNowCommandPayload` +- `type` non-`command` ignoré pour dispatch de commande +- `type=ack` ignoré côté dispatch commande diff --git a/docs/espnow_contract.md b/docs/espnow_contract.md new file mode 100644 index 0000000..27bf54e --- /dev/null +++ b/docs/espnow_contract.md @@ -0,0 +1,73 @@ +# ESP-NOW Contract — Freenove (Quick) + +Cible: intégration seconde carte (app/RTC). + +## Recommandation trame +- `type=command` +- `msg_id` et `seq` pour corrélation +- `ack=true` pour obtenir un retour +- `payload` en JSON ou texte + +Exemple recommandé: + +```json +{ + "msg_id": "req-001", + "seq": 7, + "type": "command", + "ack": true, + "payload": { + "cmd": "WIFI_STATUS" + } +} +``` + +## Réponse ACK attendue + +```json +{ + "msg_id": "req-001", + "seq": 7, + "type": "ack", + "ack": true, + "payload": { "ok": true, "code": "WIFI_STATUS", "error": "", "data": {} } +} +``` + +## Compatibilité entrées +Le parseur accepte aussi: + +- `cmd`, `command`, `action` (root ou `payload`) +- format texte "`CMD arg`" + +## Commandes supportées (ESP-NOW) + +- `STATUS` +- `WIFI_STATUS` +- `ESPNOW_STATUS` +- `UNLOCK`, `NEXT` +- `WIFI_DISCONNECT`, `WIFI_RECONNECT` +- `ESPNOW_ON`, `ESPNOW_OFF` +- `STORY_REFRESH_SD` +- `SC_EVENT` +- `RING` +- `SCENE ` + - exemples: `SCENE SCENE_WIN_ETAPE` + - JSON: `{"cmd":"SCENE","args":{"id":"SCENE_WIN_ETAPE"}}` +- Actions de contrôle (générique): `HW_*`, `AUDIO_*`, `MEDIA_*`, etc. + +## Erreurs fréquentes +- `unsupported_command` +- `missing_scene_id` +- `scene_not_found` +- erreurs réseau/connexion habituelles (`peer`, `payload` vide, trame > 240) + +## Limites runtime +- Trame brute max: `240` +- Peers: `16` +- RX queue: `6` + +## Réception côté firmware +- `type=command` -> `executeEspNowCommandPayload` +- autre `type` -> ignoré en commande (bridge story seulement si activé) +- les frames `type=ack` reçues sont ignorées côté dispatch diff --git a/platformio.ini b/platformio.ini index 91a29e7..2ad6b4a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,16 +11,10 @@ monitor_speed = 115200 test_build_src = yes build_flags = -DCORE_DEBUG_LEVEL=1 - -DCONFIG_ASYNC_TCP_STACK_SIZE=3072 - -DCONFIG_ASYNC_TCP_QUEUE_SIZE=8 - -DCONFIG_ASYNC_TCP_PRIORITY=8 lib_deps = bblanchon/ArduinoJson@^7.0.4 - ESP32Async/AsyncTCP@^3.3.2 - ESP32Async/ESPAsyncWebServer@^3.6.0 throwtheswitch/Unity@^2.6.1 - knolleary/PubSubClient@^2.8 - https://github.com/luisllamasbinaburo/Arduino-List.git#master + https://github.com/pschatzmann/arduino-audio-tools.git lib_ignore = ESPAsyncTCP RPAsyncTCP @@ -29,17 +23,43 @@ lib_ignore = [env:esp32dev] board = esp32dev board_build.partitions = huge_app.csv +upload_port = /dev/cu.usbserial +monitor_port = /dev/cu.usbserial build_flags = ${env.build_flags} -DBOARD_PROFILE_A252 +build_src_filter = + + + +