4.8 KiB
Contrat CMD ESP-NOW — cartes annexes Zacus (roadmap)
Statut : design / roadmap (D5 du redesign DSL). Aucun firmware annexe n'implémente encore ce contrat — ce document le spécifie pour que les cartes M5 / BOX-3 / PLIP / p7_coffre le décodent plus tard.
1. État actuel (constaté)
Le master Freenove relaie vers ses pairs ESP-NOW de deux façons :
- IR de scénario :
POST /game/scenario/relay {peers,ir}→scenario_mesh_send(chunké, ≤64 KiB, ack 300 ms). Sert à pousser un scénario à une carte. - Canal CMD texte :
scenario_mesh_send_text— texte libre UTF-8 ≤200 octets, déclenché parPOST /game/espnow/cmd {peer, command}. Aujourd'hui le master émet la trame ({ok:true, ESP_OK}) mais aucun executor annexe ne consomme les CMD entrants (elles sont seulement loguées).
Le routage gateway D2 (/v1/media/play, /v1/step/{board}) utilise déjà ce canal en best-effort avec des commandes texte ad-hoc : play:<path>, step:<id>. D5 formalise ce texte ad-hoc en schéma structuré.
2. Schéma CMD structuré
Trame = JSON compact, UTF-8, ≤ 200 octets (limite scenario_mesh_send_text). Forme :
{"op":"<verbe>","a":{<args>}}
op: verbe court (≤12 car.).a: objet d'arguments (clés courtes pour tenir dans 200 o). Absent si pas d'argument.- Pas d'espaces superflus (séparateurs
,/:compacts). Encodage côté gateway ; décodage côté carte annexe.
Verbes (v1)
op |
Args (a) |
Sémantique | Carte cible typique |
|---|---|---|---|
play |
{"p":"<path>","l":<0|1>} |
Jouer un média par chemin SD/LittleFS (l=loop) |
toutes (audio) |
stop |
— | Stopper l'audio | toutes |
vol |
{"v":<0-100>} |
Volume | toutes |
step |
{"s":"<step_id>"} |
Armer une étape | toutes |
screen |
{"t":"<title>","u":"<subtitle>","y":"<symbol>","e":"<effect>"} |
Afficher une scène LCD | M5/BOX-3 (écran) |
led |
{"p":"<pattern>"} |
Motif LED | M5/BOX-3 |
ring |
{"d":<sec>} |
Faire sonner (PLIP) | PLIP |
evt |
{"n":"<name>"} |
Remonter un événement vers le master (sens carte→master) | toutes |
Exemples (≤200 o)
{"op":"play","a":{"p":"zacus/box3/intro.wav","l":0}}
{"op":"step","a":{"s":"STEP_LEFOU"}}
{"op":"screen","a":{"t":"LABORATOIRE","y":"LA","e":"pulse"}}
{"op":"evt","a":{"n":"solved:1"}}
3. Sens master → carte (CMD) et carte → master (EVT)
- CMD (master→pair) : émise par le gateway via
POST /game/espnow/cmd {peer, command:<json>}. Le master relaie la trame ; la carte annexe décode{op,a}et exécute. - EVT (pair→master) : la carte annexe émet
{"op":"evt","a":{"n":"<name>"}}; le master la remonte au gateway (endpoint à définir, p.ex. réutiliser le log CMD/EVT entrant). Lesevt(solved:<id>,button:<x>,done) alimentent les transitions réelles (D4) côté gateway :evt n="solved:1"↔ transitiononPuzzleSolved(1)(event_type:unlock,event_name:puzzle:1).
4. Implémentation gateway (présent / futur)
- Présent (D2) :
/v1/media/playet/v1/step/{board}envoient déjà du texte (play:<path>,step:<id>) via/game/espnow/cmd. À migrer vers le JSON structuré ci-dessus (changement local, sans toucher le firmware master). - Futur : un endpoint d'EVT entrant (le master expose les CMD/EVT reçues) pour fermer la boucle carte→gateway→transition.
5. Implémentation firmware annexe (futur, hors de ce repo-ci)
Chaque carte annexe (M5 / BOX-3 / PLIP / p7_coffre) doit :
- recevoir la trame ESP-NOW texte (callback existant côté master à relayer/exposer) ;
- parser le JSON
{op,a}(parser compact, tolérant : trame inconnue → ignorée + log) ; - dispatcher sur
opvers ses capacités locales (audio I2S, écran, LED, sonnerie…) ; - émettre des
evtpour les événements joueur (énigme résolue, bouton…).
Tant que ce firmware n'existe pas, les blocs de la voie 🔬 hardware futur (M5/BOX-3/PLIP, etc.) restent validés mais non exécutés — le compilateur émet le warning « hardware non câblé » (D1), et le routage relais du gateway répond ok:false + warning D5 (cf. /v1/media/play, /v1/step sur une carte annexe).
6. Contraintes & notes
- Budget 200 o : privilégier des clés courtes (
p,s,t,v…). Unscreenavec title+subtitle longs peut dépasser → tronquer côté gateway (limites firmware : title≤47, subtitle≤63, symbol≤15) avant émission. - Idempotence / ordre : ESP-NOW n'est pas fiable ordonné ; les
opdoivent être idempotents quand c'est possible (step,screen,vol). Pour les séquences critiques, préférer le push d'IR complet (/game/scenario/relay). - Versionnement : si le schéma évolue, ajouter un champ
vau niveau racine ({"v":2,"op":...}) ; absence = v1.