Files
le-mystere-professeur-zacus/PLIP_FIRMWARE
L'électron rare 07433dfdc5 feat(plip): firmware skeleton on ES8388 dev kit
Bringup target: AI-Thinker ESP32-A1S Audio Kit V2.2
(ES8388 codec). End target: custom PLIP PCB with Si3210
SLIC + RJ9 retro handset (per
docs/superpowers/specs/2026-04-08-plip-telephone-design.md).

Three FreeRTOS tasks share state via a queue:
- phone_task (core 1): off-hook GPIO interrupt + ring
- audio_task (core 0): drains command queue, routes to
  ES8388 (dev kit) / Si3210 (PCB) via ESP32-audioI2S
- network_task (core 0): WiFi station + REST server

REST contract for the Zacus master ESP32 to drive PLIP:
- POST /ring   {duration_ms}
- POST /play   {source: "sd:/..." | "http://tower:8001/..."}
- POST /stop
- GET  /status -> {off_hook, playing}

Pin assignments live in platformio.ini build_flags so the
dev kit -> PCB swap stays mechanical (just rewire to
Si3210 SPI + INT and switch the codec init in
audio_task).

MVP scope (option b in the brainstorm):
- ring on demand
- off-hook GPIO -> notify master
- MP3 playback from SD
- WiFi REST endpoint
Out of v1 (deliberately deferred):
- Bluetooth Classic A2DP/HFP (still in the design doc)
- Si3210 driver (when PCB lands)
- OTA via desktop FirmwareManager (after bringup stable)

Files:
- platformio.ini (espressif32@6.5.0, esp32dev board with
  custom partitions + ESP32-A1S build flags)
- partitions/plip_4mb.csv (2x OTA app slots + LittleFS)
- src/main.cpp + 3 task .cpp files (skeleton, log-only)
- README.md (architecture, REST contract, pin map,
  9-step roadmap, submodule conversion)
- CLAUDE.md (nested guidance for Claude Code)
- .gitignore

Inlined for now; convert to a git submodule once a remote
URL is provisioned (procedure documented in README).
Root CLAUDE.md "Firmware" surface and "Where to Look"
table updated.

Acceptance: directory layout + build config + skeleton
sources land. No compile validation in this commit
(PlatformIO not on the workstation per the Q1=ESP32-A1S
dev kit bringup decision).
2026-05-03 13:21:43 +02:00
..

PLIP firmware

Bringup target: AI-Thinker ESP32-A1S Audio Kit V2.2 (ES8388 codec). End target: the custom PLIP-Téléphone PCB with Si3210 SLIC + RJ9 retro handset (see ../hardware/projects/plip-telephone/ and ../docs/superpowers/specs/2026-04-08-plip-telephone-design.md).

We start on the dev kit so the audio + network state machine can be debugged without waiting for the PCB. The codec is swapped (ES8388 → Si3210) on the PCB; everything else stays.

Architecture

Three FreeRTOS tasks, message-passing through a queue:

Task Core Stack Role
phone 1 4096 Off-hook GPIO interrupt (dev kit: BOOT button on GPIO4; PCB: Si3210 INT). Drives ring on the codec.
audio 0 8192 Drains audio_command_queue. Routes Stop / PlaySdMp3 / PlayHttpStream through ESP32-audioI2S.
network 0 8192 WiFi station + REST endpoint. Translates HTTP requests into audio queue messages.

REST contract

The Zacus master ESP32 talks to PLIP over the LAN:

Method Path Body Effect
POST /ring {"duration_ms": 4000} Trigger ring (SLIC -72V; dev kit: short audio beep)
POST /play {"source": "sd:/intro.mp3"} or {"source": "http://tower:8001/zacus/welcome.wav"} Start playback
POST /stop Stop current playback
GET /status {"off_hook": bool, "playing": bool}

The Piper TTS server on Tower:8001 streams WAV; the audio task consumes it directly via the I2S library's connecttohost.

Build

cd PLIP_FIRMWARE
pio run                               # compile
pio run -t upload                     # flash over USB
pio device monitor                    # serial monitor (115200 8N1)

PlatformIO will pull espressif32@6.5.0 + Arduino framework + the audio library on first build (~3 min).

Pin map (dev kit)

Defined in platformio.ini build flags:

ES8388_ADDR  = 0x10   (I2C)
IIS_BCLK     = 27     (I2S bit clock)
IIS_LCLK     = 26     (I2S word select)
IIS_DSIN     = 25     (DAC in)
IIS_DOUT     = 35     (ADC out)
SD_CS        = 13
SD_MOSI      = 15
SD_MISO      = 2
SD_SCK       = 14
OFF_HOOK_GPIO = 4     (BOOT/KEY1 stand-in)

When the PCB lands: rewire to Si3210 SPI + INT, swap the codec init in audio_task, ring goes from "ES8388 beep" to "Si3210 ring control register". The build_flags file is the only place pins live; source code reads them via macros.

MVP scope (Phase 3 = b in the brainstorm)

  • Sonnerie (ring) on demand
  • Off-hook GPIO interrupt → notify master
  • MP3 playback from SD
  • WiFi REST endpoint (4 routes above)

Out of scope for v1:

  • Bluetooth Classic (A2DP sink + HFP) — the design doc specs it but the bringup focus is REST + SD first.
  • Si3210 driver — comes when the PCB arrives.
  • OTA via the desktop FirmwareManager — comes after the bringup loop is stable.

Roadmap

Step Goal Status
0 Skeleton (this commit) — tasks fire Serial.println only
1 I2C + ES8388 init in audio task; play a 1 kHz tone on demand TODO
2 SD card mount + MP3 file playback TODO
3 WiFi station + REST /ring /play /stop /status TODO
4 Off-hook button → REST status update TODO
5 HTTP stream from Tower:8001 Piper TTS TODO
6 NVS provisioning via desktop NvsConfigurator TODO
7 OTA via desktop FirmwareManager TODO
8 Port to Si3210 SLIC on the custom PCB TODO
9 Bluetooth A2DP sink (game-master streaming) TODO

Convert to a git submodule

The directory is currently inlined in the parent repo for convenience. Once you have a remote URL ready:

# 1. Create a separate repo for PLIP_FIRMWARE on your Git host
# 2. From this directory:
git init
git add .
git commit -m "Initial commit"
git remote add origin <url>
git push -u origin main

# 3. From the parent repo (le-mystere-professeur-zacus):
git rm -r PLIP_FIRMWARE
git submodule add <url> PLIP_FIRMWARE
git commit -m "chore: PLIP_FIRMWARE -> submodule"

This mirrors the existing ESP32_ZACUS/ submodule layout.