# local_puzzles

Énigmes locales du master Freenove (FNK0102H Media Kit) : la séquence QR (P3)
et la mélodie au son (P1). Le composant câble la capture matérielle (caméra,
micro) à une logique de validation pure, et reporte les fragments de code
résolus dans `puzzle_state`.

## Sous-modules

- **`board_pins_mediakit.h`** — pin-map de la carte Freenove FNK0102H (gelée le
  2026-06-10, source de vérité). Caméra, micro I2S IN, haut-parleur I2S OUT et
  écran TFT ST7796, sans collision GPIO. À noter : les défauts de
  `voice_pipeline` ciblent un autre câblage et **entrent en collision** avec les
  pins caméra — ils doivent être surchargés par les valeurs FNK0102H (micro
  3/14/46, HP 42/41/1) à l'init de l'application.
- **`seq_validator`** / **`melody_validator`** — logique pure, sans I/O, testée
  sur hôte. `seq_validator` valide une séquence de codes QR dans l'ordre (labels
  distincts ; un mauvais scan réinitialise, un doublon du dernier code correct
  est ignoré). `melody_validator` valide une séquence de notes MIDI avec
  tolérance en demi-tons (notes répétées permises).
- **`mic_broker`** — propriétaire unique du micro I2S RX, partagé avec
  `voice_pipeline`. Route les trames PCM16 mono vers **un seul** consommateur
  actif selon le mode : `MIC_IDLE` (aucune trame), `MIC_NPC_LISTEN` (dialogue
  PNJ), `MIC_P1_SOUND` (énigme son). Init unique ; un second `mic_broker_init()`
  renvoie `ESP_ERR_INVALID_STATE`.
- **`qr_puzzle`** — caméra OV3660 en QVGA 320×240 grayscale (esp32-camera) +
  décodage **quirc**. La tâche de scan ne passe la trame à quirc que si sa
  géométrie correspond à la taille configurée (garde adaptatif à la géométrie du
  capteur) ; hook viewfinder optionnel avant décodage. Teardown caméra
  **asynchrone** après `stop` — `start` renvoie `ESP_ERR_INVALID_STATE` jusqu'à
  la fin du teardown (réessayer après ~100 ms).
- **`sound_puzzle`** — consomme les trames `MIC_P1_SOUND` et détecte les notes
  par taux de **passages par zéro** (zero-crossing → fréquence fondamentale →
  note MIDI), avant validation par `melody_validator`.
- **`local_puzzles`** — couche de câblage : `local_puzzles_init` (cible
  d'agrégation), `arm_qr` / `arm_sound` (armer une énigme avec sa séquence
  attendue et le fragment à reporter), `disarm` (arrêter les deux). Les callbacks
  de résolution ne doivent pas ré-armer ni désarmer directement — différer à une
  autre tâche.

## Tests hôte

```bash
make -C idf_zacus/components/local_puzzles/test/host test
```

6 tests (3 `seq_validator`, 3 `melody_validator`) compilés et exécutés sur hôte,
sans matériel. Nécessite Unity (`UNITY_DIR`, défaut `~/esp/esp-idf/...`).

## Notes / incohérences

- **Capteur** : le matériel est un **OV3660**. Le code et certains commentaires
  (ex. en-tête `qr_puzzle.h`) disent parfois OV2640 à tort.
- **QR sur LCD** : le décodage par la caméra d'un QR affiché sur un écran LCD
  (émissif) n'est pas fiable — préférer un QR imprimé pour P3.
