# Zacus BOX-3 Voice Pipeline

ESP-IDF firmware for ESP32-S3-BOX-3 — voice interface for
*Le Mystere du Professeur Zacus* escape room.

## Prerequisites

- ESP-IDF v5.2+ (with ESP32-S3 support)
- Python 3.8+ (for idf.py)

## Build

```bash
# Set target
idf.py set-target esp32s3

# Configure (optional — defaults in sdkconfig.defaults)
idf.py menuconfig

# Build
idf.py build

# Flash (USB-C on BOX-3)
idf.py -p /dev/tty.usbmodem* flash monitor
```

## Configuration

Use `idf.py menuconfig` > **Zacus BOX-3 Voice Configuration**:

| Option | Default | Description |
|--------|---------|-------------|
| WiFi SSID | `zacus-net` | Network for voice bridge |
| WiFi Password | *(empty)* | WPA2 password |
| Voice Bridge URL | `ws://192.168.4.1:8080/ws/voice` | mascarade WebSocket |
| Wake Word | `hi esp` | WakeNet9 trigger phrase |
| Speaker Volume | 70 | 0-100 |
| Mic Gain | 80 | 0-100 |

## Hardware

- **Board**: ESP32-S3-BOX-3 (16MB flash, 8MB PSRAM)
- **Codec**: ES8311 (I2C addr 0x18)
- **Display**: ILI9341 320x240
- **Mic**: onboard MEMS
- **Speaker**: onboard 1W with PA

## Architecture

```
[Mic] -> I2S -> WakeNet -> Voice Capture -> WebSocket -> mascarade bridge
                                                              |
[Speaker] <- I2S <- TTS audio <------------- WebSocket -------+
                                                              |
                                              hints engine <--+
```

## Générateur de stimulus (QR + mélodie)

En plus du pipeline vocal, la BOX-3 sert de **source de stimulus** pour le
master Freenove. Elle affiche un QR code (lu par la caméra du master) et joue
une mélodie (entendue par le micro du master). Cela permet de tester les
énigmes locales P1 (son) et P3 (QR) du master de bout en bout, sans QR imprimé
ni instrument de musique.

Les routes sont enregistrées sur le serveur HTTP existant (`scenario_server`)
au démarrage, juste après l'init de l'écran et de l'UI (`stimulus_init()` puis
`stimulus_register_routes()` dans `app_main`).

### `POST /stim/qr`

```json
{"text": "zacus-qr-1"}
```

Affiche le texte en QR plein écran : un widget `lv_qrcode` de **160 px** sur une
page LVGL dédiée, chargée au premier plan. Le rétroéclairage est **atténué à
35 %** pour réduire le halo de l'écran émissif et préserver le contraste pour la
caméra du master.

```bash
curl -X POST http://192.168.1.50/stim/qr \
  -H 'Content-Type: application/json' \
  -d '{"text":"zacus-qr-1"}'
# {"status":"ok","text":"zacus-qr-1"}
```

### `POST /stim/melody`

```json
{"notes": [60, 62, 64, 65], "ms": 400}
```

Joue la séquence de notes **MIDI** au haut-parleur. `notes` est un tableau
d'entiers `0-127` (60 = do central), `ms` est la durée par note en
millisecondes, bornée à `1-4000` (défaut 400). La séquence est jouée sur une
**tâche worker** dédiée, donc la réponse HTTP revient immédiatement. Maximum
32 notes ; les notes hors plage sont ignorées.

```bash
curl -X POST http://192.168.1.50/stim/melody \
  -H 'Content-Type: application/json' \
  -d '{"notes":[60,62,64,65,67],"ms":300}'
# {"status":"ok","notes":5}
```

### Note de terrain

Le décodage par la caméra du master du **QR affiché sur l'écran LCD n'est pas
fiable** : le contraste émissif est insuffisant pour quirc, qui ne retrouve pas
les motifs de repérage (finder patterns) quelle que soit la résolution, même
écran atténué. **La mélodie est le chemin de stimulus le plus robuste.** Pour
P3, le **QR imprimé reste recommandé** ; le `/stim/qr` sert surtout au cadrage
et au débogage de la caméra.

### Configuration Wi-Fi

Le générateur est joignable sur l'IP de la BOX-3 en mode station. Configurez le
réseau via `idf.py menuconfig` > **Zacus BOX-3 Voice Configuration**, ou
directement par `CONFIG_ZACUS_WIFI_SSID` / `CONFIG_ZACUS_WIFI_PASSWORD`.
