feat: Zacus V3 hardware + kit + playtest

Phase 2: BOM 107 components (727€), GPIO mapping,
enclosures 3D, suitcase layout
Phase 7: Power management, assembly checklist,
maintenance guide
Phase 8: Playtest protocol, metrics template,
serial log collector (Python)
This commit is contained in:
L'électron rare
2026-04-03 09:20:25 +02:00
parent 700e361986
commit 5b73170da3
10 changed files with 2216 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
session_id,date,group_size,target_duration,actual_duration,group_profile_detected,group_profile_actual,puzzles_solved,puzzles_skipped,hints_total,nps_score,notes
beta_001,,,,,,,,,,,
beta_002,,,,,,,,,,,
1 session_id date group_size target_duration actual_duration group_profile_detected group_profile_actual puzzles_solved puzzles_skipped hints_total nps_score notes
2 beta_001
3 beta_002
+187
View File
@@ -0,0 +1,187 @@
# Protocole de Bêta-Test — Le Mystère du Professeur Zacus V3
> Version : 1.0 — 2026-04-03
> Objectif : Valider l'expérience complète avec 2 groupes minimum (profil TECH + profil NON_TECH)
---
## Sélection des Groupes
| Critère | Groupe 1 (TECH) | Groupe 2 (NON_TECH) |
|---------|-----------------|---------------------|
| Taille | 46 personnes | 46 personnes |
| Profil | Développeurs, ingénieurs, makers | Artistes, enseignants, étudiants hors tech |
| Âge recommandé | 1845 ans | 1050 ans |
| Expérience escape room | Pas d'expérience (ou peu) | Pas d'expérience (ou peu) |
| Langue | Français | Français |
---
## Brief Pré-Session (30 min avant chaque groupe)
### Checklist déploiement complet
- [ ] Checklist 15 minutes (`docs/deployment/15min_setup_checklist.md`) complétée
- [ ] Durée cible réglée à **60 minutes** pour le bêta-test
- [ ] Caméra d'observation positionnée (consentement joueurs signé)
- [ ] Grille d'observation imprimée pour chaque observateur (cette page, section "Points d'observation")
- [ ] Questionnaire NPS préparé (papier ou Typeform sur téléphone)
- [ ] Chronomètre prêt (ou timer BOX-3 activé)
- [ ] Maître du jeu en position d'observation silencieuse
### Consignes aux observateurs
L'observateur **ne participe pas** et **ne donne aucune aide** sauf urgence de sécurité. Il note en silence :
- Les temps de résolution
- Les comportements de groupe
- Les verbatims spontanés (noter mot pour mot)
- Les moments de blocage, frustration, découverte
---
## Points d'Observation par Puzzle
Pour chaque puzzle, l'observateur complète la grille suivante :
### Grille d'observation (par puzzle)
| Puzzle | Heure début | Heure fin | Durée | Indices demandés | Tentatives ratées | Verbatims clés | Observation |
|--------|------------|-----------|-------|-----------------|-------------------|----------------|-------------|
| P1 — Séquence Sonore | | | | | | | |
| P2 — Circuit LED | | | | | | | |
| P3 — QR Treasure | | | | | | | |
| P4 — Fréquence Radio | | | | | | | |
| P5 — Code Morse | | | | | | | |
| P6 — Symboles Alchimiques | | | | | | | |
| P7 — Coffre Final | | | | | | | |
### Comportements à noter impérativement
- **Confusion initiale :** "C'est quoi ça ?", "On fait quoi ?" sans lire les instructions
- **Blocage prolongé** : plus de 5 min sans progression visible
- **Moment "Eurêka"** : réaction physique ou verbale de découverte
- **Frustration** : abandon, verbalisation négative, regard vers l'observateur
- **Collaboration** : qui prend le leadership ? comment le groupe se coordonne-t-il ?
- **Réaction au Professeur Zacus** : rire, sourire, question sur la voix (est-ce un humain ?)
---
## Vérification de la Classification NPC (Phase de Profilage)
À noter après la résolution de P1 et P2 :
| Donnée | Valeur mesurée |
|--------|---------------|
| Temps P1 (P1 solve time) | _______ secondes |
| Temps P2 (P2 solve time) | _______ secondes |
| Classification NPC affichée sur BOX-3 | TECH / NON_TECH / MIXED |
| Votre évaluation subjective du groupe | TECH / NON_TECH / MIXED |
| Classification correcte ? | OUI / NON |
**Objectif bêta :** > 80 % de classifications correctes sur l'ensemble des sessions.
---
## Suivi de la Précision Temporelle
| Donnée | Valeur |
|--------|--------|
| Durée cible configurée | 60 minutes |
| Heure de début effective | ____:____ |
| Heure de fin effective | ____:____ |
| Durée réelle | _______ minutes |
| Écart (réel - cible) | _______ minutes (_______ %) |
**Objectif bêta :** écart ≤ ± 10 % de la durée cible (soit ≤ 6 minutes sur 60 min).
---
## Débrief Post-Session (30 min après la fin du jeu)
### Phase 1 : Discussion libre (10 min)
Laisser le groupe s'exprimer librement. Questions ouvertes :
- "Quelle a été votre première impression en arrivant ?"
- "Quel puzzle vous a le plus surpris ?"
- "Avez-vous cru que le Professeur était une vraie personne ?"
- "Qu'est-ce qui vous a le plus bloqués ?"
- "Si vous pouviez changer une chose, ce serait quoi ?"
### Phase 2 : Enquête NPS (10 min)
Distribuer le questionnaire NPS (voir section suivante). Anonyme, individuel, sans discussion.
### Phase 3 : Questions techniques (10 min, optionnel)
Pour les groupes curieux (souvent TECH) :
- Montrer l'architecture ESP-NOW sur BOX-3
- Expliquer le clonage vocal XTTS-v2
- Montrer le scoring adaptatif
---
## Questionnaire NPS — 10 Questions (Français)
> Instructions : Notez chacune des affirmations suivantes de 1 (pas du tout) à 10 (absolument).
**Q1.** Dans quelle mesure recommanderiez-vous cette expérience à un ami ou collègue ?
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q2.** Le Professeur Zacus vous a semblé crédible et engageant comme personnage.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q3.** La difficulté des puzzles était adaptée à votre groupe.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q4.** Les instructions de chaque puzzle étaient claires et compréhensibles.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q5.** La qualité de la voix du Professeur Zacus était convaincante.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q6.** L'ambiance générale (décor, son, lumières) vous a immergé dans l'univers.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q7.** Vous avez ressenti une progression narrative cohérente du début à la fin.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q8.** Le matériel physique (boîtiers, tablettes, télégraphe) vous a semblé solide et bien fait.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q9.** L'expérience a favorisé la cohésion et la collaboration au sein de votre groupe.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Q10.** Vous seriez prêt(e) à participer à une prochaine version ou à un scénario différent.
`[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][ 10 ]`
**Questions ouvertes :**
- Votre puzzle préféré et pourquoi : ___________________________________
- Le moment le plus confus ou frustrant : ___________________________________
- Suggestions libres : ___________________________________
---
## Critères de Succès du Bêta-Test
| Critère | Objectif | Mesure |
|---------|----------|--------|
| Taux de complétion | Les 2 groupes atteignent GAME_OUTRO | BOX-3 session log |
| NPS moyen (Q1) | ≥ 8/10 | Questionnaire |
| Temps de setup | ≤ 15 minutes | Chronomètre |
| Précision durée | Écart ≤ ±10 % | Mesure directe |
| Classification NPC | Correcte pour les 2 groupes | Grille observation |
| Pannes matériel | Zéro pendant la session | Grille observation |
| Voix perçue comme humaine | ≥ 3/5 joueurs ne font pas la différence | Q5 + verbatims |
---
## Checklist Observateur (pendant le jeu)
- [ ] Timer démarré à `[GAME_START]` (synchro avec BOX-3)
- [ ] Grille observation imprimée, stylo en main
- [ ] Téléphone en mode silencieux
- [ ] Caméra en marche (si consentement obtenu)
- [ ] Ne pas répondre aux questions des joueurs (sourire et pointer BOX-3)
- [ ] Marquer toute panne matérielle immédiatement (heure + description)
- [ ] Marquer toute intervention NPC (heure + phrase + réaction groupe)
- [ ] À la fin : noter heure exacte de GAME_OUTRO et score affiché
+113
View File
@@ -0,0 +1,113 @@
Puzzle,Reference,Value,Footprint,Quantity,LCSC_Part,Unit_Price_EUR,Total_Price_EUR,Datasheet_URL
P1 Séquence Sonore,MCU_P1,ESP32-S3-DevKitC-1-N16R8,Module_ESP32-S3:ESP32-S3-DevKitC-1,1,C2913202,12.00,12.00,https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html
P1 Séquence Sonore,U1_P1,MAX98357AEWL+T I2S Class-D Amp,Package_SON:WLCSP-9_1.595x1.415mm_Pitch0.4mm,1,C236813,2.50,2.50,https://datasheets.maximintegrated.com/en/ds/MAX98357A-MAX98357B.pdf
P1 Séquence Sonore,SP1_P1,Speaker 8ohm 2W 57mm,Connector:PinHeader_2.54mm_1x02,1,,2.80,2.80,https://www.tme.eu/Document/bc34b0283d78d58b7ec55d24a1e3fde4/KPSP-0520L.pdf
P1 Séquence Sonore,SW1_P1_RED,Arcade Button 30mm RED,Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical,1,,2.80,2.80,https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
P1 Séquence Sonore,SW2_P1_BLUE,Arcade Button 30mm BLUE,Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical,1,,2.80,2.80,https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
P1 Séquence Sonore,SW3_P1_YELLOW,Arcade Button 30mm YELLOW,Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical,1,,2.80,2.80,https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
P1 Séquence Sonore,SW4_P1_GREEN,Arcade Button 30mm GREEN,Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical,1,,2.80,2.80,https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
P1 Séquence Sonore,LED1_P1,WS2812B 4-LED Strip,LED_WS2812B:WS2812B_Strip,1,C2761795,1.20,1.20,https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
P1 Séquence Sonore,U2_P1,SN74HCT125N Level Shifter,Package_DIP:DIP-14_W7.62mm,1,C5616,0.25,0.25,https://www.ti.com/lit/ds/symlink/sn74hct125.pdf
P1 Séquence Sonore,U3_P1,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P1 Séquence Sonore,R1_P1,10kΩ Pull-up 0603,Resistor_SMD:R_0603_1608Metric,4,C25804,0.01,0.04,
P1 Séquence Sonore,R2_P1,470Ω LED series 0603,Resistor_SMD:R_0603_1608Metric,1,C23179,0.01,0.01,
P1 Séquence Sonore,C1_P1,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,4,C14663,0.01,0.04,
P1 Séquence Sonore,CONN1_P1,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P1 Séquence Sonore,PCB_P1,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
P2 Circuit LED,MCU_P2,ESP32-DevKitC-32E,Module_ESP32:ESP32-DevKitC-32E,1,C3406749,6.50,6.50,https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
P2 Circuit LED,SW_REED_P2,Reed Switch NC MK04,Connector:PinHeader_2.54mm_1x02,8,C90067,0.45,3.60,https://standexelectronics.com/wp-content/uploads/datasheet_reed_switch_MK04.pdf
P2 Circuit LED,LED2_P2,WS2812B 16-LED Strip,LED_WS2812B:WS2812B_Strip,1,C2761795,3.50,3.50,https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
P2 Circuit LED,U1_P2,SN74HCT125N Level Shifter,Package_DIP:DIP-14_W7.62mm,1,C5616,0.25,0.25,https://www.ti.com/lit/ds/symlink/sn74hct125.pdf
P2 Circuit LED,U2_P2,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P2 Circuit LED,BZ1_P2,Active Buzzer 5V TMB12A05,Buzzer_Beeper:BZ_KPM-G1205,1,C96093,0.15,0.15,
P2 Circuit LED,LED_STATUS_P2,LED Green 5mm 470Ω,LED_THT:LED_D5.0mm,1,C434435,0.05,0.05,
P2 Circuit LED,Q1_P2,NPN 2N2222 Transistor,Package_TO_SOT_THT:TO-92_Inline,1,C2154,0.10,0.10,https://www.onsemi.com/pdf/datasheet/p2n2222a-d.pdf
P2 Circuit LED,R1_P2,10kΩ Pull-up 0603,Resistor_SMD:R_0603_1608Metric,8,C25804,0.01,0.08,
P2 Circuit LED,R2_P2,1kΩ Base resistor 0603,Resistor_SMD:R_0603_1608Metric,1,C21190,0.01,0.01,
P2 Circuit LED,R3_P2,470Ω LED series 0603,Resistor_SMD:R_0603_1608Metric,1,C23179,0.01,0.01,
P2 Circuit LED,C1_P2,1000µF 10V Bulk Cap,Capacitor_THT:C_Radial_D8.0mm_H11.5mm_P3.50mm,1,C2915,0.25,0.25,
P2 Circuit LED,C2_P2,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,2,C14663,0.01,0.02,
P2 Circuit LED,CONN1_P2,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P2 Circuit LED,MAG_COMPONENTS,Magnetic Component Set (resistors/LEDs/wires on magnetic board),Mechanical,1,,12.00,12.00,Custom kit — laser-cut magnetic folding board
P2 Circuit LED,PCB_P2,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
P3 QR Codes,CARDS_P3,Laminated A5 QR Code Cards,Mechanical,6,,0.30,1.80,Print locally — QR generated by BOX-3 app
P4 Radio Rétro,MCU_P4,ESP32-DevKitC-32E,Module_ESP32:ESP32-DevKitC-32E,1,C3406749,6.50,6.50,https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
P4 Radio Rétro,ENC_P4,Rotary Encoder KY-040,Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm,1,C141881,0.60,0.60,https://www.alldatasheet.com/datasheet-pdf/pdf/1132088/ETC2/KY-040.html
P4 Radio Rétro,U1_P4,MAX98357AEWL+T I2S Class-D Amp,Package_SON:WLCSP-9_1.595x1.415mm_Pitch0.4mm,1,C236813,2.50,2.50,https://datasheets.maximintegrated.com/en/ds/MAX98357A-MAX98357B.pdf
P4 Radio Rétro,SP1_P4,Speaker 8ohm 2W 57mm,Connector:PinHeader_2.54mm_1x02,1,,2.80,2.80,
P4 Radio Rétro,DISP1_P4,SSD1306 OLED 128x64 I2C,Display_OLED:OLED_128x64,1,C125770,1.80,1.80,https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
P4 Radio Rétro,U2_P4,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P4 Radio Rétro,U3_P4,TP4056 Li-Ion Charger,Package_SO:MSOP-8_3x3mm_P0.65mm,1,C16581,0.12,0.12,http://www.tp4056.com/d/tp4056.pdf
P4 Radio Rétro,U4_P4,MT3608 Boost Converter,Package_TO_SOT_SMD:SOT-23-6,1,C84817,0.18,0.18,https://www.aerosemi.com/upload/MT3608_DS_1.0.pdf
P4 Radio Rétro,BAT_P4,18650 Li-Ion 3000mAh Samsung INR18650-30Q,BatteryHolder:BatteryHolder_2x18650,2,C148263,5.50,11.00,https://www.mouser.com/datasheet/2/408/Samsung_SDI_INR18650_30Q_35E-1709614.pdf
P4 Radio Rétro,R1_P4,10kΩ Pull-up 0603,Resistor_SMD:R_0603_1608Metric,3,C25804,0.01,0.03,
P4 Radio Rétro,R2_P4,4.7kΩ I2C pull-up 0603,Resistor_SMD:R_0603_1608Metric,2,C23162,0.01,0.02,
P4 Radio Rétro,R3_P4,10kΩ RC filter 0603,Resistor_SMD:R_0603_1608Metric,1,C25804,0.01,0.01,
P4 Radio Rétro,C1_P4,100nF RC filter 0603,Capacitor_SMD:C_0603_1608Metric,1,C14663,0.01,0.01,
P4 Radio Rétro,C2_P4,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,3,C14663,0.01,0.03,
P4 Radio Rétro,CONN1_P4,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P4 Radio Rétro,PCB_P4,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
P5 Morse,MCU_P5,ESP32-DevKitC-32E,Module_ESP32:ESP32-DevKitC-32E,1,C3406749,6.50,6.50,https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
P5 Morse,SW_TELEGRAPH_P5,Brass Morse Telegraph Key,Connector:PinHeader_2.54mm_1x02,1,,3.50,3.50,Custom brass/wood — Aliexpress morse key
P5 Morse,BZ1_P5,Active Buzzer 5V TMB12A05,Buzzer_Beeper:BZ_KPM-G1205,1,C96093,0.15,0.15,
P5 Morse,LED_RED_P5,LED Red 5mm,LED_THT:LED_D5.0mm,1,C434434,0.05,0.05,
P5 Morse,LED_GREEN_P5,LED Green 5mm,LED_THT:LED_D5.0mm,1,C434435,0.05,0.05,
P5 Morse,LED_WS_P5,WS2812B 1-LED (NON_TECH light mode),LED_WS2812B:WS2812B_2020,1,C2761795,0.30,0.30,https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
P5 Morse,U1_P5,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P5 Morse,Q1_P5,NPN 2N2222 Transistor,Package_TO_SOT_THT:TO-92_Inline,1,C2154,0.10,0.10,https://www.onsemi.com/pdf/datasheet/p2n2222a-d.pdf
P5 Morse,R1_P5,10kΩ Pull-up 0603,Resistor_SMD:R_0603_1608Metric,1,C25804,0.01,0.01,
P5 Morse,R2_P5,1kΩ Base resistor 0603,Resistor_SMD:R_0603_1608Metric,1,C21190,0.01,0.01,
P5 Morse,R3_P5,470Ω LED series 0603,Resistor_SMD:R_0603_1608Metric,2,C23179,0.01,0.02,
P5 Morse,C1_P5,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,2,C14663,0.01,0.02,
P5 Morse,CONN1_P5,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P5 Morse,PCB_P5,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
P6 Symboles NFC,MCU_P6,ESP32-DevKitC-32E,Module_ESP32:ESP32-DevKitC-32E,1,C3406749,6.50,6.50,https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
P6 Symboles NFC,U1_P6,MFRC522 NFC Reader Module,MFRC522:MFRC522_Module,1,C411208,1.20,1.20,https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf
P6 Symboles NFC,NFC_TAGS_P6,NTAG213 NFC Stickers (25mm round),Mechanical,12,C7499,0.18,2.16,https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf
P6 Symboles NFC,TABLET_P6,Wooden Tablet 300x200mm (laser cut),Mechanical,1,,8.00,8.00,12 slots for NFC symbol pieces — 3mm plywood
P6 Symboles NFC,SYMBOLS_P6,Symbol Pieces (laser cut plywood 40x40mm),Mechanical,12,,0.50,6.00,Alchemical symbols engraved — fit over NFC stickers
P6 Symboles NFC,BZ1_P6,Active Buzzer 5V TMB12A05,Buzzer_Beeper:BZ_KPM-G1205,1,C96093,0.15,0.15,
P6 Symboles NFC,LED_GREEN_P6,LED Green 5mm,LED_THT:LED_D5.0mm,1,C434435,0.05,0.05,
P6 Symboles NFC,LED_RED_P6,LED Red 5mm,LED_THT:LED_D5.0mm,1,C434434,0.05,0.05,
P6 Symboles NFC,U2_P6,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P6 Symboles NFC,Q1_P6,NPN 2N2222 Transistor,Package_TO_SOT_THT:TO-92_Inline,1,C2154,0.10,0.10,
P6 Symboles NFC,R1_P6,4.7kΩ SPI pull-up 0603,Resistor_SMD:R_0603_1608Metric,2,C23162,0.01,0.02,
P6 Symboles NFC,R2_P6,1kΩ Base resistor 0603,Resistor_SMD:R_0603_1608Metric,1,C21190,0.01,0.01,
P6 Symboles NFC,R3_P6,470Ω LED series 0603,Resistor_SMD:R_0603_1608Metric,2,C23179,0.01,0.02,
P6 Symboles NFC,C1_P6,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,2,C14663,0.01,0.02,
P6 Symboles NFC,CONN1_P6,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P6 Symboles NFC,PCB_P6,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
P7 Coffre Final,MCU_P7,ESP32-DevKitC-32E,Module_ESP32:ESP32-DevKitC-32E,1,C3406749,6.50,6.50,https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
P7 Coffre Final,SERVO_P7,SG90 Servo Motor,Connector:PinHeader_2.54mm_1x03,1,C72037,1.20,1.20,https://www.towerpro.com.tw/product/sg90-7/
P7 Coffre Final,KBD_P7,4x3 Membrane Keypad,Connector:PinHeader_2.54mm_1x07,1,C2935585,1.20,1.20,
P7 Coffre Final,DISP1_P7,SSD1306 OLED 128x64 I2C,Display_OLED:OLED_128x64,1,C125770,1.80,1.80,https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
P7 Coffre Final,LED_RGB_P7,WS2812B RGB LED 1x,LED_WS2812B:WS2812B_2020,1,C2761795,0.30,0.30,https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
P7 Coffre Final,U1_P7,AMS1117-3.3 LDO SOT-223,Package_TO_SOT_SMD:SOT-223-3_TabPin2,1,C6186,0.08,0.08,https://www.advanced-monolithic.com/pdf/ds1117.pdf
P7 Coffre Final,BZ1_P7,Active Buzzer 5V TMB12A05,Buzzer_Beeper:BZ_KPM-G1205,1,C96093,0.15,0.15,
P7 Coffre Final,Q1_P7,NPN 2N2222 Transistor,Package_TO_SOT_THT:TO-92_Inline,1,C2154,0.10,0.10,
P7 Coffre Final,R1_P7,4.7kΩ I2C pull-up 0603,Resistor_SMD:R_0603_1608Metric,2,C23162,0.01,0.02,
P7 Coffre Final,R2_P7,10kΩ Keypad pull-up 0603,Resistor_SMD:R_0603_1608Metric,3,C25804,0.01,0.03,
P7 Coffre Final,R3_P7,1kΩ Base resistor 0603,Resistor_SMD:R_0603_1608Metric,1,C21190,0.01,0.01,
P7 Coffre Final,C1_P7,100nF Bypass Cap 0603,Capacitor_SMD:C_0603_1608Metric,3,C14663,0.01,0.03,
P7 Coffre Final,BOX_P7,Wooden box 250x200x150mm,Mechanical,1,,12.00,12.00,Pine wood box — hardware store
P7 Coffre Final,CONN1_P7,USB-C Power Connector 2A,Connector_USB:USB_C_Receptacle_GCT_USB4085,1,C167688,0.35,0.35,https://gct.co/files/specs/usb4085-spec.pdf
P7 Coffre Final,PCB_P7,Custom PCB 100x100mm 2L JLCPCB,PCB,1,,2.00,2.00,https://jlcpcb.com
HUB,BOX3_HUB,ESP32-S3-BOX-3,Espressif Dev Kit,1,,49.00,49.00,https://www.espressif.com/en/products/devkits/esp32-s3-box-3
HUB,RTC_PHONE_HUB,RTC_BL_PHONE Custom Board (ESP32 + SLIC),Custom PCB,1,,35.00,35.00,Voice hub — ESP32 + Si3220 SLIC + RJ11 connector
HUB,ROUTER_HUB,GL.iNet GL-MT3000 WiFi 6 Router,Network,1,,69.00,69.00,https://www.gl-inet.com/products/gl-mt3000/
HUB,BAT1_HUB,Anker 20000mAh USB-C PowerBank A1287,Power,3,,25.00,75.00,https://www.anker.com/products/a1287
HUB,SPEAKER_HUB,JBL Go 3 Bluetooth Speaker,Audio,1,,29.00,29.00,https://www.jbl.com/bluetooth-speakers/JBL+GO+3.html
HUB,LED_STRIP_HUB,USB LED Strip 3m Warm White,Lighting,1,,8.00,8.00,Ambient lighting for suitcase
HUB,CABLE_USB_HUB,USB-C Cable 1m Ugreen (Qty 10),Power,10,,3.00,30.00,All puzzle power cables
HUB,SUITCASE_1,Suitcase Rimowa 57L equivalent ABS,Mechanical,1,,45.00,45.00,Main hub suitcase — rigid
HUB,SUITCASE_2,Suitcase 42L softcase,Mechanical,1,,25.00,25.00,Puzzle objects suitcase A
HUB,SUITCASE_3,Suitcase 42L softcase,Mechanical,1,,25.00,25.00,Puzzle objects suitcase B
HUB,FOAM_CUT,EVA Foam sheets 10mm (for suitcase cutouts),Mechanical,3,,6.00,18.00,Custom foam per suitcase
HUB,SIGNAGE,Printed signage A4 laminated (puzzle labels),Print,10,,0.50,5.00,Puzzle identification labels
HUB,MORSE_REF,Morse code reference card A6 laminated,Print,10,,0.20,2.00,Hint card for P5
HUB,PCB_HUB,RTC_PHONE Custom PCB JLCPCB,PCB,1,,8.00,8.00,4-layer — SLIC + ESP32 integration
,,,,,,,,
SUMMARY,Total Puzzle Electronics,,,,,,,207.00
SUMMARY,Total Hub & Infrastructure,,,,,,,390.00
SUMMARY,Total Enclosures & Mechanical,,,,,,,130.00
SUMMARY,Total Kit,,,,,,,727.00
1 Puzzle Reference Value Footprint Quantity LCSC_Part Unit_Price_EUR Total_Price_EUR Datasheet_URL
2 P1 Séquence Sonore MCU_P1 ESP32-S3-DevKitC-1-N16R8 Module_ESP32-S3:ESP32-S3-DevKitC-1 1 C2913202 12.00 12.00 https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html
3 P1 Séquence Sonore U1_P1 MAX98357AEWL+T I2S Class-D Amp Package_SON:WLCSP-9_1.595x1.415mm_Pitch0.4mm 1 C236813 2.50 2.50 https://datasheets.maximintegrated.com/en/ds/MAX98357A-MAX98357B.pdf
4 P1 Séquence Sonore SP1_P1 Speaker 8ohm 2W 57mm Connector:PinHeader_2.54mm_1x02 1 2.80 2.80 https://www.tme.eu/Document/bc34b0283d78d58b7ec55d24a1e3fde4/KPSP-0520L.pdf
5 P1 Séquence Sonore SW1_P1_RED Arcade Button 30mm RED Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical 1 2.80 2.80 https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
6 P1 Séquence Sonore SW2_P1_BLUE Arcade Button 30mm BLUE Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical 1 2.80 2.80 https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
7 P1 Séquence Sonore SW3_P1_YELLOW Arcade Button 30mm YELLOW Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical 1 2.80 2.80 https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
8 P1 Séquence Sonore SW4_P1_GREEN Arcade Button 30mm GREEN Button_Switch_THT:SW_Sanwa_OBSF-30_Vertical 1 2.80 2.80 https://www.sanwa.co.jp/product/game/pdf/obsf-30.pdf
9 P1 Séquence Sonore LED1_P1 WS2812B 4-LED Strip LED_WS2812B:WS2812B_Strip 1 C2761795 1.20 1.20 https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
10 P1 Séquence Sonore U2_P1 SN74HCT125N Level Shifter Package_DIP:DIP-14_W7.62mm 1 C5616 0.25 0.25 https://www.ti.com/lit/ds/symlink/sn74hct125.pdf
11 P1 Séquence Sonore U3_P1 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
12 P1 Séquence Sonore R1_P1 10kΩ Pull-up 0603 Resistor_SMD:R_0603_1608Metric 4 C25804 0.01 0.04
13 P1 Séquence Sonore R2_P1 470Ω LED series 0603 Resistor_SMD:R_0603_1608Metric 1 C23179 0.01 0.01
14 P1 Séquence Sonore C1_P1 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 4 C14663 0.01 0.04
15 P1 Séquence Sonore CONN1_P1 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
16 P1 Séquence Sonore PCB_P1 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
17 P2 Circuit LED MCU_P2 ESP32-DevKitC-32E Module_ESP32:ESP32-DevKitC-32E 1 C3406749 6.50 6.50 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
18 P2 Circuit LED SW_REED_P2 Reed Switch NC MK04 Connector:PinHeader_2.54mm_1x02 8 C90067 0.45 3.60 https://standexelectronics.com/wp-content/uploads/datasheet_reed_switch_MK04.pdf
19 P2 Circuit LED LED2_P2 WS2812B 16-LED Strip LED_WS2812B:WS2812B_Strip 1 C2761795 3.50 3.50 https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
20 P2 Circuit LED U1_P2 SN74HCT125N Level Shifter Package_DIP:DIP-14_W7.62mm 1 C5616 0.25 0.25 https://www.ti.com/lit/ds/symlink/sn74hct125.pdf
21 P2 Circuit LED U2_P2 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
22 P2 Circuit LED BZ1_P2 Active Buzzer 5V TMB12A05 Buzzer_Beeper:BZ_KPM-G1205 1 C96093 0.15 0.15
23 P2 Circuit LED LED_STATUS_P2 LED Green 5mm 470Ω LED_THT:LED_D5.0mm 1 C434435 0.05 0.05
24 P2 Circuit LED Q1_P2 NPN 2N2222 Transistor Package_TO_SOT_THT:TO-92_Inline 1 C2154 0.10 0.10 https://www.onsemi.com/pdf/datasheet/p2n2222a-d.pdf
25 P2 Circuit LED R1_P2 10kΩ Pull-up 0603 Resistor_SMD:R_0603_1608Metric 8 C25804 0.01 0.08
26 P2 Circuit LED R2_P2 1kΩ Base resistor 0603 Resistor_SMD:R_0603_1608Metric 1 C21190 0.01 0.01
27 P2 Circuit LED R3_P2 470Ω LED series 0603 Resistor_SMD:R_0603_1608Metric 1 C23179 0.01 0.01
28 P2 Circuit LED C1_P2 1000µF 10V Bulk Cap Capacitor_THT:C_Radial_D8.0mm_H11.5mm_P3.50mm 1 C2915 0.25 0.25
29 P2 Circuit LED C2_P2 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 2 C14663 0.01 0.02
30 P2 Circuit LED CONN1_P2 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
31 P2 Circuit LED MAG_COMPONENTS Magnetic Component Set (resistors/LEDs/wires on magnetic board) Mechanical 1 12.00 12.00 Custom kit — laser-cut magnetic folding board
32 P2 Circuit LED PCB_P2 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
33 P3 QR Codes CARDS_P3 Laminated A5 QR Code Cards Mechanical 6 0.30 1.80 Print locally — QR generated by BOX-3 app
34 P4 Radio Rétro MCU_P4 ESP32-DevKitC-32E Module_ESP32:ESP32-DevKitC-32E 1 C3406749 6.50 6.50 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
35 P4 Radio Rétro ENC_P4 Rotary Encoder KY-040 Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm 1 C141881 0.60 0.60 https://www.alldatasheet.com/datasheet-pdf/pdf/1132088/ETC2/KY-040.html
36 P4 Radio Rétro U1_P4 MAX98357AEWL+T I2S Class-D Amp Package_SON:WLCSP-9_1.595x1.415mm_Pitch0.4mm 1 C236813 2.50 2.50 https://datasheets.maximintegrated.com/en/ds/MAX98357A-MAX98357B.pdf
37 P4 Radio Rétro SP1_P4 Speaker 8ohm 2W 57mm Connector:PinHeader_2.54mm_1x02 1 2.80 2.80
38 P4 Radio Rétro DISP1_P4 SSD1306 OLED 128x64 I2C Display_OLED:OLED_128x64 1 C125770 1.80 1.80 https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
39 P4 Radio Rétro U2_P4 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
40 P4 Radio Rétro U3_P4 TP4056 Li-Ion Charger Package_SO:MSOP-8_3x3mm_P0.65mm 1 C16581 0.12 0.12 http://www.tp4056.com/d/tp4056.pdf
41 P4 Radio Rétro U4_P4 MT3608 Boost Converter Package_TO_SOT_SMD:SOT-23-6 1 C84817 0.18 0.18 https://www.aerosemi.com/upload/MT3608_DS_1.0.pdf
42 P4 Radio Rétro BAT_P4 18650 Li-Ion 3000mAh Samsung INR18650-30Q BatteryHolder:BatteryHolder_2x18650 2 C148263 5.50 11.00 https://www.mouser.com/datasheet/2/408/Samsung_SDI_INR18650_30Q_35E-1709614.pdf
43 P4 Radio Rétro R1_P4 10kΩ Pull-up 0603 Resistor_SMD:R_0603_1608Metric 3 C25804 0.01 0.03
44 P4 Radio Rétro R2_P4 4.7kΩ I2C pull-up 0603 Resistor_SMD:R_0603_1608Metric 2 C23162 0.01 0.02
45 P4 Radio Rétro R3_P4 10kΩ RC filter 0603 Resistor_SMD:R_0603_1608Metric 1 C25804 0.01 0.01
46 P4 Radio Rétro C1_P4 100nF RC filter 0603 Capacitor_SMD:C_0603_1608Metric 1 C14663 0.01 0.01
47 P4 Radio Rétro C2_P4 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 3 C14663 0.01 0.03
48 P4 Radio Rétro CONN1_P4 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
49 P4 Radio Rétro PCB_P4 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
50 P5 Morse MCU_P5 ESP32-DevKitC-32E Module_ESP32:ESP32-DevKitC-32E 1 C3406749 6.50 6.50 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
51 P5 Morse SW_TELEGRAPH_P5 Brass Morse Telegraph Key Connector:PinHeader_2.54mm_1x02 1 3.50 3.50 Custom brass/wood — Aliexpress morse key
52 P5 Morse BZ1_P5 Active Buzzer 5V TMB12A05 Buzzer_Beeper:BZ_KPM-G1205 1 C96093 0.15 0.15
53 P5 Morse LED_RED_P5 LED Red 5mm LED_THT:LED_D5.0mm 1 C434434 0.05 0.05
54 P5 Morse LED_GREEN_P5 LED Green 5mm LED_THT:LED_D5.0mm 1 C434435 0.05 0.05
55 P5 Morse LED_WS_P5 WS2812B 1-LED (NON_TECH light mode) LED_WS2812B:WS2812B_2020 1 C2761795 0.30 0.30 https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
56 P5 Morse U1_P5 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
57 P5 Morse Q1_P5 NPN 2N2222 Transistor Package_TO_SOT_THT:TO-92_Inline 1 C2154 0.10 0.10 https://www.onsemi.com/pdf/datasheet/p2n2222a-d.pdf
58 P5 Morse R1_P5 10kΩ Pull-up 0603 Resistor_SMD:R_0603_1608Metric 1 C25804 0.01 0.01
59 P5 Morse R2_P5 1kΩ Base resistor 0603 Resistor_SMD:R_0603_1608Metric 1 C21190 0.01 0.01
60 P5 Morse R3_P5 470Ω LED series 0603 Resistor_SMD:R_0603_1608Metric 2 C23179 0.01 0.02
61 P5 Morse C1_P5 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 2 C14663 0.01 0.02
62 P5 Morse CONN1_P5 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
63 P5 Morse PCB_P5 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
64 P6 Symboles NFC MCU_P6 ESP32-DevKitC-32E Module_ESP32:ESP32-DevKitC-32E 1 C3406749 6.50 6.50 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
65 P6 Symboles NFC U1_P6 MFRC522 NFC Reader Module MFRC522:MFRC522_Module 1 C411208 1.20 1.20 https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf
66 P6 Symboles NFC NFC_TAGS_P6 NTAG213 NFC Stickers (25mm round) Mechanical 12 C7499 0.18 2.16 https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf
67 P6 Symboles NFC TABLET_P6 Wooden Tablet 300x200mm (laser cut) Mechanical 1 8.00 8.00 12 slots for NFC symbol pieces — 3mm plywood
68 P6 Symboles NFC SYMBOLS_P6 Symbol Pieces (laser cut plywood 40x40mm) Mechanical 12 0.50 6.00 Alchemical symbols engraved — fit over NFC stickers
69 P6 Symboles NFC BZ1_P6 Active Buzzer 5V TMB12A05 Buzzer_Beeper:BZ_KPM-G1205 1 C96093 0.15 0.15
70 P6 Symboles NFC LED_GREEN_P6 LED Green 5mm LED_THT:LED_D5.0mm 1 C434435 0.05 0.05
71 P6 Symboles NFC LED_RED_P6 LED Red 5mm LED_THT:LED_D5.0mm 1 C434434 0.05 0.05
72 P6 Symboles NFC U2_P6 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
73 P6 Symboles NFC Q1_P6 NPN 2N2222 Transistor Package_TO_SOT_THT:TO-92_Inline 1 C2154 0.10 0.10
74 P6 Symboles NFC R1_P6 4.7kΩ SPI pull-up 0603 Resistor_SMD:R_0603_1608Metric 2 C23162 0.01 0.02
75 P6 Symboles NFC R2_P6 1kΩ Base resistor 0603 Resistor_SMD:R_0603_1608Metric 1 C21190 0.01 0.01
76 P6 Symboles NFC R3_P6 470Ω LED series 0603 Resistor_SMD:R_0603_1608Metric 2 C23179 0.01 0.02
77 P6 Symboles NFC C1_P6 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 2 C14663 0.01 0.02
78 P6 Symboles NFC CONN1_P6 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
79 P6 Symboles NFC PCB_P6 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
80 P7 Coffre Final MCU_P7 ESP32-DevKitC-32E Module_ESP32:ESP32-DevKitC-32E 1 C3406749 6.50 6.50 https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html
81 P7 Coffre Final SERVO_P7 SG90 Servo Motor Connector:PinHeader_2.54mm_1x03 1 C72037 1.20 1.20 https://www.towerpro.com.tw/product/sg90-7/
82 P7 Coffre Final KBD_P7 4x3 Membrane Keypad Connector:PinHeader_2.54mm_1x07 1 C2935585 1.20 1.20
83 P7 Coffre Final DISP1_P7 SSD1306 OLED 128x64 I2C Display_OLED:OLED_128x64 1 C125770 1.80 1.80 https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
84 P7 Coffre Final LED_RGB_P7 WS2812B RGB LED 1x LED_WS2812B:WS2812B_2020 1 C2761795 0.30 0.30 https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
85 P7 Coffre Final U1_P7 AMS1117-3.3 LDO SOT-223 Package_TO_SOT_SMD:SOT-223-3_TabPin2 1 C6186 0.08 0.08 https://www.advanced-monolithic.com/pdf/ds1117.pdf
86 P7 Coffre Final BZ1_P7 Active Buzzer 5V TMB12A05 Buzzer_Beeper:BZ_KPM-G1205 1 C96093 0.15 0.15
87 P7 Coffre Final Q1_P7 NPN 2N2222 Transistor Package_TO_SOT_THT:TO-92_Inline 1 C2154 0.10 0.10
88 P7 Coffre Final R1_P7 4.7kΩ I2C pull-up 0603 Resistor_SMD:R_0603_1608Metric 2 C23162 0.01 0.02
89 P7 Coffre Final R2_P7 10kΩ Keypad pull-up 0603 Resistor_SMD:R_0603_1608Metric 3 C25804 0.01 0.03
90 P7 Coffre Final R3_P7 1kΩ Base resistor 0603 Resistor_SMD:R_0603_1608Metric 1 C21190 0.01 0.01
91 P7 Coffre Final C1_P7 100nF Bypass Cap 0603 Capacitor_SMD:C_0603_1608Metric 3 C14663 0.01 0.03
92 P7 Coffre Final BOX_P7 Wooden box 250x200x150mm Mechanical 1 12.00 12.00 Pine wood box — hardware store
93 P7 Coffre Final CONN1_P7 USB-C Power Connector 2A Connector_USB:USB_C_Receptacle_GCT_USB4085 1 C167688 0.35 0.35 https://gct.co/files/specs/usb4085-spec.pdf
94 P7 Coffre Final PCB_P7 Custom PCB 100x100mm 2L JLCPCB PCB 1 2.00 2.00 https://jlcpcb.com
95 HUB BOX3_HUB ESP32-S3-BOX-3 Espressif Dev Kit 1 49.00 49.00 https://www.espressif.com/en/products/devkits/esp32-s3-box-3
96 HUB RTC_PHONE_HUB RTC_BL_PHONE Custom Board (ESP32 + SLIC) Custom PCB 1 35.00 35.00 Voice hub — ESP32 + Si3220 SLIC + RJ11 connector
97 HUB ROUTER_HUB GL.iNet GL-MT3000 WiFi 6 Router Network 1 69.00 69.00 https://www.gl-inet.com/products/gl-mt3000/
98 HUB BAT1_HUB Anker 20000mAh USB-C PowerBank A1287 Power 3 25.00 75.00 https://www.anker.com/products/a1287
99 HUB SPEAKER_HUB JBL Go 3 Bluetooth Speaker Audio 1 29.00 29.00 https://www.jbl.com/bluetooth-speakers/JBL+GO+3.html
100 HUB LED_STRIP_HUB USB LED Strip 3m Warm White Lighting 1 8.00 8.00 Ambient lighting for suitcase
101 HUB CABLE_USB_HUB USB-C Cable 1m Ugreen (Qty 10) Power 10 3.00 30.00 All puzzle power cables
102 HUB SUITCASE_1 Suitcase Rimowa 57L equivalent ABS Mechanical 1 45.00 45.00 Main hub suitcase — rigid
103 HUB SUITCASE_2 Suitcase 42L softcase Mechanical 1 25.00 25.00 Puzzle objects suitcase A
104 HUB SUITCASE_3 Suitcase 42L softcase Mechanical 1 25.00 25.00 Puzzle objects suitcase B
105 HUB FOAM_CUT EVA Foam sheets 10mm (for suitcase cutouts) Mechanical 3 6.00 18.00 Custom foam per suitcase
106 HUB SIGNAGE Printed signage A4 laminated (puzzle labels) Print 10 0.50 5.00 Puzzle identification labels
107 HUB MORSE_REF Morse code reference card A6 laminated Print 10 0.20 2.00 Hint card for P5
108 HUB PCB_HUB RTC_PHONE Custom PCB JLCPCB PCB 1 8.00 8.00 4-layer — SLIC + ESP32 integration
109
110 SUMMARY Total Puzzle Electronics 207.00
111 SUMMARY Total Hub & Infrastructure 390.00
112 SUMMARY Total Enclosures & Mechanical 130.00
113 SUMMARY Total Kit 727.00
+315
View File
@@ -0,0 +1,315 @@
# Guide d'Assemblage et Checklist Qualité — Kit V3
> Dernière mise à jour : 2026-04-03
> Temps estimé (premier assemblage) : 8-12 heures
> Temps estimé (assemblage répété, kit connu) : 3-4 heures
---
## Prérequis
### Outillage
- Fer à souder 50 W + étain 0,8 mm
- Multimètre (continuité + tension)
- Pistolet à colle thermique
- Tournevis Torx T8 + cruciforme PH1
- Pince coupante, pince à dénuder
- Imprimante 3D (PLA/ABS pour boîtiers)
- Découpeuse laser (optionnel — tablette P6 peut être achetée)
### Logiciels requis sur le poste d'assemblage
- PlatformIO CLI (`pip install platformio`)
- Python 3.11+
- `esptool.py` (`pip install esptool`)
- `idf.py` (ESP-IDF v5.x pour ESP32-S3-BOX-3)
---
## Étape 1 : Impression 3D des Boîtiers (2-4 h)
| Boîtier | Fichier STL | Matière | Paramètres |
|---------|------------|---------|-----------|
| P1 Boîte Sonore | `hardware/enclosure/p1_son/p1_son_box.stl` | PLA noir | 0,2 mm, 20% infill |
| P2 Support Breadboard | `hardware/enclosure/p2_circuit/p2_frame.stl` | PLA gris | 0,2 mm, 15% infill |
| P4 Poste Radio | `hardware/enclosure/p4_radio/p4_radio_retro.stl` | PLA marron | 0,2 mm, 30% infill |
| P5 Télégraphe base | `hardware/enclosure/p5_morse/p5_base.stl` | PLA bois (PHA) | 0,15 mm, 25% infill |
| P7 Coffre | `hardware/enclosure/p7_coffre/p7_coffre_body.stl` | ABS noir | 0,2 mm, 40% infill |
- [ ] Tous les boîtiers imprimés sans warping
- [ ] Trous de fixation propres (percer si nécessaire : M3 pour PCB, M2 pour servo)
- [ ] Post-traitement : ponçage 120→240 grains si besoin esthétique
---
## Étape 2 : Assemblage Électronique par Puzzle
### P1 — Boîte Sonore (Séquence musicale)
**Composants :**
- ESP32-S3 DevKit × 1
- Haut-parleur 8 Ω 1 W × 1
- MAX98357A (I2S amplificateur) × 1
- Boutons arcade 30 mm (rouge, bleu, jaune, vert) × 4
- LED 5 mm (couleurs correspondantes) × 4
- Résistances 220 Ω × 4
**Câblage :**
```
ESP32-S3 GPIO4 → BCLK (MAX98357A)
ESP32-S3 GPIO5 → LRCLK
ESP32-S3 GPIO6 → DIN
GPIO 12/13/14/15 → Boutons (INPUT_PULLUP)
GPIO 16/17/18/19 → LEDs via 220 Ω
```
- [ ] Souder MAX98357A sur mini-PCB stripboard
- [ ] Tester amplificateur seul (multimètre sur sortie : ~5V idle)
- [ ] Coller haut-parleur avec colle thermique
- [ ] Insérer boutons arcade dans les 4 trous du boîtier
- [ ] Câbler selon schéma `hardware/wiring/wiring.md`
- [ ] Test continuité toutes les connexions
### P2 — Breadboard Magnétique (Circuit LED)
**Composants :**
- ESP32 DevKit v1 × 1
- Reed switches × 8 (détection positionnement composants)
- Magnets néodyme Ø5mm × 8 (intégrés aux composants pédagogiques)
- LED 5 mm rouge × 1
- Panneau support MDF 60×40 cm (découpé laser)
**Câblage :**
```
GPIO 12-19 → Reed switches (INPUT_PULLUP)
GPIO 21 → LED témoin résolution
```
- [ ] Coller reed switches sous le panneau MDF aux positions définies
- [ ] Coller magnets dans les embases composants pédagogiques
- [ ] Câbler ESP32 et tester chaque reed switch individuellement
- [ ] Vérifier que la combinaison correcte allume LED GPIO21
### P4 — Poste Radio (Fréquence)
**Composants :**
- ESP32 DevKit v1 × 1
- Encodeur rotatif KY-040 × 1
- Écran OLED 128×64 SSD1306 I2C × 1
- 18650 Li-Ion + support + circuit de charge TP4056 × 1
**Câblage :**
```
Encodeur CLK → GPIO 32
Encodeur DT → GPIO 33
Encodeur SW → GPIO 25
SSD1306 SDA → GPIO 21
SSD1306 SCL → GPIO 22
TP4056 OUT → VIN ESP32
```
- [ ] Souder support 18650 + TP4056 sur veroboard
- [ ] Monter encodeur sur face avant boîtier rétro
- [ ] Coller OLED sur fenêtre boîtier (cadre époxy)
- [ ] Charger 18650 à 100% via USB-C avant test
### P5 — Télégraphe Morse
**Composants :**
- ESP32 DevKit v1 × 1
- Bouton télégraphe bois + laiton (artisanal ou impression 3D) × 1
- Buzzer actif 5V × 1
- LED ambre 5 mm × 1 (signal visuel mode non-tech)
**Câblage :**
```
Bouton télégraphe → GPIO 12 (INPUT_PULLUP, ACTIVE LOW)
Buzzer → GPIO 13 (via transistor NPN 2N2222)
LED ambre → GPIO 14 (via 220 Ω)
```
- [ ] Assembler mécanisme télégraphe (ressort de rappel, laiton contact)
- [ ] Tester durée de vie contact (appuyer 100× : doit être fiable)
- [ ] Régler seuil point/tiret dans firmware (défaut : 150 ms)
### P6 — Tablette Symboles Alchimiques (NFC)
**Composants :**
- ESP32 DevKit v1 × 1
- Module NFC PN532 I2C × 1
- NTAG213 (autocollant NFC) × 12
- Tablette bois découpée laser 30×20 cm × 1
- Emplacements gravés laser × 12
**Câblage :**
```
PN532 SDA → GPIO 21
PN532 SCL → GPIO 22
PN532 IRQ → GPIO 19 (interruption lecture NFC)
```
- [ ] Programmer les 12 UIDs NTAG213 (voir section NVS ci-dessous)
- [ ] Coller symboles alchimiques sur les tuiles NTAG213
- [ ] Tester lecture NFC de chaque tuile (distance max : 3 cm)
- [ ] Graver les 12 emplacements sur la tablette (laser ou Dremel)
### P7 — Coffre Final (Verrou électronique)
**Composants :**
- ESP32 DevKit v1 × 1
- Clavier matriciel 4×4 × 1
- Servo SG90 × 1 (verrou mécanique)
- OLED 128×32 SSD1306 × 1
- Buzzer passif × 1
- Coffre en bois 25×20×15 cm × 1
**Câblage :**
```
Clavier lignes → GPIO 12/13/14/15
Clavier colonnes→ GPIO 16/17/18/19
Servo signal → GPIO 23 (PWM)
OLED SDA → GPIO 21
OLED SCL → GPIO 22
Buzzer → GPIO 25
```
- [ ] Tester servo (0°=fermé, 90°=ouvert)
- [ ] Ajuster mécanisme de verrou (doit tenir sans alimentation — ressort)
- [ ] Tester clavier matriciel (toutes les 16 touches)
---
## Étape 3 : Flash Firmware par Puzzle (PlatformIO)
### Prérequis
```bash
# Cloner le repo et les submodules
git clone --recurse-submodules https://github.com/electron-rare/le-mystere-professeur-zacus.git
cd le-mystere-professeur-zacus/ESP32_ZACUS
# Vérifier que PlatformIO reconnaît les environnements
pio project config
```
### Flash par environnement
```bash
# P1 — Boîte Sonore
pio run -e p1_son --target upload --upload-port /dev/ttyUSB0
# P2 — Breadboard Magnétique
pio run -e p2_circuit --target upload --upload-port /dev/ttyUSB0
# P4 — Poste Radio
pio run -e p4_radio --target upload --upload-port /dev/ttyUSB0
# P5 — Télégraphe Morse
pio run -e p5_morse --target upload --upload-port /dev/ttyUSB0
# P6 — Tablette Symboles
pio run -e p6_symboles --target upload --upload-port /dev/ttyUSB0
# P7 — Coffre Final
pio run -e p7_coffre --target upload --upload-port /dev/ttyUSB0
# BOX-3 (ESP32-S3 — utiliser idf.py)
cd ui_freenove_allinone
idf.py -p /dev/ttyUSB0 flash monitor
```
> **Note :** sur macOS, remplacer `/dev/ttyUSB0` par `/dev/cu.usbserial-XXXX` (vérifier avec `ls /dev/cu.*`).
### Vérification post-flash
```bash
# Monitor série pour vérifier le boot
pio device monitor -e p1_son --baud 115200
# Attendu dans les logs :
# [BOOT] P1_SON v3.1 — ESP-NOW MAC: AA:BB:CC:DD:EE:FF
# [ESPNOW] Waiting for BOX-3 master...
```
- [ ] P1 : boot OK, MAC affiché, LED clignotante (attente mesh)
- [ ] P2 : boot OK, reed switches testés (log: `REED[0..7]: 00000000`)
- [ ] P4 : boot OK, OLED affiche `FREQ: 800 Hz`, encodeur réactif
- [ ] P5 : boot OK, mode détecté (`TECH` ou `NON_TECH` selon profil)
- [ ] P6 : boot OK, NFC prêt (`PN532: READY, 12 UIDs loaded`)
- [ ] P7 : boot OK, OLED `LOCKED`, clavier actif, servo en position 0°
---
## Étape 4 : Configuration NVS (Non-Volatile Storage)
Le NVS stocke les paramètres persistants de chaque puzzle : UIDs NFC, credentials WiFi, paramètres maître du jeu.
### Configuration NFC (P6)
```bash
# Programmer les UIDs des 12 tuiles NTAG213
# Créer le fichier nvs_p6.csv :
cat > /tmp/nvs_p6.csv << 'EOF'
key,type,encoding,value
nfc_uid_01,data,hex,04A1B2C3D4E500
nfc_uid_02,data,hex,04B2C3D4E5F600
# ... (12 UIDs au total)
nfc_correct_order,data,string,"7,2,11,4,9,1,8,3,12,6,10,5"
EOF
# Flash NVS partition
python3 $IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py \
generate /tmp/nvs_p6.csv /tmp/nvs_p6.bin 0x6000
esptool.py -p /dev/ttyUSB0 write_flash 0x9000 /tmp/nvs_p6.bin
```
### Configuration WiFi (tous les ESP32)
```bash
# Méthode simple : via serial monitor au premier démarrage
# Sur le terminal PlatformIO monitor, envoyer :
WIFI_SSID=ZacusVenue2026
WIFI_PASS=motdepasse_lieu
# Ou via nvs_partition_gen pour le déploiement en masse :
cat > /tmp/nvs_wifi.csv << 'EOF'
key,type,encoding,value
wifi_ssid,data,string,ZacusVenue2026
wifi_pass,data,string,motdepasse_lieu
EOF
```
### Paramètres Maître du Jeu (BOX-3)
Sur l'interface tactile BOX-3 au premier démarrage :
- `Settings → Game Master PIN` : définir un PIN à 4 chiffres (accès réglages en jeu)
- `Settings → Default Duration` : 60 min (modifiable à chaque session)
- `Settings → XTTS Host` : `kxkm-ai:5002` (ou laisser vide pour Piper/SD fallback)
- `Settings → Piper Host` : `192.168.0.120:8001`
---
## Étape 5 : Checklist Qualité Avant Première Utilisation
### Test fonctionnel complet (durée : ~30 min)
- [ ] **Allumer tout le kit** — toutes batteries connectées, attendre 60 secondes
- [ ] **Mesh ESP-NOW** — BOX-3 affiche `MESH: 6/6 nodes connected`
- [ ] **P1 Séquence Sonore** — Mélodie jouée, appuyer séquence correcte → signal OK envoyé au BOX-3
- [ ] **P2 Circuit** — Placer composants en position correcte → LED GPIO21 allumée → BOX-3 reçoit `P2_SOLVED`
- [ ] **P3 QR Treasure** — Scanner les 6 QR dans l'ordre → BOX-3 valide `P3_SOLVED`
- [ ] **P4 Radio** — Tourner à 1337 Hz → BOX-3 reçoit `P4_SOLVED`
- [ ] **P5 Morse** — Taper ZACUS en morse → BOX-3 reçoit `P5_SOLVED`
- [ ] **P6 Symboles** — Placer 12 tuiles dans l'ordre correct → BOX-3 reçoit `P6_SOLVED`
- [ ] **P7 Coffre** — Entrer le code 8 chiffres assemblé → servo tourne à 90° → coffre ouvert
- [ ] **NPC Audio** — Le Professeur Zacus parle à chaque étape clé (tester avec `./tools/dev/zacus.sh voice-bridge test`)
- [ ] **Scoring** — Fin de partie → BOX-3 affiche score et diplôme PDF généré
### Checklist esthétique et robustesse
- [ ] Aucun câble visible ou dangereux (tout cheminé + fermé)
- [ ] Boîtiers vissés ou clipsés — aucune pièce qui s'ouvre involontairement
- [ ] Toutes les LEDs et écrans visibles depuis la position joueur normale
- [ ] Texte imprimé lisible (taille min 14 pt à distance de bras)
- [ ] Logo et habillage thématique ("Laboratoire du Pr. Zacus") appliqués
- [ ] Kit de secours validé : code maître P7 fonctionne, fiche lamifiée présente
+200
View File
@@ -0,0 +1,200 @@
# 3D Enclosure Specifications — Zacus V3
All enclosures designed for FDM 3D printing unless noted. STL source files live in `hardware/enclosures/`.
OpenSCAD source: `hardware/enclosures/*.scad` (see plan Task 2.3 for parametric source).
---
## P1 — Boîte Sonore (Séquence Sonore)
**Dimensions:** 150 × 100 × 80 mm (L × W × H)
**Material:** PETG (preferred for durability) or PLA+
**Color:** Black — for stage/theatrical look; buttons are colored by their caps
### Openings & Cutouts
| Feature | Location | Size | Notes |
|------------------|------------------------|---------------|----------------------------------------------|
| Speaker grille | Front face, upper half | 40 × 40 mm | Honeycomb pattern — 2mm hex holes, 1mm walls |
| Button hole RED | Top face, x=20 | ⌀30 mm | Sanwa OBSF-30 standard mounting |
| Button hole BLUE | Top face, x=60 | ⌀30 mm | Sanwa OBSF-30 |
| Button hole YEL | Top face, x=90 | ⌀30 mm | Sanwa OBSF-30 |
| Button hole GRN | Top face, x=130 | ⌀30 mm | Sanwa OBSF-30 |
| USB-C slot | Right side, bottom | 12 × 6 mm | Power input access |
| LED strip window | Front face, lower strip| 100 × 8 mm | Diffused acrylic insert (frosted tape ok) |
| Lid vent grid | Lid top | 4×1 mm slots | 10mm grid — passive thermal |
| PCB mount studs | Interior floor | M3 standoffs | 4× M3×5mm brass inserts |
### Assembly
- Box body + removable lid (M3 screws, 4 corners)
- Speaker mounted on interior face panel with 2× M2.5 self-tappers
- MAX98357A module hot-glued behind speaker grille
- ESP32-S3 DevKit velcro-mounted to floor (removable for flashing)
- Buttons snap into top holes with Sanwa mounting nut
### Print Settings
| Parameter | Value |
|-----------------|------------------------|
| Layer height | 0.2 mm |
| Infill | 25% gyroid |
| Walls | 3 perimeters |
| Top/bottom | 4 layers |
| Supports | Yes — lid vent overhang only |
| Print time est. | ~5h (box) + 1h (lid) |
| Filament | ~180g PETG |
---
## P4 — Poste Radio Rétro (Fréquence Radio)
**Dimensions:** 200 × 150 × 120 mm (L × W × H)
**Material:** PLA (retro aesthetic — easier to sand and paint)
**Color:** Print in dark brown or beige PLA; hand-paint or vinyl wrap for retro finish
### Openings & Cutouts
| Feature | Location | Size | Notes |
|---------------------|------------------------------|-----------------|-----------------------------------------------|
| Rotary encoder hole | Front face, right side | ⌀14 mm | KY-040 shaft + nut clearance |
| Encoder knob recess | Front face, around encoder | ⌀35 mm, 5mm deep| For large Bakelite-style knob |
| OLED window | Front face, left-center | 35 × 18 mm | Acrylic window insert — glued from inside |
| Speaker grille | Front face, upper center | ⌀80 mm (circular)| 19-hole hex pattern — retro radio aesthetic |
| USB-C slot | Bottom face | 12 × 6 mm | Recessed — hidden when on table |
| Battery access door | Back face | 60 × 30 mm | Sliding door for 18650 replacement |
| Antenna cutout | Top face, right side | 8 × 60 mm slot | Decorative antenna rod (wooden dowel) |
| Corner radius | All edges | R15 mm | Rounded hull — retro form factor |
| Vent holes | Back face, bottom | 4 × 8mm slots | Battery charging ventilation |
### Assembly
- Body printed in 2 halves (top + bottom) — split at midline
- OLED glued behind acrylic window from inside
- KY-040 mounted through front panel, locked with hex nut
- Speaker front-mounted, MAX98357A behind grille
- 18650 holder slides into battery compartment from back door
- ESP32 DevKit mounted on standoffs — accessible from bottom removal
### Print Settings
| Parameter | Value |
|-----------------|--------------------------|
| Layer height | 0.2 mm (0.15 for details)|
| Infill | 20% gyroid |
| Walls | 4 perimeters |
| Top/bottom | 5 layers |
| Supports | Yes — rounded hull corners, antenna slot |
| Print time est. | ~9h (body halves) + 1h (door) |
| Filament | ~320g PLA |
| Post-process | Sand 220 grit → prime → paint (retro brown/beige) |
---
## P5 — Télégraphe (Code Morse)
**Dimensions:** 180 × 80 × 60 mm (L × W × H) — for base only; brass key mounted on top
**Material:** PETG dark brown or woodPLA for telegraph aesthetic
**Color:** Dark brown — telegraph base aesthetic
### Openings & Cutouts
| Feature | Location | Size | Notes |
|---------------------|-----------------------|---------------|-------------------------------------------------|
| Key mounting posts | Top face, right half | 2× M3 holes | Brass telegraph key bolted down |
| Buzzer grille | Front face | 20 × 20 mm | Active buzzer — 2mm hex holes |
| LED red (key press) | Front face, left | ⌀5 mm | 5mm LED holder press-fit |
| LED green (valid) | Front face, right | ⌀5 mm | 5mm LED holder press-fit |
| WS2812B window | Top face, left area | 15 × 8 mm | Diffused window for light-mode morse |
| USB-C slot | Left side, bottom | 12 × 6 mm | Power input |
| ESP32 access slot | Bottom face | 80 × 20 mm | Removable bottom panel for programming |
| Cable routing slot | Interior | 6 mm channels | Wires from key to PCB |
### Assembly
- Single-piece base with snap-on bottom panel
- Brass telegraph key (Aliexpress morse key) mounted with M3 bolts
- Key spring contact wired to GPIO4 with 2-pin JST connector
- Buzzer hot-glued behind front grille
- 5mm LED holders press-fit into front holes
- WS2812B behind frosted diffuser window on top
### Print Settings
| Parameter | Value |
|-----------------|-------------------------|
| Layer height | 0.2 mm |
| Infill | 30% gyroid (heavier base)|
| Walls | 4 perimeters |
| Top/bottom | 4 layers |
| Supports | Minimal — USB slot only |
| Print time est. | ~4h |
| Filament | ~130g PETG/woodPLA |
---
## P7 — Coffre Final (Coffre avec verrou électronique)
**Dimensions:** 250 × 200 × 150 mm (L × W × H) — wooden box, NOT 3D printed
**Material:** Pine wood box (hardware store) — ESP32 electronics in internal tray (3D printed)
### Wooden Box Modifications
| Feature | Location | Size | Notes |
|---------------------|-----------------------|-----------------|------------------------------------------------|
| Keypad cutout | Lid front face | 75 × 60 mm | 4×3 membrane keypad recess |
| OLED window | Lid front face | 35 × 18 mm | Above keypad — acrylic window |
| RGB LED hole | Lid front face | ⌀8 mm | WS2812B status indicator |
| Servo mount | Inside lid | 30 × 20 mm slot | SG90 servo + latch mechanism |
| Latch slot | Front face (lid edge) | 15 × 8 mm | Steel latch bar — closes box electromagnetically|
| USB-C slot | Back face | 12 × 6 mm | Hidden — covered by rubber flap |
| Electronics tray | Inside base | 220 × 170 × 40 | 3D printed PETG tray — holds ESP32, PCB, servo |
| Cable grommet | Tray → lid | ⌀12 mm | Spiral cable wrap — lid opening clearance |
### 3D Printed Electronics Tray (Internal)
**Dimensions:** 220 × 170 × 40 mm
**Material:** PETG
**Features:**
- ESP32 DevKit mount (4× M3 standoffs)
- Servo horn slot and cable routing
- Buzzer pocket (press-fit)
- PCB mount (2× M3)
- Lid hinge clearance notch
### Print Settings (Electronics Tray)
| Parameter | Value |
|-----------------|--------------------|
| Layer height | 0.25 mm |
| Infill | 20% grid |
| Walls | 3 perimeters |
| Top/bottom | 3 layers |
| Supports | None |
| Print time est. | ~3h |
| Filament | ~90g PETG |
### Servo Latch Mechanism
- SG90 at 0° = latch bar extended (locked)
- SG90 at 90° = latch bar retracted (open)
- Latch bar: 3D printed PETG bar 60×8×5mm
- Spring-loaded return (2mm steel wire spring)
- PWM: 50Hz, duty 1ms=closed, 2ms=open
---
## P2, P3, P6 — No Custom Enclosure Needed
| Puzzle | Housing |
|--------|-----------------------------------------------------|
| P2 | Magnetic folding board (self-contained, no enclosure)|
| P3 | Cards only — no enclosure |
| P6 | Wooden tablet is the housing (laser cut 3mm plywood)|
---
## Print Summary
| Puzzle | Volume | Material | Print Time | Filament | Est. Cost |
|--------|----------|----------|------------|----------|-----------|
| P1 | 150×100×80 | PETG | ~6h | ~180g | ~5€ |
| P4 | 200×150×120| PLA | ~10h | ~320g | ~9€ |
| P5 | 180×80×60 | PETG | ~4h | ~130g | ~4€ |
| P7 tray| 220×170×40 | PETG | ~3h | ~90g | ~3€ |
| **Total** | | | **~23h** | **~720g**| **~21€** |
Filament cost based on ~0.03€/g PETG bulk spool.
Wooden box for P7: ~12€ from hardware store.
+347
View File
@@ -0,0 +1,347 @@
# GPIO Mapping — Zacus V3 Puzzle Boards
All puzzle nodes run ESP-NOW slave firmware. Pinout must match `ESP32_ZACUS/puzzles/common/espnow_slave.h`.
Master MAC address is hard-coded per production run and set via `espnow_slave_init()` at boot.
---
## P1 — Séquence Sonore (ESP32-S3-DevKitC-1-N16R8)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|-------------------|-----------|------------------------------------------------|
| 4 | I2S BCLK | OUT | MAX98357A bit clock |
| 5 | I2S LRCLK (WS) | OUT | MAX98357A left/right clock |
| 6 | I2S DIN | OUT | MAX98357A data in |
| 7 | MAX98357A SD_MODE | OUT | HIGH = stereo / LOW = shutdown |
| 8 | WS2812B DIN | OUT | 4-LED strip via SN74HCT125 level shifter |
| 15 | Button RED | IN_PULLUP | Active LOW — 10kΩ external pull-up |
| 16 | Button BLUE | IN_PULLUP | Active LOW — 10kΩ external pull-up |
| 17 | Button YELLOW | IN_PULLUP | Active LOW — 10kΩ external pull-up |
| 18 | Button GREEN | IN_PULLUP | Active LOW — 10kΩ external pull-up |
### Power Rails
- 5V from USB-C → AMS1117-3.3 → ESP32 3V3 rail
- 5V direct → MAX98357A VDD and WS2812B VCC
### Wiring Diagram
```
USB-C 5V ─────┬──────────────────────────── MAX98357A VDD
│ ┌── WS2812B VCC
└── AMS1117-3.3 ── 3V3 ──┤── ESP32 3V3
└── SN74HCT125 VCC
ESP32-S3
GPIO4 ──────────────────────────────── MAX98357A BCLK
GPIO5 ──────────────────────────────── MAX98357A LRC
GPIO6 ──────────────────────────────── MAX98357A DIN
GPIO7 ──── 3V3 (tied HIGH) ─────────── MAX98357A SD_MODE
GPIO8 ──── SN74HCT125 A1 ── Y1 ─────── WS2812B DIN (4 LEDs)
GPIO15 ──┬── 10kΩ ── 3V3
└────────────────────────────── Arcade Button RED (NO → GND)
GPIO16 ──┬── 10kΩ ── 3V3
└────────────────────────────── Arcade Button BLUE (NO → GND)
GPIO17 ──┬── 10kΩ ── 3V3
└────────────────────────────── Arcade Button YELLOW (NO → GND)
GPIO18 ──┬── 10kΩ ── 3V3
└────────────────────────────── Arcade Button GREEN (NO → GND)
MAX98357A OUTP/OUTN ─────────────────── Speaker 8Ω 2W
```
---
## P2 — Circuit LED (ESP32-DevKitC-32E)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|----------------------|-----------|----------------------------------------------|
| 4 | Reed Switch 1 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 5 | Reed Switch 2 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 6 | Reed Switch 3 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 7 | Reed Switch 4 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 8 | Reed Switch 5 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 9 | Reed Switch 6 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 10 | Reed Switch 7 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 11 | Reed Switch 8 | IN_PULLUP | Magnetic sensor — 10kΩ external pull-up |
| 12 | WS2812B DIN Seg A | OUT | LED segment 12 via 74HCT125 |
| 13 | WS2812B DIN Seg B | OUT | LED segment 34 via 74HCT125 |
| 14 | WS2812B DIN Seg C | OUT | LED segment 56 via 74HCT125 |
| 15 | WS2812B DIN Seg D | OUT | LED segment 78 via 74HCT125 |
| 16 | WS2812B DIN Seg E | OUT | LED segment 910 via 74HCT125 |
| 17 | WS2812B DIN Seg F | OUT | LED segment 1112 via 74HCT125 |
| 18 | WS2812B DIN Seg G | OUT | LED segment 1314 via 74HCT125 |
| 19 | WS2812B DIN Seg H | OUT | LED segment 1516 via 74HCT125 |
| 21 | Buzzer | OUT | NPN 2N2222 base via 1kΩ → active buzzer 5V |
| 22 | Circuit Valid LED | OUT | Green LED via 470Ω series resistor |
### Wiring Diagram
```
USB-C 5V ────┬───────────────────────────── WS2812B VCC (each segment)
│ ┌── 1000µF bulk cap (5V decoupling)
└── AMS1117-3.3 ── 3V3 ──── ESP32 3V3
ESP32
GPIO4..11 ─── (each) ──┬── 10kΩ ── 3V3
└───────────────── Reed Switch (NO → GND)
GPIO12..19 ─── SN74HCT125 Ax ── Yx ────── WS2812B DINx (2 LEDs each)
GPIO21 ──── 1kΩ ──── Q1(2N2222) base
Q1 collector ─ Buzzer+ (5V)
Q1 emitter ─ GND
Buzzer- ─ GND
GPIO22 ──── 470Ω ──── LED Green+ ──── GND
Magnetic board (folding 60x40cm):
Reed switches are embedded in the board surface
Magnetic component pegs activate the reed switches when placed
Board connector: 10-pin ribbon → ESP32 GPIO4..11 + 2x GND
```
---
## P3 — QR Treasure (No dedicated electronics)
P3 uses the BOX-3 camera directly for QR scanning. No separate ESP32 board.
| Component | Notes |
|-----------|-------|
| 6× A5 laminated QR cards | Printed + laminated — QR content set at scenario compile time |
| BOX-3 camera | ESP32-S3-BOX-3 integrated camera — handles scanning |
QR card placement order encoded in `game/scenarios/zacus_v3_complete.yaml` under `P3_QR.correct_order`.
---
## P4 — Fréquence Radio (ESP32-DevKitC-32E)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|--------------------|-----------|--------------------------------------------------|
| 4 | I2S BCLK | OUT | MAX98357A — radio static synthesis |
| 5 | I2S LRCLK (WS) | OUT | MAX98357A |
| 6 | I2S DIN | OUT | MAX98357A data |
| 12 | Rotary Encoder DT | IN_PULLUP | KY-040 — direction detect |
| 13 | Rotary Encoder SW | IN_PULLUP | KY-040 push button — confirm selection |
| 14 | Rotary Encoder CLK | IN_PULLUP | KY-040 — clock signal |
| 21 | I2C SDA (OLED) | BIDIR | SSD1306 128x64 — 4.7kΩ pull-up to 3V3 |
| 22 | I2C SCL (OLED) | OUT | SSD1306 — 4.7kΩ pull-up to 3V3 |
| 25 | DAC Output | OUT | RC low-pass → analog indicator LED (warm/cold) |
### Power Rails
- 2× 18650 (2S) → TP4056 charger → MT3608 boost 5V → AMS1117-3.3 → 3V3
- Alternatively: USB-C 5V input for bench/testing
### Wiring Diagram
```
2x 18650 (2S = 7.4V nom) ── TP4056 ── MT3608 boost ── 5V rail
5V rail ─────┬───────────────────────── MAX98357A VDD
└── AMS1117-3.3 ── 3V3 ── ESP32 3V3
ESP32
GPIO4 ──────────────────────────── MAX98357A BCLK
GPIO5 ──────────────────────────── MAX98357A LRC
GPIO6 ──────────────────────────── MAX98357A DIN
MAX98357A OUTP/OUTN ─────────────── Speaker 8Ω 2W (in retro grille)
GPIO14 ──┬── 10kΩ ── 3V3
└───────────────────────── KY-040 CLK
GPIO12 ──┬── 10kΩ ── 3V3
└───────────────────────── KY-040 DT
GPIO13 ──┬── 10kΩ ── 3V3
└───────────────────────── KY-040 SW (GND when pressed)
GPIO21 ──┬── 4.7kΩ ── 3V3
└───────────────────────── SSD1306 SDA
GPIO22 ──┬── 4.7kΩ ── 3V3
└───────────────────────── SSD1306 SCL
GPIO25 ── 10kΩ ──┬── 100nF ── GND (RC low-pass filter)
└────────────────── LED warm/cold indicator anode
LED cathode ── 470Ω ── GND
```
---
## P5 — Code Morse (ESP32-DevKitC-32E)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|----------------------|-----------|------------------------------------------------|
| 4 | Telegraph Key | IN_PULLUP | Brass key NO contact — 10kΩ external pull-up |
| 5 | Buzzer | OUT | NPN 2N2222 base via 1kΩ — active buzzer 5V |
| 6 | LED Red (key press) | OUT | Key pressed indicator — 470Ω series |
| 7 | LED Green (validated)| OUT | Morse message valid — 470Ω series |
| 8 | WS2812B (light mode) | OUT | 1 LED — NON_TECH visual morse pulses |
### Wiring Diagram
```
USB-C 5V ────── AMS1117-3.3 ────── 3V3 ── ESP32 3V3
ESP32
GPIO4 ──┬── 10kΩ ── 3V3
└──────────────────────── Telegraph Key+ (brass contact)
Telegraph Key- ───── GND
GPIO5 ──── 1kΩ ──── Q1(2N2222) base
Q1 collector ── Buzzer+ ── 5V
Q1 emitter ── GND
Buzzer- ── GND
GPIO6 ──── 470Ω ──── LED Red anode ──── GND
GPIO7 ──── 470Ω ──── LED Green anode ── GND
GPIO8 ──────────────────────────── WS2812B DIN (1 LED, 5V)
[3V3 → 5V: use SN74HCT125 or direct (WS2812B tolerates 3V3 DIN)]
```
---
## P6 — Symboles Alchimiques NFC (ESP32-DevKitC-32E)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|-------------------|-----------|-----------------------------------------------|
| 4 | MFRC522 RST | OUT | NFC module reset |
| 5 | MFRC522 SDA (SS) | OUT | SPI chip select |
| 18 | MFRC522 SCK | OUT | SPI clock |
| 19 | MFRC522 MISO | IN | SPI data from MFRC522 |
| 23 | MFRC522 MOSI | OUT | SPI data to MFRC522 |
| 25 | Buzzer | OUT | NPN 2N2222 base via 1kΩ |
| 26 | LED Green (valid) | OUT | Configuration valid — 470Ω |
| 27 | LED Red (wrong) | OUT | Wrong placement — 470Ω |
### Wiring Diagram
```
USB-C 5V ───── AMS1117-3.3 ───── 3V3 ── ESP32 3V3
ESP32
GPIO5 (SDA/SS) ────────────── MFRC522 SDA
GPIO18 (SCK) ────────────── MFRC522 SCK
GPIO19 (MISO) ────────────── MFRC522 MISO
GPIO23 (MOSI) ────────────── MFRC522 MOSI
GPIO4 (RST) ────────────── MFRC522 RST
3V3 ────────────── MFRC522 3.3V
GND ────────────── MFRC522 GND
[MFRC522 IRQ pin: not connected — polling mode]
GPIO25 ── 1kΩ ── Q1(2N2222) base
Q1 collector ── Buzzer+ ── 5V
Q1 emitter ── GND
GPIO26 ── 470Ω ── LED Green ── GND
GPIO27 ── 470Ω ── LED Red ── GND
Wooden tablet (300x200mm, 3mm plywood):
12 recessed slots, each slot has an NTAG213 sticker wired via flat cable
to a multiplexed antenna array — OR single MFRC522 scans one tag at a time
(player places one piece → holds it → scans → places next)
Correct order: [7,2,11,4,9,1,8,3,12,6,10,5] (from zacus_v3_complete.yaml)
```
---
## P7 — Coffre Final (ESP32-DevKitC-32E)
### GPIO Table
| GPIO | Function | Direction | Notes |
|------|----------------------|-----------|------------------------------------------------|
| 4 | Keypad Row 1 | OUT | Matrix scan — driven LOW one at a time |
| 5 | WS2812B RGB LED | OUT | Status LED — idle/entering/error/open |
| 6 | Keypad Row 2 | OUT | Matrix scan |
| 7 | Keypad Row 3 | OUT | Matrix scan |
| 8 | Keypad Row 4 | OUT | Matrix scan |
| 12 | Keypad Col 1 | IN_PULLUP | 10kΩ pull-up — reads HIGH unless row driven |
| 13 | Keypad Col 2 | IN_PULLUP | 10kΩ pull-up |
| 14 | Keypad Col 3 | IN_PULLUP | 10kΩ pull-up |
| 18 | Buzzer | OUT | NPN 2N2222 base via 1kΩ |
| 21 | I2C SDA (OLED) | BIDIR | SSD1306 128x64 — 4.7kΩ pull-up |
| 22 | I2C SCL (OLED) | OUT | SSD1306 — 4.7kΩ pull-up |
| 25 | SG90 Servo PWM | OUT | 50Hz PWM — 1ms=closed, 2ms=open |
### Wiring Diagram
```
USB-C 5V ────┬─────────────────────────── SG90 Servo VCC (5V direct)
│ ┌── WS2812B VCC
└── AMS1117-3.3 ── 3V3 ── ESP32 3V3
ESP32
GPIO4,6,7,8 ──────────────────────── Keypad Rows R1..R4
(each scanned LOW sequentially, others HIGH-Z)
GPIO12 ──┬── 10kΩ ── 3V3 ────────── Keypad Col C1
GPIO13 ──┬── 10kΩ ── 3V3 ────────── Keypad Col C2
GPIO14 ──┬── 10kΩ ── 3V3 ────────── Keypad Col C3
GPIO21 ──┬── 4.7kΩ ── 3V3 ─────── SSD1306 SDA
GPIO22 ──┬── 4.7kΩ ── 3V3 ─────── SSD1306 SCL
GPIO25 ─────────────────────────── SG90 Signal (orange wire)
SG90 Red ── 5V
SG90 Brown ── GND
GPIO5 ─────────────────────────── WS2812B DIN (1 LED)
GPIO18 ── 1kΩ ── Q1(2N2222) base
Q1 collector ── Buzzer+ ── 5V
Q1 emitter ── GND
Keypad 4x3 matrix layout:
[1][2][3]
[4][5][6]
[7][8][9]
[*][0][#]
Rows: R1=top, R4=bottom
Cols: C1=left, C3=right
```
---
## Hub — BOX-3 + RTC_PHONE
### BOX-3 (ESP32-S3-BOX-3)
All BOX-3 GPIO is managed by Espressif BSP. Key internal connections:
| Resource | Notes |
|------------------|----------------------------------------------------|
| ESP-NOW Master | Coordinates all P1..P7 + RTC_PHONE via 2.4GHz |
| Camera (GC0308) | QR code scanning for P3 |
| Display (ILI9342)| Game master dashboard, scoring, puzzle status |
| I2S Speaker | ES8311 audio codec — NPC voice output |
| Mute button | GPIO0 — NPC voice mute toggle |
| USB-C | Programming + 5V power from Anker powerbank |
### RTC_PHONE (Custom Board)
| GPIO/Pin | Function | Notes |
|----------|--------------------|------------------------------------------|
| GPIO4 | I2S BCLK → SLIC | Si3220 voice codec clock |
| GPIO5 | I2S LRCLK → SLIC | Si3220 left/right clock |
| GPIO6 | I2S DIN → SLIC | Audio data to SLIC (NPC voice) |
| GPIO7 | I2S DOUT ← SLIC | Audio from handset (player speech) |
| GPIO21 | I2C SDA → Si3220 | SLIC configuration |
| GPIO22 | I2C SCL → Si3220 | SLIC clock |
| GPIO18 | Hook switch detect | HIGH = handset lifted (interrupt) |
| RJ11 | POTS line out | Standard telephone handset connector |
| USB-C | 5V power | From Anker powerbank |
+216
View File
@@ -0,0 +1,216 @@
# Guide de Maintenance — Kit V3
> Dernière mise à jour : 2026-04-03
> Fréquence : après chaque session (vérifications rapides) + annuelle (maintenance complète)
---
## Dépannage des Problèmes Courants
### Mesh ESP-NOW — Nœuds manquants
**Symptôme :** BOX-3 affiche `MESH: X/6 nodes connected` avec X < 6.
| Cause probable | Diagnostic | Solution |
|----------------|-----------|---------|
| ESP32 non alimenté | LED d'alimentation éteinte sur le puzzle | Vérifier câble USB-C + batterie |
| ESP32 bloqué au boot | LED clignote rapidement sans s'arrêter | Appuyer bouton Reset (EN) sur le DevKit |
| Hors de portée ESP-NOW (~200 m max) | Puzzle à plus de 20 m de BOX-3 | Rapprocher le puzzle ou repositionner |
| Firmware corrompu | Log série : `Guru Meditation Error` | Re-flasher le firmware (voir section Update) |
| Conflit MAC address | Log série : `ESPNOW: peer already exists` | Effacer NVS et redémarrer : `esptool.py erase_flash` |
**Commande de diagnostic rapide :**
```bash
# Afficher l'état du mesh depuis BOX-3 via serial
pio device monitor -e box3 --baud 115200
# Puis dans le menu tactile BOX-3 : Debug → ESP-NOW Status
```
---
### P6 Tablette Symboles NFC — Tuile non détectée
**Symptôme :** Une tuile NFC posée sur son emplacement n'est pas reconnue.
| Cause | Diagnostic | Solution |
|-------|-----------|---------|
| Autocollant NFC endommagé | Tester la tuile avec un smartphone NFC | Remplacer l'autocollant NTAG213 (stock de rechange) |
| Distance trop grande | Positionner la tuile directement sur l'encoche | Vérifier que le PN532 est bien collé sous la surface |
| PN532 déconnecté | Log série : `PN532: INIT FAILED` | Vérifier câbles I2C SDA/SCL, re-souder si nécessaire |
| UID non enregistré en NVS | Log série : `UNKNOWN UID: XXXXXXXX` | Re-programmer le NVS avec les UIDs corrects |
**Programmer un autocollant NTAG213 de remplacement :**
```bash
# Avec un smartphone Android + app NFC Tools
# OU avec le reader PN532 + script Python :
python3 tools/dev/nfc_program.py --uid-slot 7 --new-uid 04A1B2C3D4E500
```
---
### P5 Télégraphe — Morse non détecté
**Symptôme :** Appuyer sur le télégraphe ne produit aucun effet.
| Cause | Solution |
|-------|---------|
| Contact oxydé (laiton) | Nettoyer les contacts avec papier abrasif 400 grain |
| Ressort de rappel trop faible | Remplacer le ressort (diamètre 5 mm, force 0,3 N) |
| Seuil point/tiret mal calibré | Ajuster `MORSE_DOT_MAX_MS` dans `firmware/p5_morse/config.h` |
| GPIO en pull-up cassé | Mesurer tension GPIO au repos (doit être 3,3 V) |
---
### P7 Coffre — Servo bloqué ou ne tourne pas
**Symptôme :** Code correct entré mais le servo ne bouge pas (coffre reste fermé).
| Cause | Solution |
|-------|---------|
| Servo desserré mécaniquement | Vérifier vis de fixation bras de servo, re-coller si nécessaire |
| Surcharge mécanique | Vérifier que le verrou n'est pas physiquement bloqué (dégager manuellement) |
| Signal PWM incorrect | Mesurer GPIO 23 avec oscilloscope : 50 Hz, pulse 1-2 ms |
| Servo défaillant | Remplacer le SG90 (prévoir 2 de rechange dans la valise) |
**Ouverture d'urgence (sans électronique) :**
Utiliser la clé Allen M2 fournie dans la poche valise 3 pour retirer le bras de servo manuellement.
---
### BOX-3 — Écran noir au démarrage
**Diagnostic :**
```bash
# Vérifier le firmware via serial
idf.py -p /dev/cu.usbserial-XXXX monitor
# Log attendu : [BOOT] BOX3 v3.1 — Display init...
```
| Cause | Solution |
|-------|---------|
| Firmware absent | Re-flasher : `idf.py -p /dev/cu.usbserial-XXXX flash` |
| Batterie trop faible | Connecter au chargeur 5V/2A minimum avant démarrage |
| Écran LCD déconnecté | Ouvrir le BOX-3 (4 vis Torx T8), vérifier nappe LCD |
---
### Voix NPC silencieuse
**Diagnostic par ordre de priorité :**
1. **Vérifier la connectivité WiFi** — BOX-3 : `Settings → WiFi Status`
2. **Tester Piper TTS directement :**
```bash
curl -X POST http://192.168.0.120:8001/api/tts \
-H "Content-Type: application/json" \
-d '{"text": "Test professeur Zacus", "voice": "tom-medium"}' \
--output /tmp/test.wav && aplay /tmp/test.wav
```
3. **Mode offline** — Vérifier que la SD card est présente dans BOX-3 et contient le pool MP3 :
```bash
ls /sdcard/hotline_tts/ | wc -l # attendu : 50+ fichiers
```
4. **Régénérer le pool MP3 si absent :**
```bash
python3 tools/tts/generate_npc_pool.py --dry-run # vérifier sans générer
python3 tools/tts/generate_npc_pool.py # générer
```
---
## Procédure de Mise à Jour Firmware
### Option 1 : Mise à jour OTA (Over-The-Air) — recommandée
```bash
# Prérequis : BOX-3 et tous les puzzles sur le même réseau WiFi
# Build le firmware mis à jour
cd ESP32_ZACUS
pio run -e p1_son
# Upload OTA (l'ESP32 doit être en mode OTA_READY)
# Depuis BOX-3 : Maintenance → OTA Update → Enable OTA on all nodes
pio run -e p1_son --target upload --upload-protocol espota \
--upload-port 192.168.1.XXX # IP de P1 affichée sur BOX-3
```
### Option 2 : Mise à jour USB-C (physique)
```bash
# Connecter le puzzle en USB-C au poste de développement
pio run -e p1_son --target upload --upload-port /dev/cu.usbserial-XXXX
# BOX-3 (ESP32-S3)
idf.py -p /dev/cu.usbserial-XXXX flash
```
### Vérification post-mise à jour
```bash
# Vérifier la version firmware
pio device monitor -e p1_son --baud 115200
# Attendu : [BOOT] P1_SON v3.X — ...
```
---
## Guide de Remplacement des Composants
### Composants consommables (à stocker en réserve)
| Composant | Quantité recommandée | Fournisseur | Prix unitaire |
|-----------|---------------------|-------------|--------------|
| NTAG213 autocollant NFC | 5 | AliExpress / Reichelt | ~0,30 € |
| Servo SG90 | 2 | AliExpress | ~2 € |
| ESP32 DevKit v1 | 1 | AliExpress / Mouser | ~5 € |
| 18650 Li-Ion (P4) | 1 | NKON / Reichelt | ~4 € |
| Bouton arcade 30 mm | 4 (jeu complet P1) | Aliexpress | ~1 € / pièce |
| Câble USB-C 1 m | 3 | n'importe | ~3 € |
| Ressort télégraphe (P5) | 3 | Bricomarché / quincaillerie | ~0,50 € |
### Remplacement d'un ESP32 DevKit
1. Déconnecter l'alimentation et tous les câbles.
2. Dessouder ou déconnecter le connecteur JST si présent.
3. Flasher le nouveau DevKit **avant** de l'installer (voir Étape 3 de l'assemblage).
4. Re-programmer le NVS avec les UIDs et credentials du puzzle remplacé.
5. Reconnecter et tester le mesh ESP-NOW.
### Remplacement d'une tuile NFC (P6)
1. Retirer l'autocollant NTAG213 de la tuile symbole (avec lame de rasoir, chauffer légèrement).
2. Programmer le nouvel autocollant avec le même UID (script `tools/dev/nfc_program.py`).
3. Coller le nouvel autocollant et vérifier la lecture (log : `NFC READ: UID match slot X`).
---
## Calendrier de Maintenance Annuelle
### Avant la saison (1× par an, avant les premières sessions)
| Tâche | Durée | Priorité |
|-------|-------|---------|
| Test complet de tous les puzzles (protocole étape 5) | 30 min | P0 |
| Mise à jour firmware tous les ESP32 et BOX-3 | 45 min | P0 |
| Vérification et remplacement des batteries dégradées | 15 min | P0 |
| Nettoyage contacts électroniques (spray contact) | 15 min | P1 |
| Vérification intégrité boîtiers (fissures, vis desserrées) | 10 min | P1 |
| Régénération pool MP3 NPC (nouvelles phrases si besoin) | 20 min | P1 |
| Vérification QR codes A5 (décoloration, plastification intacte) | 5 min | P2 |
| Test servo P7 (1000 cycles ouvrir/fermer) | 10 min | P2 |
| Mise à jour documentation si modifications hardware | — | P2 |
### Après 50 sessions (compteur dans BOX-3)
- Remplacer les 4 boutons arcade de P1 (usure mécanique prévisible).
- Remplacer le servo SG90 de P7 (durée de vie : ~3000-5000 cycles).
- Vérifier les contacts du télégraphe P5 (re-polir si nécessaire).
- Inspecter le câblage interne de P6 (vibrations de transport).
### Log de maintenance
Tenir à jour le fichier `hardware/maintenance_log.csv` avec :
```
date,session_count,technician,actions_performed,components_replaced,notes
```
+115
View File
@@ -0,0 +1,115 @@
# Gestion de l'Alimentation — Kit V3
> Dernière mise à jour : 2026-04-03
> Concerne : 3 valises, 7 puzzles ESP32, RTC_PHONE, BOX-3, routeur GL.iNet
---
## Assignation des Batteries
| Batterie | Capacité | Appareils alimentés | Puissance totale | Autonomie estimée |
|----------|----------|---------------------|-----------------|-------------------|
| **Anker #1** (valise Hub) | 20 000 mAh / 5V | RTC_PHONE (0,5 W) + BOX-3 (2 W) + Routeur GL.iNet (3 W) | 5,5 W | ~3,5 h |
| **Anker #2** (valise Hub, réserve) | 20 000 mAh / 5V | Chargeur USB multi-port puzzles (en session) ou secours Hub | — | — |
| **Pack Puzzle A** | 10 000 mAh / 5V | P1 Boîte Sonore (1 W) + P2 Breadboard (2 W) + P4 Radio (interne 18650) | 3 W | ~8 h |
| **Pack Puzzle B** | 10 000 mAh / 5V | P5 Télégraphe (0,5 W) + P6 Tablette Symboles (0,5 W) + P7 Coffre Final (1 W) | 2 W | ~15 h |
> **Note :** P3 QR Treasure utilise la caméra intégrée de BOX-3 (déjà compté dans Anker #1).
> P4 intègre un 18650 rechargeable en interne — chargé via USB-C indépendamment.
---
## Autonomie par Appareil
| Appareil | Puissance moy. | Batterie | Autonomie calculée | Marge pour 2 sessions |
|----------|---------------|----------|--------------------|-----------------------|
| RTC_PHONE | 0,5 W | Anker #1 | >10 h | Oui |
| BOX-3 (ESP32-S3-BOX-3) | 2 W | Anker #1 | ~5 h | Non (recharge entre sessions) |
| Routeur GL.iNet GL-MT3000 | 3 W | Anker #1 | ~3,5 h | Non (1 session) |
| P1 Boîte Sonore | 1 W | Pack A | ~12 h | Oui |
| P2 Breadboard Magnétique | 2 W (pics) | Pack A | ~6 h | Oui |
| P4 Poste Radio | 18650 interne | — | ~4 h | Vérifier avant chaque session |
| P5 Télégraphe | 0,5 W | Pack B | >20 h | Oui |
| P6 Tablette Symboles | 0,5 W | Pack B | >20 h | Oui |
| P7 Coffre Final | 1 W (+ servo) | Pack B | ~10 h | Oui |
---
## Procédure de Recharge entre Sessions
### Séquence optimale (chargeur GaN 65 W, 4 ports)
```
Port 1 (PD 45W) → Anker #1 (20 000 mAh) — Temps : ~2h00
Port 2 (PD 18W) → Anker #2 (20 000 mAh) — Temps : ~3h30
Port 3 (PD 18W) → Pack Puzzle A (10 000 mAh) — Temps : ~1h30
Port 4 (5W) → P4 Radio via USB-C interne — Temps : ~1h30
```
> Commencer immédiatement après la session pour avoir tout chargé avant la session suivante.
### Règles de gestion
1. **Ne jamais stocker une batterie déchargée** — endommageur des cellules Li-Ion.
2. **Recharger à 100% avant chaque déploiement**, pas seulement à "suffisant".
3. **Vérifier le niveau avant d'emballer** — toutes les LEDs de niveau doivent montrer 4/4.
4. **Pack B peut alimenter 2 sessions consécutives** sans recharge intermédiaire (autonomie 15 h+).
5. **Anker #1 nécessite une recharge entre sessions** si deux sessions le même jour.
### Matériel de recharge (à emporter dans la valise 1)
- [ ] Chargeur GaN 65 W, 4 ports USB-C (Anker 737 ou équivalent)
- [ ] 5× câbles USB-C courts (0,5 m) pour la recharge simultanée
- [ ] Étiquettes indiquant le port assigné à chaque batterie
---
## Alimentation d'Urgence (batterie morte en cours de partie)
### Procédure selon l'appareil défaillant
#### Hub mort (Anker #1 vide) — impact critique
**Symptômes :** BOX-3 éteint, RTC_PHONE silencieux, routeur éteint.
**Action immédiate :**
1. Brancher Anker #2 (réserve) sur le splitter Hub.
2. Temps de redémarrage : ~30 secondes.
3. Annoncer aux joueurs (en voix de maître du jeu) : *"Le Professeur a eu un petit problème technique…"*
4. Sur BOX-3 : `Settings → Restore Session` — reprend là où le jeu s'est arrêté.
#### Puzzle mort (Pack A ou B vide) — impact partiel
**Symptômes :** Un ou plusieurs puzzles ESP32 ne répondent plus sur le mesh.
**Action :**
1. Connecter ce puzzle sur le chargeur GaN (Port 3 ou 4) — le puzzle redémarre en quelques secondes.
2. Sur BOX-3 : le nœud manquant réapparaît dans `MESH: X/6 nodes`.
3. Si un nœud ne revient pas après 2 min : `Reset Hardware` sur BOX-3 → envoie une commande de reset ESP-NOW à tous les nœuds.
#### P4 Radio (18650 interne) mort
**Action :** Brancher USB-C sur n'importe quel port disponible pendant le jeu — charge et fonctionne simultanément.
#### Cas extrême : panne totale irréparable
1. Passer en mode "maître du jeu manuel" : lire les énigmes depuis le kit papier (valise 3).
2. Simuler les réponses du Professeur avec le kit audio de secours (téléphone + playlist MP3).
3. Utiliser le code de secours maître (imprimé sur la fiche lamifiée valise 1) pour ouvrir P7 manuellement.
---
## Mode Hors-Ligne (sans WiFi/internet)
Tous les puzzles fonctionnent **entièrement sans WiFi** via le mesh ESP-NOW local :
| Fonction | Online (WiFi dispo) | Offline (ESP-NOW seul) |
|----------|---------------------|------------------------|
| NPC voix (XTTS-v2) | Live depuis KXKM-AI | Fallback MP3 SD card |
| NPC voix (Piper TTS) | Tower:8001 | Fallback MP3 SD card |
| Détection NFC P6 | Locale (pas de réseau) | Identique |
| QR scanner P3 | Locale (BOX-3 camera) | Identique |
| Analytics | Cache local, upload post-jeu | Cache local |
| Scoring + diplôme PDF | En mémoire BOX-3 | En mémoire BOX-3 |
> **Recommandation :** générer le pool MP3 XTTS avant chaque déploiement si de nouvelles phrases ont été ajoutées (`python3 tools/tts/generate_xtts_pool.py`).
+220
View File
@@ -0,0 +1,220 @@
# Suitcase Layout — Zacus V3 Mobile Kit
3 suitcases total. All foam cutouts use 10mm EVA foam sheets.
Foam cavities are cut 1mm undersize for snug friction fit.
---
## Overview — 3-Suitcase System
| Suitcase | Name | Size | Contents | Weight est. |
|----------|----------------|------------|-------------------------------------------|-------------|
| A | Hub Suitcase | 57L rigid | BOX-3, router, powerbanks, RTC_PHONE, audio | ~6 kg |
| B | Puzzle Objects | 42L soft | P1, P4, P5, P7 puzzle boxes + accessories | ~4 kg |
| C | Props & Cards | 42L soft | P2 board, P3 cards, P6 tablet, cables, signage | ~3 kg |
---
## Suitcase A — Hub Suitcase (57L Rigid ABS)
**Dimensions:** 750 × 500 × 280 mm interior usable space
**Foam layout:** 2 layers — top tray + base
```
SUITCASE A — TOP VIEW (750mm wide × 500mm deep)
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────┐ ┌────────────────────┐ ┌─────────────┐ │
│ │ │ │ │ │ │ │
│ │ ESP32-S3-BOX-3 │ │ GL.iNet GL-MT3000 │ │ RTC_PHONE │ │
│ │ 200×120×70mm │ │ 150×100×45mm │ │ 120×80×40 │ │
│ │ │ │ │ │ │ │
│ └──────────────────┘ └────────────────────┘ └─────────────┘ │
│ │
│ ┌────────────────────────┐ ┌──────────────────────────────────┐ │
│ │ Anker Powerbank #1 │ │ Anker Powerbank #2 │ │
│ │ 175×80×28mm │ │ 175×80×28mm │ │
│ └────────────────────────┘ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌────────────────────────────────────────┐ │
│ │ Anker PB #3 │ │ JBL Go 3 Speaker 85×85×45mm │ │
│ │ 175×80×28mm │ └────────────────────────────────────────┘ │
│ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ USB-C Cables roll (10× 1m) — 200×100×60mm bundle │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ LED Strip USB 3m (rolled) 60×60×30mm │ │
│ │ USB Hub 4-port 90×35×20mm (cable mgmt) │ │
│ │ Ethernet patch cable 1m (zip bag) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
LID (zip pocket):
- GL.iNet power adapter
- Quick-start card (laminated A5)
- Master game guide booklet
- Spare micro-USB cable (BOX-3 fallback)
```
### Foam Cutout Specifications (Suitcase A)
| Item | Foam Cavity (L×W×D) | Layer |
|--------------------|---------------------|---------|
| BOX-3 | 202×122×72 mm | Base |
| GL-MT3000 | 152×102×47 mm | Base |
| RTC_PHONE | 122×82×42 mm | Base |
| Powerbank × 3 | 177×82×30 mm each | Base |
| JBL Go 3 | 87×87×47 mm | Base |
| Cable bundle | 202×102×62 mm | Base |
| LED strip roll | 62×62×32 mm | Base |
| USB Hub | 92×37×22 mm | Base |
| Ethernet cable | 50×50×15 mm | Base |
---
## Suitcase B — Puzzle Objects (42L Softcase)
**Dimensions:** 600 × 420 × 240 mm interior usable space
**Foam layout:** 1 layer with tall cutouts; lid pocket for accessories
```
SUITCASE B — TOP VIEW (600mm wide × 420mm deep)
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ │ │
│ │ P1 Boîte Sonore │ │ P4 Poste Radio Rétro │ │
│ │ 150×100×80mm │ │ 200×150×120mm │ │
│ │ │ │ │ │
│ └───────────────────┘ └──────────────────────────┘ │
│ │
│ ┌───────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ │ │
│ │ P5 Télégraphe │ │ P7 Coffre Final │ │
│ │ 180×80×60mm │ │ 250×200×150mm │ │
│ │ │ │ │ │
│ └───────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Accessory zone │ │
│ │ - Morse reference cards (zip bag) 150×120×5mm │ │
│ │ - Arcade button spare caps (bag) 80×80×30mm │ │
│ │ - USB-C cables 4× short (bag) 100×50×20mm │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
LID pocket:
- Puzzle reset cards (laminated, one per puzzle)
- Allen key M3 + M2.5 set
- Spare WS2812B LEDs (bag)
- Spare fuse kit
```
### Foam Cutout Specifications (Suitcase B)
| Item | Foam Cavity (L×W×D) | Notes |
|--------------------|---------------------|--------------------------------|
| P1 Boîte Sonore | 152×102×82 mm | Snug — PETG box |
| P4 Poste Radio | 202×152×122 mm | Extra 2mm clearance for knob |
| P5 Télégraphe | 182×82×62 mm | Brass key faces up |
| P7 Coffre Final | 252×202×152 mm | Heaviest piece — bottom layer |
| Accessory strip | 300×120×35 mm | Shared zone, non-foam dividers |
**Weight check:**
- P7 coffre: ~2.5kg (wood + electronics)
- P4 radio: ~0.8kg (PLA + 18650s)
- P1 boîte: ~0.5kg (PETG + speaker)
- P5 télégraphe: ~0.4kg (PETG + brass key)
- Accessories: ~0.3kg
- Foam: ~0.4kg
- **Total suitcase B: ~4.9kg** — within 5kg carry-on limit
---
## Suitcase C — Props & Cards (42L Softcase)
**Dimensions:** 600 × 420 × 240 mm interior usable space
```
SUITCASE C — TOP VIEW (600mm wide × 420mm deep)
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ P2 Magnetic Folding Board │ │
│ │ 600×400mm folded to 300×400×20mm │ │
│ │ (occupies full width when folded) │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ │ │
│ │ P6 Wooden │ │ P3 QR Cards + Signage │ │
│ │ Tablet │ │ - 6× A5 QR cards │ │
│ │ 300×200×20mm │ │ - 10× A4 labels │ │
│ │ (slots up) │ │ - 1× A3 puzzle map │ │
│ │ │ │ All in clear folder │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ P6 Symbol Pieces bag (12 pcs, 40×40×8mm each) │ │
│ │ Total bag: 200×150×30mm │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ P2 Component Kit │ │
│ │ - Magnetic resistors, LEDs, wires (bag) │ │
│ │ - Foam-padded compartment tray 200×150×40mm │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Misc │ │
│ │ - Setup guide A4 folder │ │
│ │ - Diploma templates (10 sheets, laminated) │ │
│ │ - USB A-C adapter set (bag) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
```
### Foam Cutout Specifications (Suitcase C)
| Item | Foam Cavity (L×W×D) | Notes |
|-------------------------|-----------------------|-------------------------------|
| P2 Folding Board | 302×402×22 mm | Full width — 2 foam strips hold|
| P6 Wooden Tablet | 302×202×22 mm | Slots face up — NFC tags safe |
| P6 Symbol Pieces bag | 202×152×32 mm | Soft bag, low cavity depth |
| P3 Cards folder | 220×160×20 mm | Clear A4 folder, thin section |
| P2 Component tray | 202×152×42 mm | Padded compartment tray |
| Misc folder zone | 220×160×20 mm | Documents + diplomas |
---
## Total Kit Weight & Volume
| Suitcase | Weight | Volume | Check-in |
|----------|--------|--------|----------|
| A (Hub) | ~6.0 kg | 57L | Yes — checked |
| B (Puzzles) | ~4.9 kg | 42L | Cabin OK |
| C (Props) | ~3.0 kg | 42L | Cabin OK |
| **Total** | **~13.9 kg** | **141L** | |
Kit fits 2-person team transport (car/train/plane). Suitcase A checked, B+C cabin luggage.
---
## Setup Procedure (Field Deployment)
1. Open Suitcase A: deploy BOX-3, plug router, connect all powerbanks via USB-C hub
2. Open Suitcase B: place P1/P4/P5 at puzzle stations; P7 at final position
3. Open Suitcase C: unfold P2 board at station; place P6 tablet; hide P3 QR cards per map
4. Power-on sequence: Router → BOX-3 → P1..P7 (each has auto-pair on boot via ESP-NOW)
5. BOX-3 dashboard shows all nodes: green = online, orange = pairing, red = fault
6. Estimated setup time: 15 minutes for 2 staff members
See `docs/DEPLOYMENT_RUNBOOK.md` for full field procedure including audio check and NPC test.
+500
View File
@@ -0,0 +1,500 @@
#!/usr/bin/env python3
"""
collect_metrics.py — Collecte et analyse des métriques de playtest.
Lit les logs série ESP-NOW de BOX-3, parse les événements de résolution,
demandes d'indices et décisions NPC. Génère un rapport Markdown de session
et l'ajoute au fichier metrics_template.csv.
Usage:
# Lire depuis port série (BOX-3 connecté en USB)
python3 tools/playtest/collect_metrics.py --port /dev/cu.usbserial-XXXX
# Lire depuis un fichier log pré-capturé
python3 tools/playtest/collect_metrics.py --log-file session_001.log
# Spécifier un identifiant de session
python3 tools/playtest/collect_metrics.py --log-file session_001.log --session beta_001
# Écrire le rapport dans un dossier spécifique
python3 tools/playtest/collect_metrics.py --log-file session_001.log --output-dir docs/playtest/reports/
"""
import argparse
import csv
import re
import sys
from datetime import datetime, timedelta
from pathlib import Path
# ---------------------------------------------------------------------------
# Constantes
# ---------------------------------------------------------------------------
PUZZLES = ["P1_SON", "P2_CIRCUIT", "P3_QR", "P4_RADIO", "P5_MORSE", "P6_SYMBOLES", "P7_COFFRE"]
PUZZLE_LABELS = {
"P1_SON": "Séquence Sonore",
"P2_CIRCUIT": "Circuit LED",
"P3_QR": "QR Treasure",
"P4_RADIO": "Fréquence Radio",
"P5_MORSE": "Code Morse",
"P6_SYMBOLES": "Symboles Alchimiques",
"P7_COFFRE": "Coffre Final",
}
EXPECTED_DURATIONS_S = {
"P1_SON": 300,
"P2_CIRCUIT": 360,
"P3_QR": 240,
"P4_RADIO": 300,
"P5_MORSE": 420,
"P6_SYMBOLES": 360,
"P7_COFFRE": 300,
}
METRICS_CSV = Path(__file__).parent.parent.parent / "docs" / "playtest" / "metrics_template.csv"
# ---------------------------------------------------------------------------
# Patterns de log ESP-NOW / BOX-3
# ---------------------------------------------------------------------------
# Exemple : [12:05:33] [ESPNOW] P1_SON: SOLVED in 287s (attempts: 2)
RE_SOLVED = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[ESPNOW\]\s+(?P<puzzle>P\d+_\w+):\s+SOLVED\s+in\s+(?P<duration>\d+)s"
r"(?:\s+\(attempts:\s*(?P<attempts>\d+)\))?"
)
# Exemple : [12:08:01] [ESPNOW] P1_SON: SKIPPED
RE_SKIPPED = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[ESPNOW\]\s+(?P<puzzle>P\d+_\w+):\s+SKIPPED"
)
# Exemple : [12:06:12] [NPC] HINT requested: P1_SON level 2
RE_HINT = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[NPC\]\s+HINT requested:\s+(?P<puzzle>P\d+_\w+)\s+level\s+(?P<level>\d+)"
)
# Exemple : [11:55:00] [GAME] START target=60min group_size=5
RE_GAME_START = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[GAME\]\s+START\s+target=(?P<target>\d+)min\s+group_size=(?P<size>\d+)"
)
# Exemple : [12:55:12] [GAME] END score=1150 profile=TECH profile_correct=true
RE_GAME_END = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[GAME\]\s+END\s+score=(?P<score>\d+)"
r"\s+profile=(?P<profile>\w+)\s+profile_correct=(?P<correct>\w+)"
)
# Exemple : [11:56:10] [NPC] PROFILE_DETECTED: TECH
RE_PROFILE = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[NPC\]\s+PROFILE_DETECTED:\s+(?P<profile>\w+)"
)
# Exemple : [11:57:00] [NPC] PHRASE: adaptation.group_tech_detected.0 via XTTS
RE_PHRASE = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[NPC\]\s+PHRASE:\s+(?P<key>\S+)\s+via\s+(?P<backend>\w+)"
)
# Exemple : [12:10:00] [HW] FAILURE: P4_RADIO ESP-NOW timeout
RE_HW_FAILURE = re.compile(
r"\[(?P<ts>\d{2}:\d{2}:\d{2})\]\s+\[HW\]\s+FAILURE:\s+(?P<description>.+)"
)
# ---------------------------------------------------------------------------
# Parseur de logs
# ---------------------------------------------------------------------------
def parse_log(lines: list[str]) -> dict:
"""Parse les lignes de log BOX-3 et retourne les données de session."""
session: dict = {
"start_ts": None,
"end_ts": None,
"target_minutes": 0,
"group_size": 0,
"profile_detected": "UNKNOWN",
"profile_correct": None,
"final_score": 0,
"puzzles": {},
"hints": [],
"phrases": [],
"hw_failures": [],
}
for line in lines:
line = line.rstrip()
m = RE_GAME_START.search(line)
if m:
session["start_ts"] = m.group("ts")
session["target_minutes"] = int(m.group("target"))
session["group_size"] = int(m.group("size"))
continue
m = RE_GAME_END.search(line)
if m:
session["end_ts"] = m.group("ts")
session["final_score"] = int(m.group("score"))
session["profile_detected"] = m.group("profile")
session["profile_correct"] = m.group("correct").lower() == "true"
continue
m = RE_PROFILE.search(line)
if m:
session["profile_detected"] = m.group("profile")
continue
m = RE_SOLVED.search(line)
if m:
puzzle = m.group("puzzle")
session["puzzles"][puzzle] = {
"status": "SOLVED",
"ts": m.group("ts"),
"duration_s": int(m.group("duration")),
"attempts": int(m.group("attempts")) if m.group("attempts") else 1,
}
continue
m = RE_SKIPPED.search(line)
if m:
puzzle = m.group("puzzle")
session["puzzles"][puzzle] = {
"status": "SKIPPED",
"ts": m.group("ts"),
"duration_s": None,
"attempts": 0,
}
continue
m = RE_HINT.search(line)
if m:
session["hints"].append({
"ts": m.group("ts"),
"puzzle": m.group("puzzle"),
"level": int(m.group("level")),
})
continue
m = RE_PHRASE.search(line)
if m:
session["phrases"].append({
"ts": m.group("ts"),
"key": m.group("key"),
"backend": m.group("backend"),
})
continue
m = RE_HW_FAILURE.search(line)
if m:
session["hw_failures"].append({
"ts": m.group("ts"),
"description": m.group("description"),
})
return session
def _ts_to_minutes(ts_start: str | None, ts_end: str | None) -> float:
"""Calcule la durée en minutes entre deux timestamps HH:MM:SS."""
if not ts_start or not ts_end:
return 0.0
fmt = "%H:%M:%S"
try:
t0 = datetime.strptime(ts_start, fmt)
t1 = datetime.strptime(ts_end, fmt)
delta = t1 - t0
if delta.total_seconds() < 0:
# Passage minuit
delta += timedelta(hours=24)
return delta.total_seconds() / 60
except ValueError:
return 0.0
# ---------------------------------------------------------------------------
# Génération du rapport Markdown
# ---------------------------------------------------------------------------
def generate_report(session_id: str, session: dict) -> str:
"""Génère un rapport Markdown complet pour la session."""
actual_minutes = _ts_to_minutes(session["start_ts"], session["end_ts"])
target_minutes = session["target_minutes"]
duration_accuracy_pct = (
abs(actual_minutes - target_minutes) / target_minutes * 100
if target_minutes > 0 else 0.0
)
solved_puzzles = [p for p, d in session["puzzles"].items() if d["status"] == "SOLVED"]
skipped_puzzles = [p for p, d in session["puzzles"].items() if d["status"] == "SKIPPED"]
hints_total = len(session["hints"])
hw_failures = session["hw_failures"]
# Backend TTS stats
backends_used = [p["backend"] for p in session["phrases"]]
xtts_count = backends_used.count("XTTS")
piper_count = backends_used.count("PIPER")
sdcard_count = backends_used.count("SDCARD")
lines = [
f"# Rapport de Session — {session_id}",
f"",
f"Généré le : {datetime.now().strftime('%Y-%m-%d %H:%M')}",
f"",
f"---",
f"",
f"## Résumé",
f"",
f"| Métrique | Valeur |",
f"|----------|--------|",
f"| Session ID | `{session_id}` |",
f"| Taille du groupe | {session['group_size']} joueurs |",
f"| Durée cible | {target_minutes} min |",
f"| Durée réelle | {actual_minutes:.1f} min |",
f"| Précision durée | ±{duration_accuracy_pct:.1f}% |",
f"| Profil NPC détecté | {session['profile_detected']} |",
f"| Profil correct | {'OUI' if session['profile_correct'] else 'NON' if session['profile_correct'] is not None else 'N/A'} |",
f"| Puzzles résolus | {len(solved_puzzles)}/{len(PUZZLES)} |",
f"| Puzzles sautés | {len(skipped_puzzles)} |",
f"| Indices utilisés | {hints_total} |",
f"| Score final | {session['final_score']} |",
f"| Pannes matériel | {len(hw_failures)} |",
f"",
]
# Critères de succès
lines += [
f"## Critères de Succès",
f"",
f"| Critère | Objectif | Résultat | Statut |",
f"|---------|----------|----------|--------|",
f"| Temps de setup | ≤ 15 min | — | — |",
f"| Précision durée | ≤ ±10% | ±{duration_accuracy_pct:.1f}% | {'PASS' if duration_accuracy_pct <= 10 else 'FAIL'} |",
f"| Profil NPC correct | OUI | {'OUI' if session['profile_correct'] else 'NON'} | {'PASS' if session['profile_correct'] else 'FAIL'} |",
f"| Pannes matériel | 0 | {len(hw_failures)} | {'PASS' if not hw_failures else 'FAIL'} |",
f"| Jeu terminé | OUI | {'OUI' if 'P7_COFFRE' in solved_puzzles else 'NON'} | {'PASS' if 'P7_COFFRE' in solved_puzzles else 'FAIL'} |",
f"",
]
# Détail puzzles
lines += [
f"## Détail par Puzzle",
f"",
f"| Puzzle | Statut | Durée réelle | Durée attendue | Écart | Tentatives | Indices |",
f"|--------|--------|-------------|----------------|-------|-----------|---------|",
]
for puzzle_id in PUZZLES:
label = PUZZLE_LABELS.get(puzzle_id, puzzle_id)
data = session["puzzles"].get(puzzle_id, {"status": "NOT_REACHED", "duration_s": None, "attempts": 0})
expected = EXPECTED_DURATIONS_S.get(puzzle_id, 0)
puzzle_hints = [h for h in session["hints"] if h["puzzle"] == puzzle_id]
status = data["status"]
duration_s = data.get("duration_s")
duration_str = f"{duration_s}s" if duration_s is not None else ""
expected_str = f"{expected}s"
ecart_str = f"{duration_s - expected:+d}s" if duration_s is not None else ""
attempts = data.get("attempts", 0)
lines.append(
f"| {label} | {status} | {duration_str} | {expected_str} | {ecart_str} | {attempts} | {len(puzzle_hints)} |"
)
lines += [""]
# Indices demandés
if session["hints"]:
lines += [
f"## Indices Demandés",
f"",
f"| Heure | Puzzle | Niveau |",
f"|-------|--------|--------|",
]
for hint in session["hints"]:
puzzle_label = PUZZLE_LABELS.get(hint["puzzle"], hint["puzzle"])
lines.append(f"| {hint['ts']} | {puzzle_label} | {hint['level']} |")
lines += [""]
# NPC / voix
if session["phrases"]:
lines += [
f"## Interventions NPC",
f"",
f"- Phrases jouées : {len(session['phrases'])}",
f"- XTTS-v2 (GPU) : {xtts_count}",
f"- Piper TTS : {piper_count}",
f"- SD Card fallback : {sdcard_count}",
f"",
]
# Pannes matériel
if hw_failures:
lines += [
f"## Pannes Matériel",
f"",
f"| Heure | Description |",
f"|-------|-------------|",
]
for failure in hw_failures:
lines.append(f"| {failure['ts']} | {failure['description']} |")
lines += [""]
lines += [
f"---",
f"",
f"*Rapport généré automatiquement par `tools/playtest/collect_metrics.py`*",
]
return "\n".join(lines)
# ---------------------------------------------------------------------------
# Écriture CSV
# ---------------------------------------------------------------------------
def append_to_csv(session_id: str, session: dict, csv_path: Path, nps_score: float = 0.0) -> None:
"""Ajoute une ligne de métriques au fichier CSV de référence."""
actual_minutes = _ts_to_minutes(session["start_ts"], session["end_ts"])
solved = [p for p, d in session["puzzles"].items() if d["status"] == "SOLVED"]
skipped = [p for p, d in session["puzzles"].items() if d["status"] == "SKIPPED"]
row = {
"session_id": session_id,
"date": datetime.now().strftime("%Y-%m-%d"),
"group_size": session["group_size"],
"target_duration": session["target_minutes"],
"actual_duration": round(actual_minutes, 1),
"group_profile_detected": session["profile_detected"],
"group_profile_actual": "",
"puzzles_solved": len(solved),
"puzzles_skipped": len(skipped),
"hints_total": len(session["hints"]),
"nps_score": nps_score,
"notes": "",
}
fieldnames = [
"session_id", "date", "group_size", "target_duration", "actual_duration",
"group_profile_detected", "group_profile_actual", "puzzles_solved",
"puzzles_skipped", "hints_total", "nps_score", "notes",
]
# Lire les lignes existantes
existing_rows: list[dict] = []
if csv_path.exists():
with open(csv_path, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for r in reader:
if r.get("session_id") != session_id:
existing_rows.append(r)
# Ré-écrire avec la nouvelle ligne
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for r in existing_rows:
writer.writerow(r)
writer.writerow(row)
print(f"CSV mis à jour : {csv_path}")
# ---------------------------------------------------------------------------
# Lecture des sources de données
# ---------------------------------------------------------------------------
def read_from_file(path: str) -> list[str]:
"""Lit les lignes depuis un fichier log."""
with open(path, encoding="utf-8") as f:
return f.readlines()
def read_from_serial(port: str, timeout_seconds: int = 7200) -> list[str]:
"""Lit les lignes depuis un port série (BOX-3 connecté en USB)."""
try:
import serial # type: ignore
except ImportError:
print("Erreur : pyserial non installé. Lancer : pip install pyserial", file=sys.stderr)
sys.exit(1)
lines: list[str] = []
print(f"Lecture du port série {port} (Ctrl+C pour arrêter)...")
with serial.Serial(port, baudrate=115200, timeout=1) as ser:
start = datetime.now()
game_ended = False
while True:
line = ser.readline().decode("utf-8", errors="replace")
if line:
lines.append(line)
print(line, end="", flush=True)
if "[GAME] END" in line:
game_ended = True
print("\n[collect_metrics] Fin de session détectée.")
break
elapsed = (datetime.now() - start).total_seconds()
if elapsed > timeout_seconds:
print(f"\n[collect_metrics] Timeout {timeout_seconds}s atteint.")
break
return lines
# ---------------------------------------------------------------------------
# Point d'entrée
# ---------------------------------------------------------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Collecte et analyse des métriques de playtest Zacus V3"
)
source = parser.add_mutually_exclusive_group(required=True)
source.add_argument("--port", help="Port série BOX-3 (ex: /dev/cu.usbserial-XXXX)")
source.add_argument("--log-file", help="Fichier log pré-capturé")
parser.add_argument("--session", default=None, help="Identifiant de session (ex: beta_001)")
parser.add_argument("--output-dir", default=None, help="Dossier de sortie pour le rapport Markdown")
parser.add_argument("--nps-score", type=float, default=0.0, help="Score NPS moyen du groupe (Q1, 0-10)")
parser.add_argument("--no-csv", action="store_true", help="Ne pas mettre à jour le fichier CSV")
args = parser.parse_args()
# Identifiant de session
session_id = args.session or f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# Lecture des données
if args.port:
lines = read_from_serial(args.port)
else:
lines = read_from_file(args.log_file)
if not lines:
print("Erreur : aucune donnée à analyser.", file=sys.stderr)
sys.exit(1)
# Analyse
session = parse_log(lines)
# Rapport Markdown
report_md = generate_report(session_id, session)
output_dir = Path(args.output_dir) if args.output_dir else Path(__file__).parent.parent.parent / "docs" / "playtest" / "reports"
output_dir.mkdir(parents=True, exist_ok=True)
report_path = output_dir / f"{session_id}_report.md"
report_path.write_text(report_md, encoding="utf-8")
print(f"Rapport Markdown : {report_path}")
# Affichage rapide
actual_minutes = _ts_to_minutes(session["start_ts"], session["end_ts"])
solved = [p for p, d in session["puzzles"].items() if d["status"] == "SOLVED"]
print(f"\nRésumé session {session_id}:")
print(f" Groupe : {session['group_size']} joueurs")
print(f" Durée : {actual_minutes:.1f} min / {session['target_minutes']} min cible")
print(f" Profil NPC : {session['profile_detected']} ({'correct' if session['profile_correct'] else 'incorrect'})")
print(f" Puzzles : {len(solved)}/{len(PUZZLES)} résolus, {len(session['hints'])} indices")
print(f" Score : {session['final_score']}")
print(f" NPS : {args.nps_score}/10")
print(f" HW pannes : {len(session['hw_failures'])}")
# Mise à jour CSV
if not args.no_csv:
append_to_csv(session_id, session, METRICS_CSV, nps_score=args.nps_score)
if __name__ == "__main__":
main()