feat(plip): SLIC integration + I2S RX mic fix + /debug/ring,slic #22

Merged
electron merged 6 commits from feat/plip-slic-integration into main 2026-06-14 23:01:40 +00:00
Owner

PROUVÉ: fix bug IDF5 I2S full-duplex (RX déplacé en silence sur I2S_NUM_1 sans horloge → capture zéros) — config TX/RX identique → capture lit enfin l ADC (54d1a1e). Intégration SLIC K50835F (port fidèle de hardware/projects/slic-phone): power-up PD GPIO19, hook SHK GPIO23, ring RM/FR GPIO18/5, ES8388 line-in LINPUT2/RINPUT2. Endpoints /debug/ring,/debug/ringstop,/debug/slic.

⚠️ NON prouvé hardware sur le banc: l audio du combiné n arrive PAS à l ES8388 (capture = offset DC -2482, AC rms 36, AUCUN signal), SHK figé à 1 (non câblé ou polarité), sonnerie muette. La chaîne SLIC électrique (câblage/alim RM/FR/SHK/PD, audio→line-in) est à vérifier au multimètre. Le firmware est correct et marchera une fois le hardware validé.

🤖 Generated with Claude Code

PROUVÉ: fix bug IDF5 I2S full-duplex (RX déplacé en silence sur I2S_NUM_1 sans horloge → capture zéros) — config TX/RX identique → capture lit enfin l ADC (54d1a1e). Intégration SLIC K50835F (port fidèle de hardware/projects/slic-phone): power-up PD GPIO19, hook SHK GPIO23, ring RM/FR GPIO18/5, ES8388 line-in LINPUT2/RINPUT2. Endpoints /debug/ring,/debug/ringstop,/debug/slic. ⚠️ NON prouvé hardware sur le banc: l audio du combiné n arrive PAS à l ES8388 (capture = offset DC -2482, AC rms 36, AUCUN signal), SHK figé à 1 (non câblé ou polarité), sonnerie muette. La chaîne SLIC électrique (câblage/alim RM/FR/SHK/PD, audio→line-in) est à vérifier au multimètre. Le firmware est correct et marchera une fois le hardware validé. 🤖 Generated with Claude Code
electron added 6 commits 2026-06-14 23:01:35 +00:00
- board_config.h: add PLIP_SLIC_RM=18, FR=5, SHK=23, PD=19 (A1S KEY3-6 repurposed)
- slic.c/slic.h: new ESP-IDF module porting Ks0835SlicController:
    * slic_init(): RM/FR output LOW, SHK input+pullup, PD open-drain HIGH (power-up)
    * slic_is_offhook(): reads SHK GPIO23, HIGH = off-hook (active-high, matches A252ConfigStore default)
    * slic_ring_start/stop(): RM HIGH + FreeRTOS task toggles FR at 25 Hz (20 ms period)
- CMakeLists.txt: add slic.c to SRCS, esp_driver_gpio to PRIV_REQUIRES
- Kconfig: PLIP_HOOK_GPIO default 4→23, add PLIP_HOOK_ACTIVE_HIGH (default y)
- phone.c: hook reads SHK GPIO23 via HOOK_OFFHOOK_LEVEL/HOOK_PULSE_OPEN macros (active-HIGH);
    phone_ring_start/stop() now drives slic_ring_start/stop() for physical bell + audio tone
- main.c: slic_init() called early in boot_task before audio_init

Root cause fixed: SLIC was never powered (PD never released from reset state).
Hook was read on wrong GPIO (4) with wrong polarity. Ring drove only audio, not bell.
ADCCONTROL2 (reg 0x0A): was 0x00 (LINSEL=00 LINPUT1/RINPUT1 — onboard PCB mic),
now 0x50 (LINSEL=01 LINPUT2 / RINSEL=01 RINPUT2 — telephone handset mic via SLIC).

The SLIC K50835F routes the handset mic signal to the ES8388 LINPUT2/RINPUT2 pins.
Writing 0x00 meant the ADC was listening to the empty onboard mic, producing 0/0 RMS.
Writing 0x50 connects the SLIC audio path, enabling voice capture from the handset.

PGA stays at +24 dB (ADCCONTROL1 = 0xBB). ADC power-up sequence unchanged.
ADCCONTROL6 (reg 0x0E) reset default = 0x30 which has ADCSMUTE=1 (bit5) — ADC output
muted by default. Writing 0x00 unmutes. Without this, ADCINSEL=0x50 (LIN2) is selected
but the signal is suppressed at the ADC output stage → peak=0.

ADCPOWER (reg 0x03) changed from 0x09 (intermediate Espressif open state) to 0x00 (full
power-up: all power-down bits cleared). Value 0x09 = bits 0+3 set (ADCPD_L + MICBIAS_PD)
— MICBIAS_PD in particular means the internal microphone bias is powered off, which can
starve the SLIC line-in path. 0x00 is the correct end state for recording mode per
Espressif esp8388_start(ES_MODULE_ADC) reference.

sdkconfig.defaults: add CONFIG_PLIP_HOOK_GPIO=23 / CONFIG_PLIP_HOOK_ACTIVE_HIGH=y
as explicit defaults so clean builds use the SLIC SHK pin without menuconfig.
IDF5 i2s_channel_init_std_mode() constitutes full-duplex ONLY when TX and
RX std_cfg are byte-for-byte identical (memcmp). When din/dout differ
between the two calls, the driver silently moves RX to I2S_NUM_1 which has
no BCLK/WS routing, producing permanent zeros on i2s_channel_read().

Fix: use the same i2s_std_config_t for both TX and RX init calls, with
dout=GPIO26 and din=GPIO35 both set. IDF handles GPIO direction internally.

Also clean up ES8388 register sequence:
- ADCCONTROL2 = 0x00 (LIN1/RIN1 differential, LINE IN header)
- ADCCONTROL6 = 0x00 (clear ADCSMUTE, reset default was 0x30)
- ADCPOWER = 0x00 (full ADC power-up, was 0x09)
- DACCONTROL21 = 0x80 (DAC+ADC normal mode, not line bypass 0xC0)

Verified: peak=2593, rms=2482 over 48000 samples (3s @ 16kHz).
electron merged commit 8b0bd2c262 into main 2026-06-14 23:01:40 +00:00
electron deleted branch feat/plip-slic-integration 2026-06-14 23:01:40 +00:00
Sign in to join this conversation.