178 Commits

Author SHA1 Message Date
clemsail ba248e92fe feat(plip): tones module — France Télécom dial/busy/ringback 2026-06-14 21:53:50 +02:00
electron 39f4cd2313 Merge pull request 'feat(plip): mute audio output when handset on-hook' (#19) from feat/plip-mute-onhook into main
CI / platformio (push) Failing after 3m57s
2026-06-14 18:45:03 +00:00
clemsail b8ba1457fb feat(plip): mute audio output when handset on-hook (gate playback on hook state)
CI / platformio (pull_request) Failing after 3m41s
2026-06-14 20:43:33 +02:00
electron 7718f4653d Merge pull request 'feat(plip): streaming file upload + playback (MAX_BODY 256K, full TTS phrases)' (#18) from feat/plip-maxbody-256k into main
CI / platformio (push) Failing after 11m35s
2026-06-14 18:30:47 +00:00
clemsail f7104b4180 feat(plip): bump /game/file MAX_BODY 64K->256K for full TTS phrases
CI / platformio (pull_request) Failing after 3m46s
2026-06-14 20:29:25 +02:00
electron b748c2d75f Merge pull request 'feat(plip): I2S RX mic capture + POST /voice/capture (STT input leg) + ES8388 ADC register fix' (#17) from feat/plip-mic-capture into main
CI / platformio (push) Failing after 4m20s
2026-06-14 16:53:01 +00:00
clemsail dd3d239784 feat(plip): I2S RX mic capture + POST /voice/capture (WAV) — STT input leg
CI / platformio (pull_request) Failing after 4m5s
- audio.c: add full-duplex I2S RX channel (s_mic_handle, GPIO35/ASDOUT)
  enabled at boot alongside TX; streaming capture API with VAD
  (audio_capture_begin/read_frame/end) avoids large buffer allocation
- audio.h: export streaming capture API
- es8388.c: fix ADC register addresses (ADCCONTROL2=0x0A, 3=0x0B, 4=0x0C,
  5=0x0D, 8=0x10, 9=0x11 — were off by 1 from Espressif reference);
  CHIP_CTL1=0x12 (record+play), DACCONTROL21=0xC0 (ADC+DAC LRCK sync),
  ADCPOWER=0x09, FSM restart pulse (CHIPPOWER 0xF0→0x00) after CTL21 write;
  fix set_volume() to use OUT1 volume regs (0x2E/0x2F) not clock-sync reg 0x2B
- net.c: POST /voice/capture streaming WAV handler (chunked, 44-byte header
  + PCM frames, VAD onset/silence, query params max_ms/silence_ms);
  GET /debug/regs for live ES8388 register dump (diagnostic aid)
- board_config.h: PLIP_I2S_DIN=GPIO35 (ES8388 ASDOUT)

Build: clean. Endpoint functional (200 OK, valid WAV structure).
RX data currently silent — suspected hardware issue (mic not connected
to LIN1/LIN2 on this specific kit instance); firmware implementation complete.
2026-06-14 18:50:22 +02:00
electron b31388dd86 Merge pull request 'feat(p5,p6): scaffold morse + nfc puzzles behind CONFIG flags (default off)' (#16) from feat/p5-p6-scaffold into main
CI / platformio (push) Failing after 11m37s
2026-06-14 15:00:31 +00:00
clemsail d5b9297cee feat(p5,p6): scaffold morse + nfc puzzles behind CONFIG flags, default off
CI / platformio (pull_request) Failing after 3m49s
2026-06-14 16:58:42 +02:00
electron 038e0a980a Merge pull request 'feat(p7): scaffold coffre actuator behind CONFIG_ZACUS_P7_COFFRE_ENABLE (default off)' (#15) from feat/p7-coffre-scaffold into main
CI / platformio (push) Failing after 4m9s
2026-06-14 14:15:26 +00:00
clemsail fc4b58f785 feat(p7): scaffold coffre actuator (servo/relay) behind CONFIG_ZACUS_P7_COFFRE_ENABLE, default off
CI / platformio (pull_request) Failing after 12m3s
2026-06-14 16:01:33 +02:00
electron 022e343750 Merge pull request 'feat(box3,plip): POST /game/cmd — trigger cmd_exec over HTTP' (#14) from feat/annex-game-cmd into main
CI / platformio (push) Failing after 4m15s
2026-06-14 12:13:33 +00:00
clemsail 875333b6f0 fix(plip): play_wav_file — SPIFFS lazy-mount for /spiffs/ paths, skip SD gate
CI / platformio (pull_request) Failing after 10m0s
2026-06-14 14:08:08 +02:00
clemsail e9413fa898 feat(box3,plip): POST /game/cmd — trigger cmd_exec over HTTP (WiFi-direct) 2026-06-14 13:54:02 +02:00
electron 39903c376d Merge pull request 'feat(plip): ESP-IDF migration — ES8388 audio + WiFi/httpd + cmd_exec + hook/ring' (#13) from feat/plip-idf-migration into main
CI / platformio (push) Failing after 5m8s
2026-06-14 11:34:21 +00:00
clemsail 309005360b feat(plip_voice): ESP-IDF port of PLIP retro telephone annex
CI / platformio (pull_request) Failing after 4m52s
New IDF project targeting AI-Thinker ESP32-A1S Audio Kit (ESP32, ES8388
codec). Replaces the Arduino PLIP_FIRMWARE with a proper IDF component
structure aligned on box3_voice / scenario_mesh infrastructure.

Phases implemented and hardware-verified:
- A: ES8388 I2C init + I2S TX; 440 Hz test tone exits the speaker
- B: WiFi STA + httpd :80 (GET /status, POST /game/scenario, /game/file)
- C: ESP-NOW CMD receiver via shared scenario_mesh lib; op=play dispatched
     to audio; screen/evt/led logged (no display on PLIP)
- D: Off-hook GPIO monitor (GPIO4=BOOT button stand-in); ring tone cadence;
     POST /voice/hook to master on pickup/hangup (200 OK confirmed)

MAC (first observed): a0:b7:65:e7:f6:44
IP (DHCP): 192.168.0.138

Files:
  plip_voice/CMakeLists.txt          - project root; EXTRA_COMPONENT_DIRS=../lib/scenario_mesh
  plip_voice/partitions.csv          - 4MB OTA dual-bank + SPIFFS (matches PLIP_FIRMWARE)
  plip_voice/sdkconfig.defaults      - ESP32 target, 4MB flash DIO, ch=11 hint (no creds)
  plip_voice/main/Kconfig.projbuild  - PLIP_ menu (SSID/pwd/channel/master_url/hook_gpio)
  plip_voice/main/board_config.h     - A1S pin map (I2C 32/33, I2S 0/27/25/26/35, PA=21)
  plip_voice/main/es8388.[ch]        - ES8388 I2C register init sequence (legacy i2c driver)
  plip_voice/main/audio.[ch]         - I2S TX + async play queue + ring task
  plip_voice/main/net.[ch]           - WiFi STA + httpd (NVS creds with Kconfig fallback)
  plip_voice/main/cmd_exec.[ch]      - D5 CMD executor (play→audio; screen/evt/led→log)
  plip_voice/main/hook_client.[ch]   - POST /voice/hook via esp_http_client
  plip_voice/main/phone.[ch]         - Off-hook GPIO ISR + debounce + ring control
  plip_voice/main/main.c             - app_main boot sequence (all four phases)

Build: IDF 5.4.4, target esp32, clean.
Flash: /dev/cu.usbserial-0001, 4MB flash verified.
2026-06-14 13:29:33 +02:00
electron bbe79deb07 Merge pull request 'feat(box3): POST /game/file over HTTP' (#12) from feat/box3-game-file into main
CI / platformio (push) Failing after 17m50s
2026-06-14 11:09:43 +00:00
clemsail 935f86d844 feat(box3): POST /game/file — write assets to SD over HTTP (WiFi-direct)
CI / platformio (pull_request) Failing after 4m26s
2026-06-14 12:58:45 +02:00
electron 29fa8eed34 Merge pull request 'feat(box3): real screen render + real WAV/I2S play in cmd_exec' (#11) from feat/box3-screen-play-real into main
CI / platformio (push) Failing after 6m5s
2026-06-14 10:02:50 +00:00
clemsail 3e702f6fd8 feat(box3): real screen rendering + WAV I2S playback for CMD executor
CI / platformio (pull_request) Failing after 9m25s
Objective 1 — screen: full-scene faithful rendering on 320x240 LVGL display.
Palette matches idf_zacus display_ui reference: bg #0055AA (Workbench blue),
symbol #FF8800 (orange / font-48), title #FFFFFF (font-24 top), subtitle
#AAAAAA (font-14 bottom). All four effects implemented:
  - pulse: LVGL anim opacity COVER→50 breathing on symbol (600ms half-period)
  - glitch: lv_timer 120ms flicker + X-jitter on title
  - gyro: lv_arc rotating ring around symbol (1200ms full rotation)
  - none: static

Objective 2 — play: real WAV PCM-16 decode + I2S streaming.
  - SD card mounted best-effort via bsp_sdcard_mount() on first play CMD
  - WAV header parser (chunk-walker, tolerant of non-standard ordering)
  - PCM samples streamed via i2s_channel_write (same s_spk_handle as TTS)
  - Embedded test cue (C5-E5-G5, 570ms, 16kHz mono, ~18 KB) baked into
    embedded_wav.h — proves real WAV decode + I2S path without SD card
  - Graceful fallback chain: SD file → embedded cue → 880 Hz beep

main.c: cmd_exec_set_spk_handle() called after speaker_init() to pass the
I2S TX channel handle to the CMD executor.

Tested on ESP32-S3-BOX-3 (/dev/cu.usbmodem11301):
  - All four screen effects confirmed via serial log + visual observation
  - play embedded://cue streams 18240 bytes PCM at 16kHz confirmed
  - SD mount succeeds (card present), file not found → embedded fallback OK
  - No crashes, existing voice pipeline and scenario server unaffected
2026-06-14 12:01:31 +02:00
electron 0b530e70e0 Merge pull request 'feat(box3): ESP-NOW CMD executor (D5 contract)' (#10) from feat/box3-cmd-executor into main
CI / platformio (push) Failing after 9m39s
2026-06-14 09:33:59 +00:00
clemsail 735aaed648 feat(box3): implement ESP-NOW CMD executor (D5 contract)
CI / platformio (pull_request) Failing after 4m36s
Add cmd_exec.c / cmd_exec.h providing cmd_exec_handle() — a tolerant
cJSON-based dispatcher for structured ESP-NOW CMD frames per the D5
wire contract ({"op":"<verb>","a":{<args>}}, ≤200 bytes).

Supported ops (all logged):
  screen  — renders title/subtitle/symbol on an LVGL full-screen
             overlay via bsp_display_lock; "pulse"/"flash" effect
             tints the background blue.
  play    — logs the path + loop flag, emits an 880 Hz beep via
             audio_play_tone() (no media_manager on box3_voice).
  evt     — logs event name (card→master direction, unusual here).
  led     — logs pattern (stub, no LED ring on box3_voice).
  unknown — ESP_LOGW + ESP_ERR_NOT_SUPPORTED, no crash.

Wire-up in main.c:
  on_mesh_text() callback installed via scenario_mesh_set_text_cb()
  immediately after scenario_mesh_init(). Every SCENARIO_MESH_TEXT_CMD
  frame is decoded and dispatched; EVT frames are logged and ignored.
  cmd_exec.c added to CMakeLists.txt SRCS.

Channel fix in sdkconfig.defaults:
  Document that CONFIG_ZACUS_WIFI_CHANNEL must match the master's
  connected WiFi channel (lab: ch11) for ESP-NOW co-channel operation.

Tested on hardware (BOX-3 MAC 90:e5:b1:cc:05:f8, /dev/cu.usbmodem11301):
  - Build: 2104/2104 targets, 0 errors, 0 warnings on new files.
  - Flash: idf.py -p /dev/cu.usbmodem11301 flash — Done.
  - Serial proof:
      I (10115) zacus-voice: ESP-NOW CMD executor registered (op: screen/play/evt/led)
      I (10115) cmd_exec: CMD op=screen t="ZACUS" u="" y="LA" e="pulse"
      I (13327) cmd_exec: CMD op=play path="/spiffs/ambiance.wav" loop=0 (beep fallback)
      W (16497) cmd_exec: CMD op="explode" unknown — ignored
    All three via POST /game/espnow/cmd on master ({"ok":true,"status":"ESP_OK"}).
2026-06-14 11:20:45 +02:00
electron 7f52d3d759 Merge pull request 'feat(P4): media_manager vrai playback WAV + /game/media/play' (#9) from feat/p4-media-wav-playback into main
CI / platformio (push) Failing after 3m27s
2026-06-13 14:44:29 +00:00
electron c7485f272f Merge pull request 'fix(box3): SPIFFS mount + canal WiFi configurable (relais ESP-NOW)' (#8) from fix/box3-spiffs-channel into main
CI / platformio (push) Has been cancelled
2026-06-13 14:44:13 +00:00
clement fcb872c226 feat(p4): décodeur MP3 (helix) + mutex I2S TTS↔musique + FATFS LFN
CI / platformio (pull_request) Failing after 10m4s
- voice_pipeline : mutex play_lock pris dans play_start / rendu dans play_end
  (+ tous chemins d'erreur). Sérialise TTS et musique sur le canal I2S TX
  partagé ; un play_start concurrent attend 2 s puis renvoie BUSY.
- media_manager : décodage MP3 via helix (chmorgan/esp-libhelix-mp3). Refactor
  playback_task en dispatcher d'extension → stream_wav / stream_mp3. stream_mp3
  décode frame par frame (buffers heap), downmix stéréo→mono, volume, feed I2S.
  Stack tâche 5120→8192 (le décodeur consomme de la pile).
- sdkconfig.defaults : CONFIG_FATFS_LFN_HEAP — les assets SD ont des noms > 8.3
  (SCENE_WIN.mp3, sonar_hint.mp3…) ; sans LFN fopen échoue ("open failed").

Validé sur Freenove : upload SCENE_WIN.mp3 (510 Ko, stéréo) sur /sdcard →
play → helix décode 22050 Hz 2ch → downmix → I2S MAX98357A. win.mp3 22050 mono
idem. Mutex : TTS et musique ne peuvent plus se chevaucher sur le DAC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:42:13 +02:00
clement 8a58c7d92b feat(p4): brancher media_manager + /game/file sur /sdcard
CI / platformio (pull_request) Failing after 3m39s
- media_manager resolve_play_path : un chemin relatif préfère désormais
  /sdcard/music/<f> quand il existe (asset store SD), fallback LittleFS.
  Les chemins absolus (/sdcard/... | /littlefs/...) passent inchangés.
- /game/file : route le préfixe `sd/<…>` → /sdcard/<…> (≤8 Mio, exige la
  carte montée) en plus de `apps/<…>` → /littlefs/apps/ (≤256 Kio).
- sd_storage.h : include <stdint.h> (uint32_t).

Validé sur Freenove : POST /game/file?path=sd/music/cue440.wav → écrit sur
la microSD ; POST /game/media/play {path:"cue440.wav"} → résolu en
/sdcard/music/cue440.wav, 32000 B streamés en ~1005 ms au MAX98357A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:28:16 +02:00
clement a8f0210567 feat(p4): montage microSD (SDMMC 1-bit) sur /sdcard
CI / platformio (pull_request) Failing after 6m0s
Nouveau composant sd_storage : monte la carte microSD du Freenove en SDMMC
1-bit (CMD=38, CLK=39, D0=40 — pinout FREENOVE_SDMMC_* du firmware Arduino)
sur /sdcard via esp_vfs_fat_sdmmc_mount. Best-effort au boot : carte absente
= non fatal, fallback LittleFS. Lève le plafond LittleFS 5 Mo pour les assets.

Validé sur Freenove : carte ~3839 MiB montée à /sdcard au boot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:49:44 +02:00
clement c35c9673f4 feat(p4): media_manager — vrai playback WAV + endpoint /game/media/play
CI / platformio (pull_request) Failing after 3m32s
Remplace le stub de media_manager_play() (qui simulait 2 s) par un vrai
streaming audio :
- parse_wav_header : RIFF/WAVE 16-bit PCM mono/stéréo, positionne sur data.
- playback_task : lit le WAV par chunks et le pousse au MAX98357A via
  l'I2S TX de voice_pipeline (play_start/chunk/end), avec downmix stéréo→mono
  et volume logiciel. Tâche dédiée (non bloquante), stop coopératif.
- media_manager_stop() attend la fin de la tâche.

Ajoute POST /game/media/play {path} dans game_endpoint pour déclencher une
lecture (gateway/atelier → cue audio), sans passer par une décision NPC.

Validé sur Freenove : upload WAV via /game/file → POST /game/media/play →
play_start sr=16000 → 32000 B streamés en ~1006 ms (cadence DMA temps réel,
= durée du fichier) → play_end. MP3 + montage SD = slices P4 suivantes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:31:54 +02:00
clement 2517cef7db fix(box3): SPIFFS mount + canal WiFi configurable pour relais ESP-NOW
CI / platformio (pull_request) Failing after 3m54s
scenario_server.c : SPIFFS_LABEL valait "storage" alors que la partition
s'appelle "spiffs" (partitions.csv) → esp_vfs_spiffs_register échouait en
ESP_ERR_NOT_FOUND, la BOX-3 ne pouvait persister aucun scénario reçu (HTTP
500 + relais ESP-NOW sans effet). Corrigé en "spiffs".

main.c + Kconfig.projbuild : ajout de CONFIG_ZACUS_WIFI_CHANNEL (défaut
0 = auto) câblé sur wifi_config.sta.channel. Sur réseau multi-AP / mesh
(même SSID sur plusieurs canaux), permet de forcer la BOX-3 co-canal avec
le master — prérequis ESP-NOW.

Validé sur matériel : master (ch6) → POST /game/scenario/relay → la BOX-3
reçoit + réassemble (679 B) via scenario_mesh, monte SPIFFS, hot-load OK,
reboot pour appliquer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:04:05 +02:00
Claude Worker claude2 7893c32bf6 feat(espnow): canal texte CMD/EVT + endpoint debug
Frames courtes commandes/evenements sur la pile scenario_mesh
(sentinel seq=0xFFFF + kind, retrocompatible : les anciens
recepteurs les jettent via le check malformed). API additive
send_text/set_text_cb (dispatch hors callback WiFi via tache
dediee). Master : POST /game/espnow/cmd {peer|broadcast,command}
+ log des CMD/EVT recus. Teste sur FNK0102H : broadcast ESP_OK,
unicast sans recepteur ESP_ERR_TIMEOUT, alias inconnu 404.
Spec : docs/superpowers/specs/2026-06-11-espnow-cmd-evt-design.md
2026-06-11 05:47:20 +02:00
Claude Worker claude2 31dcb28c62 fix(espnow): compteur peers reel + alias UAF
scenario_mesh_peer_count() cable sur le stub sante (espnow_peers
restait a 0) ; reponse POST /game/peers construite avant
cJSON_Delete (use-after-free observe sur carte : alias illisible).
Teste sur FNK0102H : register live + NVS recharge au boot
(espnow_peers=2), relay -> timeout/unknown_peer attendus.
2026-06-11 05:18:55 +02:00
Claude Worker claude2 f5d8134cee fix(intro): horloge intro en temps reel
Sur carte, la boucle display (vTaskDelay 10 ms + rendu LVGL)
tourne sous 100 Hz : le dt fixe de 10 ms faisait trainer
l'horloge virtuelle derriere la deadline murale et la phase FX
ne demarrait jamais. Mesure du dt reel par delta de ticks
(borne 100 ms) + log de l'ordre des FX tires. Verifie sur
FNK0102H : intro fx order=0,2,3 a t=9.6s, 35 s sans panic.
2026-06-11 05:03:03 +02:00
Claude Worker claude2 9c133842a4 feat(intro): pool FX 3D complet + tirage par boot
Ajout des 3 dernieres scenes 3D de l'original : starfield 3D
(vol en z + trainees), voxel landscape (heightfield raycast) et
wirecube v9 (cube filaire Bresenham). L'intro tire 3 effets
distincts au hasard parmi les 6 a chaque boot (Fisher-Yates),
sans rallonger les 16 s. Build vert.
2026-06-11 04:54:17 +02:00
Claude Worker claude2 1faba591d5 feat(p1): log notes detectees + progression
Trace serie du bout-en-bout melodie : note MIDI entendue,
progression pos/count du validateur, marqueur (reset) sur fausse
note, ligne dediee a la completion. Prerequis documente au test
physique P1.
2026-06-11 04:47:53 +02:00
Claude Worker claude2 ea0e07acbe feat(intro): port 3D FX phases B/C/D
Rotozoom, dot sphere et ray corridor portes depuis le FxEngine
Arduino (boucles de rendu seules, sans CapsAllocator ni timelines).
Rendu 240x160 RGB565 en PSRAM double en pixels vers un lv_canvas
plein ecran derriere logo+scroller ; phases A 7s puis 3x3s ;
fallback starfield si l'alloc PSRAM echoue. Build vert (35% libre).
2026-06-11 04:45:56 +02:00
Claude Worker claude2 898808c396 docs: top README points to idf_zacus + box3 2026-06-11 02:58:10 +02:00
Claude Worker claude2 a7463464c2 docs: correct sensor comment to OV3660 2026-06-11 02:57:05 +02:00
Claude Worker claude2 080581f39f Merge qr fixes: adaptive geometry + mirror + QVGA 2026-06-11 02:56:47 +02:00
Claude Worker claude2 6d7b670256 docs: refresh firmware READMEs (puzzles+UI+REST) 2026-06-11 02:56:47 +02:00
Claude Worker claude2 fac94d5885 fix(qr): QVGA viewfinder, mirror, sensor tuning 2026-06-11 02:43:51 +02:00
Claude Worker claude2 ed336967a1 feat(box3): QR size + dim screen for camera 2026-06-11 02:43:51 +02:00
Claude Worker claude2 132b88337b fix(qr): adaptive frame size + OV3660 tuning 2026-06-11 02:03:13 +02:00
Claude Worker claude2 859c975654 feat(box3): larger QR for camera decode 2026-06-11 02:03:13 +02:00
Claude Worker claude2 087dbd3c03 feat(box3): QR + melody stimulus generator 2026-06-10 23:15:33 +02:00
Claude Worker claude2 e8125a20e5 Merge feat/freenove-local-puzzles: local puzzles + scenario hook + UI port 2026-06-10 23:06:04 +02:00
Claude Worker claude2 0e4cea9c39 feat(game): POST /game/file provisions apps 2026-06-10 22:46:05 +02:00
Claude Worker claude2 fd1a517989 feat(ui): cracktro intro + file browser + apps 2026-06-10 22:38:44 +02:00
Claude Worker claude2 563dfea368 feat(ui): Workbench shell + boot zoom intro 2026-06-10 22:20:28 +02:00
Claude Worker claude2 a552e1976e feat(ui): 5-way buttons (view + brightness) 2026-06-10 22:09:47 +02:00
Claude Worker claude2 d0f217f5d9 feat(ui): scene IR metadata + fx glitch/gyro 2026-06-10 22:01:17 +02:00