# 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.
