This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# Freenove Media Kit — énigmes locales (P1 son, P3 QR) — Design Spec
|
||||
|
||||
> Statut : design validé en brainstorming (2026-06-10), à relire avant plan
|
||||
> d'implémentation. Approche retenue : **A — composant `local_puzzles`**.
|
||||
|
||||
## Contexte & objectif
|
||||
|
||||
La Freenove **ESP32-S3 Media Kit v1.2** (module **N16R8** : 16 Mo flash,
|
||||
8 Mo PSRAM octale, caméra **OV2640**, micro I2S MEMS, haut-parleur, SD,
|
||||
batterie) exécute le firmware master `idf_zacus`. Aujourd'hui le master est un
|
||||
orchestrateur headless (ESP-NOW hub, `game_endpoint`, `voice_pipeline`,
|
||||
`npc_engine`, relay scénario), et les énigmes sont des **nœuds ESP32 dédiés**
|
||||
(`puzzles/p1,p5,p6,p7`).
|
||||
|
||||
**Objectif** : faire de la Freenove un **master portable tout-en-un** qui, en
|
||||
plus d'orchestrer, prend en charge **directement** deux énigmes via ses
|
||||
capteurs intégrés :
|
||||
|
||||
- **P3 — QR Treasure** : scanner N QR codes dans le bon ordre (caméra OV2640).
|
||||
- **P1 — Séquence sonore** : valider une mélodie reproduite par le joueur
|
||||
(micro I2S).
|
||||
|
||||
Les énigmes **P5 morse / P6 NFC / P7 coffre**, ainsi que **BOX-3** et **PLIP**,
|
||||
restent des nœuds ESP-NOW : aucune modification de leur firmware.
|
||||
|
||||
### Non-objectifs
|
||||
|
||||
- Pas de migration de P5/P6/P7 sur la Freenove (périphériques externes non
|
||||
« média » ; hors scope de ce spec).
|
||||
- Pas de suppression du protocole ESP-NOW ni du `game_endpoint` existant.
|
||||
- Pas de refonte du `npc_engine` / `voice_pipeline` au-delà du strict
|
||||
nécessaire pour partager le micro.
|
||||
|
||||
## Architecture — `idf_zacus/components/local_puzzles/`
|
||||
|
||||
Nouveau composant, trois unités à responsabilité unique + un point d'entrée
|
||||
commun. Chaque unité est testable et remplaçable indépendamment.
|
||||
|
||||
### `mic_broker`
|
||||
|
||||
Possède le canal I2S micro (RX) et distribue les frames PCM16 16 kHz à **un
|
||||
seul** consommateur actif, selon un mode explicite :
|
||||
|
||||
```
|
||||
typedef enum { MIC_IDLE, MIC_NPC_LISTEN, MIC_P1_SOUND } mic_mode_t;
|
||||
void mic_broker_set_mode(mic_mode_t mode);
|
||||
// Un consommateur s'enregistre ; il ne reçoit des frames que dans son mode.
|
||||
void mic_broker_register(mic_mode_t mode, mic_frame_cb_t cb, void *ctx);
|
||||
```
|
||||
|
||||
- Résout la **contention micro** par construction : NPC et P1 ne tournent
|
||||
jamais en même temps.
|
||||
- `voice_pipeline` est refactoré pour **tirer ses frames du broker**
|
||||
(`MIC_NPC_LISTEN`) au lieu de posséder l'I2S directement. Changement minimal :
|
||||
le broker encapsule l'init I2S existante du `voice_pipeline`.
|
||||
- Le mode est piloté par l'état de scénario (voir Flux de données).
|
||||
|
||||
### `qr_puzzle`
|
||||
|
||||
- Init **`esp32-camera`** (composant `espressif/esp32-camera`) avec le pin-map
|
||||
**Freenove ESP32-S3 Media Kit** (OV2640, voir Annexe pin-map).
|
||||
- Décodage QR via **`quirc`** (bibliothèque C, intégrée en composant — **pas**
|
||||
la lib Arduino `lib/ESP32QRCodeReader_vendor`, incompatible IDF).
|
||||
- Boucle de scan active **uniquement** quand P3 est en cours ; accumule les
|
||||
codes lus et valide la **séquence** contre l'ordre attendu (réutilise la
|
||||
logique `correct_order` du bloc P3 de l'atelier).
|
||||
- Caméra **dé-initialisée** hors P3 (`esp_camera_deinit`) — économie batterie
|
||||
et libération PSRAM.
|
||||
|
||||
### `sound_puzzle`
|
||||
|
||||
- Consommateur `MIC_P1_SOUND` du broker.
|
||||
- Détection d'attaque/hauteur (énergie + estimation de fréquence, fenêtres
|
||||
glissantes) ; valide la **séquence de notes** reproduite contre la mélodie
|
||||
attendue (logique reprise du nœud `puzzles/p1_sequence_sonore`).
|
||||
- Tolérance/seuils paramétrables (difficulté).
|
||||
|
||||
### `local_puzzle_report()`
|
||||
|
||||
```
|
||||
void local_puzzle_report(uint8_t puzzle_id, const uint8_t code_fragment[4]);
|
||||
```
|
||||
|
||||
Point d'entrée commun : **synthétise un événement « puzzle résolu »** dans
|
||||
`game_endpoint`, identique au chemin du handler `MSG_PUZZLE_SOLVED` des nœuds
|
||||
ESP-NOW. Le master traite ainsi énigmes **locales et distantes de façon
|
||||
uniforme** (assemblage du code, ligne NPC, LED, scoring).
|
||||
|
||||
## Flux de données
|
||||
|
||||
```
|
||||
scénario/état de jeu
|
||||
│ active P1 ou P3 (étape courante)
|
||||
▼
|
||||
local_puzzles ──arme le module──► qr_puzzle / sound_puzzle
|
||||
│ règle le mode micro (mic_broker_set_mode)
|
||||
▼
|
||||
succès (séquence/mélodie correcte)
|
||||
│
|
||||
▼
|
||||
local_puzzle_report(puzzle_id, code_fragment)
|
||||
│
|
||||
▼
|
||||
game_endpoint ──► même aval que les énigmes ESP-NOW
|
||||
(code assemblé, NPC, LED, score)
|
||||
```
|
||||
|
||||
## Configuration & compatibilité
|
||||
|
||||
Chaque énigme est déclarée **`local` vs `remote`** (dans `boards.yaml` master
|
||||
ou le scénario). Le **même firmware** fonctionne que P1/P3 soient locaux
|
||||
(Freenove) ou en nœud ESP-NOW. Le nœud `puzzles/p1_sequence_sonore` devient
|
||||
**optionnel** : repli si la caméra/micro local est indisponible.
|
||||
|
||||
## Gestion des erreurs & batterie
|
||||
|
||||
- **Échec init caméra** → P3 retombe sur le nœud ESP-NOW s'il est déclaré,
|
||||
sinon log d'erreur et P3 inactif (le reste du jeu continue).
|
||||
- **Contention micro** → impossible (mode exclusif du broker).
|
||||
- **Batterie** → caméra dé-initialisée hors P3 ; micro en `MIC_IDLE` quand ni
|
||||
NPC ni P1 actifs. Pas de capture inutile.
|
||||
- **PSRAM** → 8 Mo octale (N16R8) : framebuffer caméra (QVGA/VGA suffisant pour
|
||||
QR) + quirc large ; budget confortable.
|
||||
|
||||
## Tests
|
||||
|
||||
- **Logique pure (host)** : validateur de séquence QR (`correct_order`) et
|
||||
validateur de mélodie (séquence de notes + tolérance) — tests unitaires sans
|
||||
matériel.
|
||||
- **Smoke hardware** : scanner un QR connu → `qr_puzzle` rapporte la bonne
|
||||
valeur ; jouer un ton/mélodie → `sound_puzzle` valide ; vérifier que
|
||||
`local_puzzle_report` déclenche le même aval qu'un `MSG_PUZZLE_SOLVED` ESP-NOW.
|
||||
- **Régression** : NPC vocal toujours fonctionnel (le broker n'a pas cassé le
|
||||
`voice_pipeline`).
|
||||
|
||||
## Découpage suggéré (pour le plan)
|
||||
|
||||
1. `mic_broker` + refacto `voice_pipeline` pour passer par le broker (NPC
|
||||
inchangé fonctionnellement). **Indépendant, testable seul.**
|
||||
2. `qr_puzzle` (caméra + quirc + validateur de séquence) + `local_puzzle_report`
|
||||
branché sur `game_endpoint`. **Livrable visible : P3 sur la Freenove.**
|
||||
3. `sound_puzzle` (détection mélodie via broker). **Livrable : P1 sur la
|
||||
Freenove.**
|
||||
4. Flag de config `local|remote` par énigme + repli ESP-NOW.
|
||||
|
||||
Chaque étape compile et est flashable indépendamment.
|
||||
|
||||
## Annexe — pin-map caméra Freenove ESP32-S3 (OV2640)
|
||||
|
||||
À confirmer sur le schéma **Media Kit v1.2** avant implémentation (valeurs
|
||||
Freenove ESP32-S3-WROOM CAM standard) :
|
||||
|
||||
| Signal | GPIO | Signal | GPIO |
|
||||
|---|---|---|---|
|
||||
| PWDN | -1 | HREF | 7 |
|
||||
| RESET | -1 | PCLK | 13 |
|
||||
| XCLK | 15 | D7 | 16 |
|
||||
| SIOD (SDA) | 4 | D6 | 17 |
|
||||
| SIOC (SCL) | 5 | D5 | 18 |
|
||||
| VSYNC | 6 | D4 | 12 |
|
||||
| D3 | 10 | D2 | 8 |
|
||||
| D1 | 9 | D0 | 11 |
|
||||
|
||||
**Micro I2S MEMS** : pins à confirmer sur le schéma Media Kit v1.2 (le
|
||||
`voice_pipeline` actuel connaît déjà la config micro du master — la réutiliser
|
||||
via le broker plutôt que la redéfinir).
|
||||
|
||||
## Inconnues résiduelles (à lever au début du plan)
|
||||
|
||||
1. Pins exactes du **micro I2S** sur le Media Kit v1.2 (récupérer depuis la
|
||||
config `voice_pipeline` existante).
|
||||
2. Confirmer **OV2640** (vs OV5640) et la résolution minimale fiable pour le
|
||||
décodage QR à la distance de jeu.
|
||||
3. Disponibilité du composant **`quirc`** en registre IDF, sinon vendoring.
|
||||
Reference in New Issue
Block a user