Files
DigiRadio/Software/docs/claude-code-audio-bringup.md
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

372 lines
15 KiB
Markdown
Raw Permalink 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.
# Claude Code — brief completo: audio bring-up DigiRadio
> **RISOLTO (2026-08-07):** audio confermato funzionante end-to-end su Bose.
> Causa finale: la cella DSP "Beep - variable gain" richiede sia `ENABLE`
> sia `KICK` per avviare la generazione — vedi
> `Adau1701Driver::setBeepEnabled()`. Il tono di test ora è generato
> dall'oscillatore interno del DSP ADAU1701, non più dall'ESP32; il codice
> `esp32_i2s_tone` e `CONFIG_ESP32_I2S_TONE_TEST` descritti in questo
> documento sono stati rimossi dal firmware. Resto del documento come
> riferimento storico del bring-up.
**Data:** 2026-08-05
**Repo:** `/Users/michelebigi/Documents/Develop/DigiRadio`
**Working dir firmware:** `Software/`
**Priorità:** far sentire audio sul **Bose Solo II** (test tono 440 Hz, poi FM)
---
## Prompt iniziale (copia-incolla in Claude Code)
```
Stai lavorando su DigiRadio (ESP32-S3 firmware in Software/).
CONTESTO
- Bring-up audio su scheda reale: Si4684 → ADAU1701 → FSC-BT1035 → Bose Solo II.
- Serial: /dev/cu.usbmodem1101
- Speaker BT salvato: Bose Solo II MAC BC87FAE69D6E
- WiFi: MikiLab, mDNS digiradio-CC4DB4.local
PROBLEMA
L'utente non sente NULLA sul Bose. I log seriali mostrano però:
- A2DPSTAT=4 (streaming OK) dopo AT+A2DPAUDIO=1
- esp32_i2s: I2S slave TX started
- esp32_i2s: i2s streaming 2048 bytes/frame to ADAU MP1
Quindi il firmware BT + I2S sembra OK; il silenzio potrebbe essere:
1) routing/mute ADAU (NVS profile sovrascrive mix),
2) formato I2S ESP32↔ADAU (24 vs 32 bit),
3) cablaggio I2S ADAU MP6 → BT1035,
4) Bose (sorgente BT errata, telefono in competizione).
LEGGI PRIMA
- Software/AGENTS.md
- Software/docs/claude-code-audio-bringup.md (questo file)
- Software/docs/audio-bringup-handoff.md
OBIETTIVO
1. Far sentire un tono 440 Hz sul Bose (CONFIG_ESP32_I2S_TONE_TEST=y).
2. Poi FM su 100.9 MHz (Massa) con applyRadioFirstMix.
VINCOLI
- NON committare senza richiesta esplicita.
- Modifiche minime, seguire convenzioni esistenti.
- Build: idf.py build da Software/
- Flash: idf.py -p /dev/cu.usbmodem1101 flash monitor
TASK SUGGERITI (in ordine)
A) Verificare log post-boot (~30s): cercare mix re-applied, i2s streaming, errori write.
B) Se software OK ma muto: aggiungere test alternativo — safeload beep interno ADAU o DC test su master volume (se possibile via parametri esistenti).
C) Avviare tone task SOLO da BluetoothService dopo "boot reconnect OK" (elimina race UART).
D) Provare entrambi i formati I2S (24-bit Philips vs 32-bit MSB<<8) con Kconfig o retry.
E) Verificare che IntegrationService::startup() non muti il mixer durante tone test (skip applyProfile se tone test attivo).
F) Documentare pinout I2S ADAU↔BT1035 da docs/manual/ e Hardware/ per debug utente.
Quando finisci: riassumi cosa hai cambiato, comando flash, log attesi.
```
---
## Setup laboratorio
| Parametro | Valore |
|-----------|--------|
| MCU | ESP32-S3 (USB-JTAG `/dev/cu.usbmodem1101`) |
| Serial monitor | 115200 baud |
| WiFi STA | `MikiLab` |
| IP / mDNS | ~`192.168.1.56`, `digiradio-CC4DB4.local` |
| Unit serial | `D8478FCC4DB4` |
| Speaker BT | **Bose Solo II**, nome AT `BOSE SOLO 2`, MAC **`BC87FAE69D6E`** |
| Area FM (Massa MS) | RTL **100900** kHz, RAI1 **95500**, Club FM **97300** |
| Build | ESP-IDF 5.5, C++23, target `esp32s3` |
---
## Catena audio
```
Si4684 I2S (slave) → ADAU1701 MP0 / Si4674 fader ─┐
ESP32 I2S (slave) → ADAU1701 MP1 / ESP32 fader ─┤→ St Mixer1 (ST0=Si4684, ST1=ESP32)
│ → Param EQ → master → MP6 SDATA_OUT0
└→ BT1035 I2S slave (AUXCFG=3, I2SCFG=67)
→ A2DP encode → Bose Solo II
```
| Ruolo | Chip | Note |
|-------|------|------|
| I2S master 48 kHz | ADAU1701 | BCLK/LRCLK su MP10/MP11 |
| I2S slave TX test | ESP32 | BCLK=GPIO6, WS=GPIO7, DOUT=GPIO16 → ADAU MP1 |
| I2S slave RX | BT1035 | Riceve da ADAU MP6 |
| FM tuner | Si4684 | I2S out verso ADAU MP0; richiede `I2SOUTEN` |
Documentazione SigmaStudio: `Software/docs/manual/ch-adau1701.tex`, export in `Software/Firmware/ADAU1701-Firmware/`.
---
## Stato attuale (2026-08-05 sera)
| Layer | Stato | Evidenza log |
|-------|-------|--------------|
| ADAU boot + program load | OK | `hw_boot: companion chips ready` |
| BT1035 I2S slave | OK | `AT+AUXCFG=3, AT+I2SCFG=67` |
| Reconnect Bose | OK | `reconnect saved speaker BC87FAE69D6E` |
| A2DP Connected (3) | OK | `A2DPSTAT=3` |
| **A2DP Streaming (4)** | **OK** | fix `AT+A2DPAUDIO=1` |
| Mix esp32-first | OK (con re-apply) | `ADAU esp32-first mix re-applied before tone` |
| ESP32 I2S tone 440 Hz | **Firmware OK** | `I2S slave TX started`, `i2s streaming 2048 bytes/frame` |
| **Audio al Bose** | **FALLITO** | Utente: *«no nulla»* |
| FM 100.9 MHz | Segnale assente | `rssi=0` (antenna wire) |
**Conclusione:** il collo di bottiglia non è più A2DP Connected-vs-Streaming. Prossimo focus: path **ADAU MP6 → BT1035** oppure formato/mix, o Bose lato utente.
---
## Modifiche firmware (non committate)
### A. Bluetooth — A2DP streaming (FIX PRINCIPALE, verificato)
**Root cause:** BT1035 restava su `A2DPSTAT=3` (Connected). Serve `AT+A2DPAUDIO=1` (Feasycom §5.3.6) per `A2DPSTAT=4` (Streaming).
| File | Modifica |
|------|----------|
| `components/core/include/core/Bt1035At.hpp` | `buildBt1035A2dpAudioLine(bool establish)` |
| `components/core/src/Bt1035At.cpp` | `AT+A2DPAUDIO=1\r\n` / `=0` |
| `components/core/test/bt1035_at_test.cpp` | Unit test linee AT |
| `components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp` | `startA2dpAudio()`, `waitForA2dpStreaming(int timeoutMs)` |
| `components/drivers/bt1035/src/Bt1035Driver.cpp` | Poll `A2DPSTAT`; retry `A2DPAUDIO=1` ogni 3 s su Connected; **no early return** se query fallisce |
| `components/services/bluetooth/src/BluetoothService.cpp` | `ensureA2dpStreaming()` dopo ogni connect/reconnect |
### B. ADAU1701 — mixer routing
| File | Modifica |
|------|----------|
| `components/core/include/core/MixerState.hpp` | `radioFirst()`, `esp32First()` |
| `components/core/src/MixerState.cpp` | ST0=Si4684, ST1=ESP32; mute path non usato a 96 dB |
| `components/services/audio/include/audio/AudioService.hpp` | `applyRadioFirstMix()`, `applyEsp32FirstMix()` |
| `components/services/audio/src/AudioService.cpp` | Safeload mixer + master unity |
| `main/hardware_bootstrap.cpp` | Dopo `loadAndApply()`: mix per Kconfig |
**Mapping SigmaStudio (St Mixer1):**
- `mixLeft` → ST0 (Si4684)
- `mixRight` → ST1 (ESP32)
**Bug scoperto:** `IntegrationService::startup()``recallPreset()``applyStoredAudioProfile()` può **sovrascrivere il mix** dopo `hw_boot`. Fix attuale: re-apply `applyEsp32FirstMix()` in `esp32I2sToneTask` prima di I2S. Fix migliore: skip apply profile durante tone test, o avviare tone da `BluetoothService` post-reconnect.
### C. Si4684 FM
| File | Modifica |
|------|----------|
| `components/drivers/si4684/src/Si4684Driver.cpp` | `AUDIO_OUTPUT_CONFIG` **I2SOUTEN** (0x0302=0x0002); unmute; vol 63; RSQ offsets AN649; tune STC fallback 150 ms |
| `components/drivers/si4684/src/Si4684Tuner.cpp` | Volume 63; READFREQ stale guard |
| `components/services/tuner/src/TunerService.cpp` | FM scan step +100 kHz; SNR≥10; chip freq check |
| `components/core/include/core/TunerStatus.hpp` | `fmChipReadFrequency` |
### D. Test tono ESP32 (bring-up dev)
| File | Modifica |
|------|----------|
| `main/Kconfig.projbuild` | `CONFIG_ESP32_I2S_TONE_TEST` (default y) |
| `main/esp32_i2s_tone.cpp` | 440 Hz sine, I2S slave, 24-bit Philips, mclk_multiple=384, amp 0.85 |
| `main/esp32_i2s_tone.hpp` | `bool start()` |
| `main/main.cpp` | Task `esp32_tone`: delay 15 s → wait A2DP → re-apply mix → `startA2dpAudio()` → I2S |
| `main/CMakeLists.txt` | Condizionale su Kconfig |
| `main/board_pins.hpp` | BCLK=6, WS=7, DOUT=16 |
| `sdkconfig.defaults` | `CONFIG_ESP32_I2S_TONE_TEST=y` |
| `sdkconfig` | `CONFIG_ESP32_I2S_TONE_TEST=y` (**critico** — se `# is not set` il tone non compila) |
### E. FM probe (alternativa, tone test OFF)
| File | Modifica |
|------|----------|
| `main/main.cpp` | `fm_probe` stack 16384; tune 100900 kHz; `applyRadioFirstMix` |
### F. Altri file dirty (sessioni precedenti, non audio-core)
Net/UI, secure store, BluetoothJson, SetupWebServer, station/tuner tests — vedi `git diff --name-only`.
---
## Boot sequence (ordine temporale)
```
app_main
→ HardwareBootstrap::boot()
→ ADAU program load
→ loadAndApply() ← profilo NVS
→ applyEsp32FirstMix() ← se tone test
→ BT1035 boot (AUXCFG=3)
→ Si4684 boot
→ integration.startup() ← può recall preset + applyProfile (SOVRASCRIVE MIX)
→ NetBootstrap
→ bluetoothService.startupReconnect() ← task async, A2DPSTAT→4
→ esp32I2sToneTask ← delay 15s, wait stream, re-apply mix, I2S start
```
---
## Build, flash, verifica
```bash
cd /Users/michelebigi/Documents/Develop/DigiRadio/Software
# Verifica tone test compilato
rg CONFIG_ESP32_I2S_TONE_TEST build/config/sdkconfig.h
# Atteso: #define CONFIG_ESP32_I2S_TONE_TEST 1
idf.py build
idf.py -p /dev/cu.usbmodem1101 flash monitor
```
**Disabilitare tone test (FM probe):**
```bash
# In sdkconfig: # CONFIG_ESP32_I2S_TONE_TEST is not set
idf.py fullclean reconfigure build flash
```
---
## Log attesi (tone test OK, ~30 s dopo reset)
```
I (hw_boot) ADAU1701 ESP32 I2S input routed (esp32-first mix)
I (Bt1035) I2S slave mode enabled (AT+AUXCFG=3, AT+I2SCFG=67)
I (BluetoothSvc) boot reconnect task started
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
I (digiradio) ==== ESP32 I2S tone test (440 Hz) — waiting A2DP streaming ====
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
```
**Errori già risolti (non devono riapparire):**
```
E (i2s_std) i2s_std_calculate_clock(68): sample rate is too large ← I2S_CLK_SRC_EXTERNAL errato
W (digiradio) A2DP not streaming after 90000 ms (a t=10s) ← race waitForA2dpStreaming
```
---
## Ipotesi da investigare (Claude Code)
### H1 — Hardware I2S ADAU → BT1035
Firmware invia PCM a BT1035 solo se ADAU MP6 ha clock+dati. Verificare:
- MP6 configurato come SDATA_OUT0 (SigmaStudio `MFSELECT6=0x4` in export)
- `REG_SERIALOUTREGISTER1 = 0x800` (output enabled)
- Cavo dati MP6 → pin I2S DIN del BT1035
### H2 — Formato I2S ESP32 → ADAU MP1
ADAU accetta 24-bit I2S. Firmware usa 24-bit Philips; provare anche 32-bit con sample `<< 8` se tono ancora muto.
### H3 — NVS profile muta mixer/enhancements
`IntegrationService` richiama `applyProfile(currentProfile())` che riapplica mixer da NVS. Durante tone test:
- Disabilitare recall preset audio, oppure
- `#if CONFIG_ESP32_I2S_TONE_TEST` skip in `IntegrationService::applyStoredAudioProfile`
### H4 — Bose non in playout
- Telefono connesso al Bose in parallelo
- Bose su input AUX invece di BT
- Volume Bose al minimo
### H5 — BT1035 I2S non sincronizzato
Con `A2DPSTAT=4` e I2S da ADAU: verificare se BT1035 richiede comandi aggiuntivi oltre `A2DPAUDIO=1` (leggere `docs/manual/ch-bt1035.tex`, manuale Feasycom).
### H6 — Master volume / enhancements
`applyProfile` applica EQ con enhancements overlay. Verificare che master gain non sia 96 dB in profilo NVS salvato.
---
## Task concreti per Claude Code (checklist)
- [ ] **T1** Leggere log fresh dopo flash; confermare sequenza sopra.
- [ ] **T2** Spostare avvio tone in callback `BluetoothService` post `boot reconnect OK` (elimina delay 15s + race UART).
- [ ] **T3** Guard tone test: skip `IntegrationService::applyStoredAudioProfile` quando `CONFIG_ESP32_I2S_TONE_TEST`.
- [ ] **T4** Aggiungere Kconfig `ESP32_I2S_TONE_FORMAT_32BIT` per A/B test formato senza rebuild manuale.
- [ ] **T5** Log `queryA2dpState` + `A2DPENC` / volume BT1035 se esiste comando AT.
- [ ] **T6** Test senza ESP32: `applyRadioFirstMix` + Si4684 tone/noise (se RSSI>0 con antenna) per isolare BT path.
- [ ] **T7** Aggiornare `docs/audio-bringup-handoff.md` con esito.
---
## File chiave (aprire per primi)
```
Software/main/main.cpp
Software/main/esp32_i2s_tone.cpp
Software/main/hardware_bootstrap.cpp
Software/main/board_pins.hpp
Software/main/Kconfig.projbuild
Software/components/services/bluetooth/src/BluetoothService.cpp
Software/components/drivers/bt1035/src/Bt1035Driver.cpp
Software/components/core/src/Bt1035At.cpp
Software/components/services/audio/src/AudioService.cpp
Software/components/core/src/MixerState.cpp
Software/components/drivers/adau1701/src/Adau1701Driver.cpp
Software/components/services/integration/src/IntegrationService.cpp
Software/components/drivers/si4684/src/Si4684Driver.cpp
Software/Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h
Software/sdkconfig
Software/sdkconfig.defaults
```
---
## Regole progetto
- Leggere `Software/AGENTS.md` e `Software/.cursor/rules/` prima di ogni modifica.
- **Non committare** senza richiesta utente.
- Tone test = solo dev; produzione usa `sdkconfig.defaults.production` senza tone test.
- Host tests: `cmake --build build-host && ctest` (se tocchi core/bt1035).
---
## Comandi debug rapidi
```bash
# Monitor filtrato
idf.py -p /dev/cu.usbmodem1101 monitor 2>&1 | rg -i 'A2DP|esp32_i2s|hw_boot|tone|streaming|error|mix'
# Reset + 90s capture
python3 <<'PY'
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:
line = s.readline()
if line: print(line.decode(errors="replace"), end="")
PY
# Host unit test BT1035 AT
cd Software && cmake -B build-host -DBUILD_HOST_TESTS=ON && cmake --build build-host && ctest --test-dir build-host -R bt1035
```
---
## Documenti correlati
| File | Contenuto |
|------|-----------|
| `Software/docs/claude-code-audio-bringup.md` | **Questo file** — brief operativo Claude Code |
| `Software/docs/audio-bringup-handoff.md` | Handoff tecnico sessione Cursor |
| `Software/docs/manual/ch-adau1701.tex` | ADAU boot, safeload, signal chain |
| `Software/docs/manual/ch-bt1035.tex` | BT1035 I2S, AUXCFG, pairing |
| `Software/AGENTS.md` | Regole agenti, build, DoD |
---
## Storia sessione Cursor
- Thread: audio/FM bring-up, ID `d10654e5-d27a-4999-812e-f371dc48567a`
- Subagent [Fix no audio path](8719ea5e-ee61-48fe-ba57-7298b5638cf9): fix `AT+A2DPAUDIO=1` + tone test scaffold
- Follow-up Cursor: sdkconfig tone test, fix I2S slave clock, mix re-apply, flash multipli
- Utente conferma silenzio persistente nonostante log software OK → sospetto hardware o mix/NVS