Files
DigiRadio/Software/docs/audio-bringup-handoff.md
T
micheleandClaude Sonnet 5 6f7b6dd12c Add internet radio streaming with runtime API, modernize web UI, remove auto-tune/beep at boot
Streaming (main feature this session):
- New WebRadioConfig/WebRadioJson core types, ISecureStore-backed persistence
- New webradio::WebRadioService (thread-safe live config) + GET/POST /api/streaming
- web_radio_stream task now runtime-toggleable (no reboot), no hardcoded URL
- Content-Type diagnostic: warns clearly when a URL is a webpage, not an audio stream

Boot cleanup:
- Removed boot-time auto FM/DAB tune, auto-beep, and the (now-concluded) Si4684
  crystal IBIAS/CTUN empirical sweep from main.cpp — tuning/beep are on-demand
  via the existing REST API only

Web UI:
- Modernized styling (cards, gradients, toggle switches, light/dark theme)
- New Stream tab wired to /api/streaming

Fixes found via real idf.py build (not just clangd):
- Restored wrongly-removed si4684/Si4684Tuner.hpp include in main.cpp
- Fixed MP3Decode() argument types in web_radio_stream.cpp (unsigned char**/int*)

Quality-gate fixes:
- Host-test stub headers (esp_log.h, freertos/*) so TunerService.cpp's
  scanForStation logging/pacing compiles for station_service_test /
  integration_service_test instead of running stale binaries
- Added WifiScanner and WebRadioService manual sections; filled in missing
  Doxygen docs on BluetoothService, i2s_sdata_probe, test_firmware, Bt1035At
- Ignore clangd's .cache/ index directory

Also includes prior uncommitted work carried in the tree: Wi-Fi/Bluetooth
device scan REST API and UI (WifiScanner, BT scan), SigmaStudio TCP bridge,
and the current ADAU1701 SigmaStudio DSP program export.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-08 21:15:22 +02:00

299 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Audio bring-up — handoff per Claude Code
> **RISOLTO (2026-08-07):** audio confermato funzionante end-to-end su Bose
> (Si4684/ESP32 → ADAU1701 → BT1035 → A2DP). Causa finale del silenzio:
> la cella DSP "Beep - variable gain" (toolbox ADI Sound Generation) usata
> come tono di test richiede sia `ENABLE` **sia** `KICK` (il parametro
> trigger) per avviare davvero la generazione — scrivere solo `ENABLE`
> lasciava il generatore silenzioso nonostante tutto il resto della catena
> (clock I2S, `AT+I2SCFG=35` a 24-bit, `AT+A2DPAUDIO`, encoder SBC attivo)
> fosse già corretto. Vedi `Adau1701Driver::setBeepEnabled()`.
>
> Il tono di test 440 Hz ora è generato dall'oscillatore interno del DSP
> ADAU1701 (programma SigmaStudio), non più dall'ESP32. Il codice
> `esp32_i2s_tone` e `CONFIG_ESP32_I2S_TONE_TEST` descritti in questo
> documento sono stati rimossi dal firmware; il resto del documento resta
> come riferimento storico del bring-up.
**Data:** 2026-08-05
**Contesto:** bring-up hardware DigiRadio su scheda reale (ESP32-S3 + Si4684 + ADAU1701 + FSC-BT1035 → Bose Solo II).
**Problema utente:** *non si sente* nulla sul Bose nonostante connessione Bluetooth.
**Stato:** fix A2DP streaming applicati e verificati parzialmente; test tono ESP32 ancora da confermare a orecchio; FM debole senza antenna.
---
## Setup laboratorio
| Parametro | Valore |
|-----------|--------|
| Serial | `/dev/cu.usbmodem1101` |
| WiFi STA | `MikiLab` |
| IP / mDNS | ~`192.168.1.56`, `digiradio-CC4DB4.local` |
| Speaker BT | Bose Solo II, MAC `BC87FAE69D6E` |
| Area FM test | Massa (MS) — RTL **100.9 MHz**, RAI1 **95.5**, Club FM **97.3** |
| Working dir build | `Software/` |
---
## Catena audio (SigmaStudio / hardware)
```
Si4684 I2S (slave) → ADAU1701 MP0 (fader Si4674) ─┐
ESP32 I2S (slave) → ADAU1701 MP1 (fader ESP32) ─┤→ St Mixer1 → EQ → master → MP6 out
└→ BT1035 I2S slave (AUXCFG=3) → A2DP → Bose
```
- **ADAU1701** = I2S **master** @ 48 kHz
- **ESP32 / BT1035 / Si4684** = I2S **slave**
- GPIO I2S ESP32: `BCLK=6`, `LRCLK/WS=7`, `DOUT=16` → ADAU MP1
---
## Modifiche effettuate (non committate)
Tutte le modifiche sono su branch `main`, **working tree dirty** (~48 file toccati in totale, molti pre-esistenti + sessione audio).
### 1. Bluetooth — A2DP streaming (causa principale del silenzio)
**Problema:** il BT1035 restava su `+A2DPSTAT=3` (Connected) senza passare a `4` (Streaming). Senza streaming il modulo non invia audio I2S→A2DP al Bose.
**Fix Feasycom §5.3.6:** dopo la connessione serve `AT+A2DPAUDIO=1`.
| File | Modifica |
|------|----------|
| `components/core/include/core/Bt1035At.hpp` | Doc + `buildBt1035A2dpAudioLine()` |
| `components/core/src/Bt1035At.cpp` | `AT+A2DPAUDIO=1/0` |
| `components/core/test/bt1035_at_test.cpp` | Test linee A2DPAUDIO |
| `components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp` | `startA2dpAudio()`, `waitForA2dpStreaming()` |
| `components/drivers/bt1035/src/Bt1035Driver.cpp` | Poll A2DPSTAT; invia A2DPAUDIO=1 se bloccato su Connected; **non uscire subito** se query fallisce (retry fino a timeout) |
| `components/services/bluetooth/src/BluetoothService.cpp` | `ensureA2dpStreaming()` dopo connect/reconnect; boot reconnect attende STAT=4 |
**Verifica log (OK):**
```
I (Bt1035) A2DP audio start (AT+A2DPAUDIO=1)
I (Bt1035) stream wait: A2DPSTAT=4
I (BluetoothSvc) A2DP streaming OK
```
### 2. ADAU1701 — routing mixer boot
| File | Modifica |
|------|----------|
| `components/core/src/MixerState.cpp` | `radioFirst()` / `esp32First()` — selezione sorgente ST0/ST1 |
| `components/services/audio/src/AudioService.cpp` | `applyRadioFirstMix()` / `applyEsp32FirstMix()` |
| `main/hardware_bootstrap.cpp` | Dopo `loadAndApply()`: mix in base a Kconfig |
Con `CONFIG_ESP32_I2S_TONE_TEST=y`:
```
I (hw_boot) ADAU1701 ESP32 I2S input routed (esp32-first mix)
```
Senza tone test (FM):
```
I (hw_boot) ADAU1701 Si4684 input routed (radio-first mix)
```
### 3. Si4684 FM — I2S out + tune/scan
| File | Modifica |
|------|----------|
| `components/drivers/si4684/src/Si4684Driver.cpp` | `AUDIO_OUTPUT_CONFIG` bit I2SOUTEN @ boot; unmute; volume max; offset RSQ AN649; tune con fallback STC; band FM 87500107900 kHz |
| `components/drivers/si4684/src/Si4684Tuner.cpp` | Volume default 63; `readStatus()` non sovrascrive freq con READFREQ stale |
| `components/services/tuner/src/TunerService.cpp` | Scan FM step +100 kHz; filtro SNR; `fmChipReadFrequency` in status |
| `components/core/include/core/TunerStatus.hpp` | Campo `fmChipReadFrequency` |
**Nota:** con filo antenna corto, `rssi=0` su 100.9 MHz è normale — non indica per forza bug software.
### 4. Test tono ESP32 → ADAU → BT (isolamento path)
| File | Modifica |
|------|----------|
| `main/Kconfig.projbuild` | `ESP32_I2S_TONE_TEST` (default y in Kconfig, disabilitare in produzione) |
| `main/esp32_i2s_tone.cpp` | Sinusoide 440 Hz, 48 kHz, sample 24-bit MSB in slot 32-bit, amp 0.5 |
| `main/esp32_i2s_tone.hpp` | API `start()` |
| `main/main.cpp` | Task `esp32_tone`: delay 15 s (lascia finire boot reconnect), poi `waitForA2DPStreaming`, poi I2S |
| `main/CMakeLists.txt` | Compila `esp32_i2s_tone.cpp` se Kconfig attivo |
| `sdkconfig.defaults` | `CONFIG_ESP32_I2S_TONE_TEST=y` |
| `sdkconfig` | `CONFIG_ESP32_I2S_TONE_TEST=y` (abilitato manualmente — prima era `# is not set` e il tone test **non veniva compilato**) |
### 5. FM probe boot (alternativa al tone test)
| File | Modifica |
|------|----------|
| `main/main.cpp` | `fm_probe` task stack **16384** (era 4096 → stack overflow); tune 100.9 MHz, volume 63 |
Attivo solo se `CONFIG_ESP32_I2S_TONE_TEST` è **disabilitato**.
---
## Build e flash
```bash
cd Software
# Tone test ON (bring-up attuale)
idf.py build
idf.py -p /dev/cu.usbmodem1101 flash monitor
# FM probe al posto del tone test
idf.py -DCONFIG_ESP32_I2S_TONE_TEST=n fullclean reconfigure build flash
```
**Attenzione:** `-DCONFIG_ESP32_I2S_TONE_TEST=y` da riga di comando **non basta** se `sdkconfig` esistente ha `# CONFIG_ESP32_I2S_TONE_TEST is not set`. Verificare:
```bash
rg CONFIG_ESP32_I2S_TONE_TEST build/config/sdkconfig.h
# deve mostrare: #define CONFIG_ESP32_I2S_TONE_TEST 1
```
Ultimo build riuscito: `digiradio.bin` ~`0x237fe0` bytes.
---
## Log osservati (sessione 2026-08-05 sera)
### A2DP — OK dopo fix
```
I (BluetoothSvc) reconnect saved speaker BC87FAE69D6E (BOSE SOLO 2)
I (Bt1035) A2DP audio start (AT+A2DPAUDIO=1)
I (Bt1035) stream wait: A2DPSTAT=4
I (BluetoothSvc) A2DP streaming OK
```
A volte compare brevemente `A2DPSTAT=5` prima del `4`.
### Tone test — problemi I2S (in corso)
**Primo flash (tone test non compilato):** partiva `fm_probe`, mix radio-first, nessun log `esp32_i2s`.
**Secondo flash (tone test compilato):**
1. Race: tone task partiva insieme al reconnect → `waitForA2dpStreaming` usciva subito (query fallita) → tono partiva **prima** dello streaming.
2. I2S init falliva:
```
E (i2s_std) i2s_std_calculate_clock(68): sample rate is too large
E (esp32_i2s) i2s_channel_init_std_mode failed
```
Causa: `I2S_CLK_SRC_EXTERNAL` senza `ext_clk_freq_hz` coerente con `bclk_div`.
**Fix applicati (post [Fix no audio path](8719ea5e-ee61-48fe-ba57-7298b5638cf9)):**
- Rimosso `I2S_CLK_SRC_EXTERNAL`; slave usa BCLK/LRCLK da ADAU con `clk_src` default.
- `waitForA2dpStreaming`: continua a pollare se query fallisce (no early exit).
- Tone task: **delay 15 s** prima di attendere A2DP (evita race con boot reconnect).
- **Re-apply `applyEsp32FirstMix()`** + `startA2dpAudio()` subito prima del tono (NVS/integration sovrascriveva il mix al boot).
- I2S **24-bit** Philips + `mclk_multiple=384`; ampiezza tono 0.85.
- `sdkconfig`: `CONFIG_ESP32_I2S_TONE_TEST=y` obbligatorio (altrimenti tone test non compilato).
**Log verificati su serial (2026-08-05 ~20:15, I2S OK ma Bose silenzioso):**
```
I (Bt1035) stream wait: A2DPSTAT=4
I (BluetoothSvc) A2DP streaming OK
I (digiradio) A2DP streaming — starting I2S tone
I (digiradio) ADAU esp32-first mix re-applied before tone
I (esp32_i2s) I2S slave TX started (BCLK=6 WS=7 DOUT=16)
I (esp32_i2s) tone task: 440 Hz sine, 48000 Hz, ADAU I2S slave TX
I (esp32_i2s) i2s streaming 2048 bytes/frame to ADAU MP1
```
### FM (senza tone test)
```
I (digiradio) FM audio probe OK: 100900 kHz rssi=0 dBuV snr=0 dB locked=0
```
Segnale assente/ debole — serve antenna.
---
## Dove siamo arrivati
| Componente | Stato |
|------------|-------|
| Boot ADAU + profilo NVS | OK |
| BT1035 I2S slave (`AUXCFG=3`, `I2SCFG=67`) | OK |
| Reconnect Bose saved speaker | OK |
| **A2DP Streaming (STAT=4)** | **OK** — fix `AT+A2DPAUDIO=1` ([Fix no audio path](8719ea5e-ee61-48fe-ba57-7298b5638cf9)) |
| Mix ADAU esp32-first / radio-first | OK; **re-apply prima del tono** necessario (NVS) |
| **Test tono 440 Hz — firmware** | **OK** (I2S init + write verificati in log) |
| **Test tono 440 Hz — Bose** | **Silenzio** — probabile hardware ADAU MP6 → BT1035 o sorgente BT Bose |
| FM audio su Bose | Non testabile finché tono non si sente o RSSI > 0 |
| Utente | *«no nulla»* — silenzio persistente nonostante log software OK |
---
## Prossimi passi consigliati (priorità)
1. **Se log mostrano `A2DPSTAT=4` + `i2s streaming … bytes/frame` ma Bose muto** → debug **hardware**:
- Oscilloscopio: BCLK/LRCLK GPIO 6/7; dati MP6 ADAU → BT1035; dati GPIO 16 → ADAU MP1.
- Bose: sorgente BT = DigiRadio, telefono scollegato, volume alto.
- Verificare saldature/cavi I2S tra ADAU e BT1035 (path dopo DSP).
2. **Se I2S init fallisce ancora**
- Provare `I2S_DATA_BIT_WIDTH_24BIT` + slot 32-bit (allineamento ADAU MP1).
- Provare `bclk_div` 4 o 12 (vedi test ESP-IDF `i2s_multi_dev` slave).
- Verificare con oscilloscopio BCLK/LRCLK su GPIO 6/7 quando ADAU è bootato (ADAU deve essere master attivo prima di `i2s_channel_enable`).
3. **Se tono OK ma FM no**
- Tornare a build senza tone test (`CONFIG_ESP32_I2S_TONE_TEST=n`).
- Antenna FM; verificare `I2SOUTEN` Si4684 e `applyRadioFirstMix`.
- Cercare `rssi>15`, `locked=1`.
4. **Se A2DPSTAT=4 ma silenzio totale**
- Hardware: MP6 ADAU → pin I2S BT1035; alimentazione Bose.
- Verificare master volume / mute ADAU (safeload profilo — non dovrebbe essere a −∞).
5. **Commit** — non richiesto dall'utente finora; quando pronto, separare almeno:
- BT A2DPAUDIO / streaming
- Si4684 I2SOUTEN + scan/tune
- Tone test Kconfig (dev-only)
---
## File chiave (quick reference)
```
main/main.cpp — fm_probe vs esp32_tone task
main/hardware_bootstrap.cpp — mix ADAU al boot
main/esp32_i2s_tone.cpp — generatore tono I2S slave
main/Kconfig.projbuild — ESP32_I2S_TONE_TEST
sdkconfig / sdkconfig.defaults — Kconfig effettivo
components/drivers/bt1035/ — A2DPAUDIO, waitForA2dpStreaming
components/services/bluetooth/ — ensureA2dpStreaming al reconnect
components/services/audio/ — applyRadioFirstMix / applyEsp32FirstMix
components/core/MixerState.cpp — preset mixer ST0/ST1
components/drivers/si4684/ — I2SOUTEN, tune, RSQ
components/services/tuner/ — FM scan step tune
```
---
## Note per Claude Code
- Leggere `AGENTS.md` e `.cursor/rules/` prima di modifiche.
- **Non committare** salvo richiesta esplicita utente.
- Il tone test è **solo bring-up** — disabilitare per produzione (`sdkconfig.defaults.production` o `CONFIG_ESP32_I2S_TONE_TEST=n`).
- Due task competono sulla UART BT1035 (reconnect + tone wait): il delay 15 s mitiga ma non elimina del tutto la contesa; alternativa migliore: avviare il tone **dal callback** post-`boot reconnect OK` in `BluetoothService`.
- Transcript conversazione Cursor: thread audio/FM, ID `d10654e5-d27a-4999-812e-f371dc48567a`.
---
## Comandi utili debug
```bash
# Monitor filtrato
idf.py -p /dev/cu.usbmodem1101 monitor | rg -i 'A2DP|esp32_i2s|hw_boot|tone|streaming|error'
# Verifica config tone test nel binario
rg CONFIG_ESP32_I2S_TONE_TEST build/config/sdkconfig.h
# Reset + cattura serial (python)
python3 -c "
import serial, time
s=serial.Serial('/dev/cu.usbmodem1101',115200,timeout=0.2)
s.dtr=False; s.rts=True; time.sleep(0.1); s.rts=False
t=time.time()
while time.time()-t<90:
l=s.readline()
if l: print(l.decode(errors='replace'), end='')
"
```