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>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Documentazione Integrazione FSC-BT1035 - Sistema DigiRadio
|
||||
|
||||
## 1. Introduzione
|
||||
Il modulo FSC-BT1035 (basato su chipset Qualcomm QCC3056) è un modulo Bluetooth Dual-Mode. Nell'ecosistema DigiRadio, agisce come bridge wireless per l'audio e i dati seriali (SPP/GATT).
|
||||
|
||||
## 2. Architettura di Sistema
|
||||
Il modulo si collega al controller principale DigiRadio tramite:
|
||||
- **UART (Baudrate 115200 8N1)**: Comando e controllo tramite AT Commands.
|
||||
- **Audio Interface**: Uscita differenziale (SPK_P/N) per il sistema di amplificazione o I2S per processori DSP esterni (come l'ADAU1701 presente nel vostro sistema).
|
||||
- **Controllo Hardware**: Pin `SYS_CTRL` per il Power-On e `RESET` per il riavvio hardware.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configurazione e Setup del Modulo
|
||||
|
||||
## 1. Parametri di Default
|
||||
- **Nome BR/EDR**: FSC-BT1035
|
||||
- **Nome LE**: FSC-BT1035-LE
|
||||
- **Baudrate**: 115200
|
||||
|
||||
## 2. Inizializzazione via UART
|
||||
Al boot, inviare i comandi per impostare l'ambiente:
|
||||
1. `AT+RESTORE`: (Opzionale) Ripristina impostazioni di fabbrica.
|
||||
2. `AT+PROFILE=BITMASK`: Selezionare i profili necessari (Somma i valori binari: SPP=1, GATT_S=2, A2DP_S=32). Esempio: `AT+PROFILE=33` (SPP+A2DP).
|
||||
3. `AT+NAME=DigiRadio_V1,0`: Imposta nome dispositivo senza suffisso MAC.
|
||||
4. `AT+REBOOT`: Applicare i cambiamenti.
|
||||
|
||||
## 3. Verifica stato
|
||||
Monitorare il pin `LEDO` (Pin 17) che indica lo stato di pairing (onda quadra durante scanning, alto a connessione stabilita).
|
||||
@@ -0,0 +1,16 @@
|
||||
# Riferimento Rapido Comandi AT
|
||||
|
||||
## Comandi Generali
|
||||
- `AT+VER`: Query versione firmware (es: `+VER=BT1035,V2.6.1...`).
|
||||
- `AT+ADDR`: Lettura MAC Address (necessario per pairing forzato).
|
||||
- `AT+STAT`: Query globale stati profili (DEV, SPP, GATT, HFP, A2DP, AVRCP).
|
||||
|
||||
## Audio e Connessione
|
||||
- `AT+A2DPCONN=[MAC]`: Connessione a specifica sorgente.
|
||||
- `AT+A2DPAUDIO=1`: Apre flusso audio.
|
||||
- `AT+SPPSEND=[LEN],[DATA]`: Invio dati via SPP (max payload 236 byte).
|
||||
|
||||
## Eventi Asincroni
|
||||
Il modulo risponde con eventi preceduti da `+`. Esempi:
|
||||
- `+SPPDATA=[LEN],[PAYLOAD]`: Ricezione dati seriali.
|
||||
- `+A2DPSTAT=4`: Streaming A2DP attivo.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Note Hardware per DigiRadio
|
||||
|
||||
## 1. Gestione Alimentazione
|
||||
- **VCHG/VBAT**: Il modulo supporta ingressi da 3.0V a 4.2V.
|
||||
- **SYS_CTRL**: Richiede un impulso di >20ms per avviare il power-up da stato DORMANT/OFF.
|
||||
|
||||
## 2. Integrazione ADAU1701 (DSP)
|
||||
Se il DigiRadio utilizza l'ADAU1701 come processore audio:
|
||||
- Collegare le uscite `SPK_P/N` del modulo agli ingressi analogici dell'ADAU1701 (tramite filtro passivo come descritto nel datasheet ADAU1701).
|
||||
- In alternativa, utilizzare l'interfaccia I2S (Pin 4-7 del BT1035) per un collegamento digitale diretto, garantendo che i clock (BCLK, LRCLK) siano configurati in modalità coerente (BT1035 come Master/Slave a seconda del setup dell'ADAU).
|
||||
@@ -0,0 +1,6 @@
|
||||
# Diagnostica e Risoluzione Problemi
|
||||
|
||||
1. **Modulo non risponde**: Verificare i livelli logici RX/TX. Il modulo BT1035 richiede TX e RX stabili. Controllare se il Pin `SYS_CTRL` è alto.
|
||||
2. **Audio distorto**: Verificare che l'impedenza di carico sui pin `SPK_P/N` sia conforme e che il volume digitale (`AT+SPKVOL`) non sia in clipping.
|
||||
3. **Mancato Pairing**: Verificare `AT+PAIR=1`. Assicurarsi che il modulo non sia già connesso a un altro dispositivo.
|
||||
4. **Terminazione Comandi**: Ogni comando deve terminare con `0x0D` (CR) e `0x0A` (LF).
|
||||
Binary file not shown.
+13
@@ -0,0 +1,13 @@
|
||||
# Manuale di Integrazione Hardware FSC-BT1035
|
||||
|
||||
## 1. Architettura di Connessione
|
||||
Il modulo FSC-BT1035 deve essere integrato seguendo rigorosamente le indicazioni di Feasycom per evitare degrado del segnale RF:
|
||||
- **Pin 34 (SYS_CTRL)**: Richiede un segnale di accensione (>20ms). È consigliato un circuito di controllo tramite il controller DigiRadio.
|
||||
- **Pin 16 (BT_RTS/PIO2)**: Utilizzato nativamente per il MUTE degli stadi di potenza. Integrare questa funzione nel sistema di amplificazione del DigiRadio.
|
||||
- **Audio I/O**: Le uscite `SPK_P/N` sono differenziali. Per il collegamento al DSP ADAU1701:
|
||||
- Utilizzare uno stadio di ingresso con op-amp (es. AD8608 come da datasheet ADAU1701) per convertire il segnale differenziale in singolo (o mantenere il bilanciamento se l'ingresso DSP lo permette).
|
||||
- Impedenza: Assicurarsi di rispettare i filtri RC per il passaggio basso (corner 50kHz) per eliminare il noise del codec Bluetooth.
|
||||
|
||||
## 2. Note di Layout (Critiche)
|
||||
- **Clearance Antenna**: Rispettare rigorosamente l'area di "Keep Out" (5mm clearance) attorno all'antenna PCB integrata, come indicato nel documento `FSC-BT1035 Datasheet` (sezione 9).
|
||||
- **Grounding**: Utilizzare una via stitching densa attorno al modulo per connettere i piani di massa e prevenire leakage RF nel PCB del DigiRadio.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Guida alla Configurazione Firmware
|
||||
|
||||
## 1. Analisi file DigiRadio.params
|
||||
Il file `DigiRadio.params` analizzato suggerisce parametri di inizializzazione specifici per l'interfacciamento del modulo. Durante il boot, il controller DigiRadio deve:
|
||||
1. Inviare `AT+VER` per identificare la versione del modulo (es. V2.6.1).
|
||||
2. Caricare i profili: `AT+PROFILE=341` (combinazione SPP, GATT Server, GATT Client, A2DP Source, ecc.).
|
||||
3. Configurare la modalità I2S se il flusso audio è digitale (`AT+I2SCFG`):
|
||||
- Impostare `Param=67` per I2S Slave, 48kHz, 32-bit (o come richiesto dall'ADAU1701).
|
||||
|
||||
## 2. Gestione Profili
|
||||
Il modulo supporta le topologie miste. È possibile mantenere simultaneamente una connessione A2DP (Audio Source) e una GATT (Data).
|
||||
- Nota: Per il passaggio tra le modalità, interrogare sempre lo stato tramite `AT+STAT` prima di tentare nuove connessioni.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Deep Dive Comandi AT e Gestione Eventi
|
||||
|
||||
## 1. Protocollo Comunicazione
|
||||
Il modulo risponde in formato ASCII. Ogni stringa inizia con `
|
||||
` (0x0D 0x0A).
|
||||
- Esempio Parsing:
|
||||
Se il controller riceve `+SPPDATA=10,ABCDEFGHIJ`, il parser deve estrarre il valore `10` come lunghezza e gestire il buffer dei successivi 10 caratteri.
|
||||
|
||||
## 2. Throughput Mode (TPMODE)
|
||||
Per applicazioni ad alto volume dati (es. aggiornamento firmware del DigiRadio via Bluetooth):
|
||||
1. Inviare `AT+TPMODE=1`.
|
||||
2. A questo punto, il modulo cessa di interpretare i comandi AT e inoltra ogni byte ricevuto sulla UART direttamente nel link radio (SPP/GATT).
|
||||
3. Per tornare in modalità comando, è necessario inviare una sequenza di escape definita nel firmware o resettare il modulo.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# Integrazione DSP ADAU1701
|
||||
|
||||
Il collegamento BT1035 -> ADAU1701 può avvenire in due modi:
|
||||
1. **Digitale (I2S)**:
|
||||
- BT1035 (Master/Slave) -> SDATA_IN0 (ADAU1701).
|
||||
- Configurazione ADAU: Impostare `MP4/MP5` come ingressi I2S (LRCLK/BCLK).
|
||||
- Assicurarsi che il sample rate del modulo Bluetooth (`AT+I2SCFG`) corrisponda al clock configurato nell'ADAU tramite il software SigmaStudio.
|
||||
|
||||
2. **Analogico**:
|
||||
- Uscite differenziali BT1035 (`SPK_P/N`) -> ingressi ADC `ADC0/1`.
|
||||
- Utilizzare il filtro passivo di ricostruzione consigliato dal datasheet dell'ADAU1701 (50kHz corner) per evitare aliasing dovuto al campionamento del DAC interno del Bluetooth.
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# Mapping Risorse DigiRadio
|
||||
|
||||
Analisi dei file del progetto DigiRadio:
|
||||
- `DigiRadio_IC_1.h`: Mappatura dei registri del controller per il modulo IC.
|
||||
- `DigiRadio_IC_1_REG.h`: Definizione dei registri di configurazione (probabilmente relativi alla configurazione I2C del modulo Bluetooth).
|
||||
- `DigiRadio.xml`: Descriptor di sistema utilizzato dal software di configurazione/test del progetto DigiRadio.
|
||||
|
||||
Il modulo BT1035 deve essere mappato nell'indirizzo di registro 0x68 (default I2C) se il controller agisce come master I2C sul bus del modulo. Verificare che il pin `ADDRO` sia configurato correttamente in base alla netlist fornita.
|
||||
@@ -0,0 +1,298 @@
|
||||
# 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 87500–107900 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='')
|
||||
"
|
||||
```
|
||||
@@ -0,0 +1,371 @@
|
||||
# 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
|
||||
@@ -99,10 +99,14 @@ renamed cells):
|
||||
signal chain changed.
|
||||
\end{enumerate}
|
||||
|
||||
\begin{drnote}[No USBi download on DigiRadio]
|
||||
Do \emph{not} rely on SigmaStudio \emph{Link Compile Download} on this
|
||||
hardware path. Export-only workflow is sufficient: the ESP32 is the programmer
|
||||
at every power-up.
|
||||
\begin{drnote}[Export-only workflow still recommended]
|
||||
The export-only workflow above remains the simplest path: the ESP32 replays
|
||||
the committed export at every power-up, so \emph{Link Compile Download} is
|
||||
not required for day-to-day firmware builds. Since firmware~0.9.0, however,
|
||||
DigiRadio does expose SigmaStudio's Remote Connection over TCP for live DSP
|
||||
work and bench debugging --- see Section~\ref{sec:adau1701-sigmastudio-tcp}.
|
||||
A completed Download over that path is automatically persisted to flash and
|
||||
becomes the boot program, exactly like re-exporting and rebuilding.
|
||||
\end{drnote}
|
||||
|
||||
\section{Boot sequence (I\textsuperscript{2}C RAM load)}
|
||||
@@ -134,14 +138,97 @@ the DSP program and writes it into RAM after each reset.
|
||||
\label{fig:adau1701-boot}
|
||||
\end{figure}
|
||||
|
||||
\texttt{SIGMA\_WRITE\_REGISTER\_BLOCK} streams each write in 64-byte I\textsuperscript{2}C
|
||||
chunks (payloads may exceed 255 bytes). The active script comes from the
|
||||
\texttt{SIGMA\_WRITE\_REGISTER\_BLOCK} streams each write in $\leq$64-byte
|
||||
I\textsuperscript{2}C chunks, rounded down to a whole number of words for the
|
||||
target region (Parameter RAM = 4~bytes/word, Program RAM = 5, control
|
||||
registers = 2) so chunk boundaries never split a word or desynchronise the
|
||||
running address (payloads may exceed 255 bytes). The active script comes from the
|
||||
\texttt{dsp} data partition when a valid \texttt{DRAD} blob is present; otherwise
|
||||
the embedded SigmaStudio export is used (Section~\ref{sec:api-dsp-program}).
|
||||
After download, \texttt{loadAndApply()} restores the saved
|
||||
\texttt{core::AudioProfile} from NVS (or factory defaults) so user settings
|
||||
survive power cycles without re-writing the whole program.
|
||||
|
||||
\section{SigmaStudio Remote Connection (TCP bridge)}
|
||||
\label{sec:adau1701-sigmastudio-tcp}
|
||||
|
||||
\texttt{net::SigmaStudioTcpServer}\label{cls:SigmaStudioTcpServer} exposes the
|
||||
ADAU1701 to SigmaStudio running on a PC over TCP/IP, so SigmaStudio's own
|
||||
\emph{Connect} and \emph{Link Compile Download} work against DigiRadio exactly
|
||||
as they would against a USBi/ICP dongle. It listens on port \textbf{8086} in
|
||||
both SoftAP setup mode and STA mode, started from \texttt{NetBootstrap}
|
||||
alongside \texttt{SetupWebServer}. The socket is unauthenticated, consistent
|
||||
with the existing LAN-exposed \texttt{POST /api/system/ota} and
|
||||
\texttt{POST /api/dsp/program} endpoints.
|
||||
|
||||
\begin{drref}[Protocol source]
|
||||
This implements the subset of SigmaStudio's TCPi wire protocol needed for
|
||||
Connect, Download, register read/write, and runtime safeload, ported from the
|
||||
\href{https://github.com/rarranzb/ADAU1701-TCPi-ESP32}{ADAU1701-TCPi-ESP32}
|
||||
reference project (MIT licence). Analog Devices' TCPi protocol itself is
|
||||
closed/undocumented; this is a single third-party reverse-engineering source,
|
||||
cross-checked against DigiRadio's own generated \texttt{DigiRadio\_IC\_1.h}
|
||||
memory-map constants (Program RAM at word-address \texttt{0x0400}, Parameter
|
||||
RAM at \texttt{0x0000}, core control register at \texttt{0x081C}) which match
|
||||
exactly. Verify with a real SigmaStudio Connect + Download session before
|
||||
relying on it for production DSP work.
|
||||
\end{drref}
|
||||
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\small
|
||||
\begin{tabular}{@{}L{2.2cm}L{9.8cm}@{}}
|
||||
\drhead Frame & Layout \\
|
||||
\midrule
|
||||
\texttt{0x09} WRITE &
|
||||
\texttt{[op][safeload][rsvd][totalLen:2][chipAddr][dataLen:2][addr:2][payload]} \\
|
||||
\texttt{0x0A} READ\_REQ &
|
||||
\texttt{[op][totalLen:2][chipAddr][dataLen:2][addr:2]} \\
|
||||
\texttt{0x0B} READ\_RESP &
|
||||
\texttt{[op][size:2][chipAddr][dataLen:2][addr:2][status][data]} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{TCPi frame layout (all multi-byte fields big-endian).}
|
||||
\label{tab:adau1701-tcpi-frames}
|
||||
\end{table}
|
||||
|
||||
Direct (non-safeload) writes reuse the region-aware
|
||||
\texttt{SIGMA\_WRITE\_REGISTER\_BLOCK} (Section~\ref{sec:adau1701-boot} boot
|
||||
path). Safeload writes forward SigmaStudio's already-encoded 4-byte words
|
||||
verbatim via \texttt{sigma\_safeload\_raw\_block()} --- unlike
|
||||
\texttt{sigma\_safeload\_block()} (Section~\ref{sec:adau1701-safeload}), which
|
||||
reconstructs the payload from a host \texttt{int}, this path never
|
||||
reinterprets network bytes. A new \texttt{sigma\_studio\_lock()} /
|
||||
\texttt{sigma\_studio\_unlock()} pair in \texttt{SigmaStudioFW.c} serialises
|
||||
whole safeload/write sequences against \texttt{Adau1701Driver}'s own
|
||||
REST-triggered safeload calls, so a live SigmaStudio session and
|
||||
\texttt{/api/audio/*} traffic cannot interleave mid-transaction.
|
||||
|
||||
\subsection{Persisting a Download as the boot program}
|
||||
|
||||
A completed \emph{Link Compile Download} is captured and made the program
|
||||
DigiRadio boots with next time --- the same outcome as re-exporting and
|
||||
rebuilding. \texttt{SigmaStudioTcpServer} coalesces contiguous direct writes
|
||||
into a handful of regions per connection (mirroring the five-block shape
|
||||
of \texttt{EmbeddedDspProgramSource}), completion is detected the same way
|
||||
the reference project detects it: a direct write to \texttt{0x081C} that sets
|
||||
the DSPRUN bit. On completion the coalesced regions are serialised
|
||||
(\texttt{core::serializeDspProgramBlob()}) and written to the \texttt{dsp}
|
||||
flash partition via \texttt{adau1701::FlashDspProgramSource::storeBlob()} ---
|
||||
the same function \texttt{POST /api/dsp/program} already uses
|
||||
(Section~\ref{sec:api-dsp-program}). If a session is too fragmented to fit the
|
||||
blob's own caps (32 regions / 16\,KiB per region / 200\,KiB total), the
|
||||
persist step is skipped for that session only; the DSP still runs from what
|
||||
was written live over I\textsuperscript{2}C.
|
||||
|
||||
\begin{drcaution}[Not ported from the reference project]
|
||||
DigiRadio does not implement the reference project's EEPROM self-boot capture
|
||||
(binary format for an external self-boot EEPROM) or its Wi-Fi/GPIO
|
||||
configuration web UI --- DigiRadio has no self-boot EEPROM on this bus (the
|
||||
24AA025E48 at \texttt{0x52} is an EUI-48 identity chip only), and Wi-Fi
|
||||
provisioning already exists via \texttt{POST /api/wifi}.
|
||||
\end{drcaution}
|
||||
|
||||
\section{Software architecture}
|
||||
\label{sec:adau1701-stack}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
The Feasycom FSC-BT1035 (Qualcomm QCC3056) is the wireless output stage of
|
||||
DigiRadio: it receives PCM from the ADAU1701 over I\textsuperscript{2}S and
|
||||
streams Bluetooth audio with aptX, aptX~HD, and aptX~Adaptive. This chapter
|
||||
documents how the ESP32-S3 controls the module over UART (AT commands with
|
||||
RTS/CTS), why I\textsuperscript{2}S slave mode is mandatory, and how
|
||||
\texttt{bt1035::Bt1035Driver} implements the bring-up sequence.
|
||||
documents how the ESP32-S3 controls the module over UART (AT commands, no
|
||||
hardware flow control), why I\textsuperscript{2}S slave mode is mandatory,
|
||||
and how \texttt{bt1035::Bt1035Driver} implements the bring-up sequence.
|
||||
|
||||
\begin{drref}[Hardware context]
|
||||
Board wiring (UART pins, I\textsuperscript{2}S to the module, flow control)
|
||||
@@ -30,9 +30,9 @@ accepted and encoded for transmission.
|
||||
|
||||
Without firmware init the module may stay in a default mode that ignores the
|
||||
I\textsuperscript{2}S bus from the ADAU1701. The mandatory
|
||||
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67} commands select I\textsuperscript{2}S
|
||||
slave input at 48\,kHz --- omitting them silently breaks the entire wireless
|
||||
output (Section~\ref{sec:bt1035-i2s}).
|
||||
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=35} commands select I\textsuperscript{2}S
|
||||
slave input at 48\,kHz, 24-bit --- omitting them silently breaks the entire
|
||||
wireless output (Section~\ref{sec:bt1035-i2s}).
|
||||
|
||||
\section{Control interface}
|
||||
\label{sec:bt1035-uart}
|
||||
@@ -48,7 +48,7 @@ output (Section~\ref{sec:bt1035-i2s}).
|
||||
Port & UART2 (not the console UART) \\
|
||||
Baud rate & 115200 \\
|
||||
Data format & 8N1 \\
|
||||
Flow control & Hardware RTS/CTS (required) \\
|
||||
Flow control & Disabled on the ESP32 UART (\texttt{UART\_HW\_FLOWCTRL\_DISABLE}) \\
|
||||
Reset & GPIO active-low pulse at boot \\
|
||||
SYS\_CTL & Held active to enable the module \\
|
||||
\bottomrule
|
||||
@@ -57,6 +57,18 @@ output (Section~\ref{sec:bt1035-i2s}).
|
||||
\label{tab:bt1035-uart}
|
||||
\end{table}
|
||||
|
||||
\begin{drnote}[Flow control disabled, not just unused]
|
||||
Earlier revisions wired \texttt{RTS}/\texttt{CTS} through
|
||||
\texttt{Bt1035Pins} and configured \texttt{UART\_HW\_FLOWCTRL\_CTS\_RTS}.
|
||||
That was reverted: \texttt{Bt1035Driver.cpp} now configures
|
||||
\texttt{UART\_HW\_FLOWCTRL\_DISABLE} and passes
|
||||
\texttt{UART\_PIN\_NO\_CHANGE} for both lines --- GPIO14/21 are not driven by
|
||||
this driver. No AT command in \texttt{core::Bt1035AtCommand} configures flow
|
||||
control on the module side either; this has not caused observed boot
|
||||
failures, but has not been independently verified against the module's own
|
||||
flow-control expectation.
|
||||
\end{drnote}
|
||||
|
||||
\subsection{Response handling}
|
||||
|
||||
Every command expects a module reply containing \texttt{OK} or
|
||||
@@ -69,7 +81,7 @@ Every command expects a module reply containing \texttt{OK} or
|
||||
\end{itemize}
|
||||
|
||||
Host tests in \drpath{components/core/test/bt1035_at_test.cpp} lock the
|
||||
init sequence (\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=67}) and the parser.
|
||||
init sequence (\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=35}) and the parser.
|
||||
|
||||
\section{Mandatory I\textsuperscript{2}S slave mode}
|
||||
\label{sec:bt1035-i2s}
|
||||
@@ -80,7 +92,9 @@ with shared BCLK/LRCLK (see Chapter~\ref{ch:hardware}). The init sequence
|
||||
must use:
|
||||
\begin{itemize}
|
||||
\item \texttt{AT+AUXCFG=3} --- I\textsuperscript{2}S mode (§5.1.25)
|
||||
\item \texttt{AT+I2SCFG=67} --- I\textsuperscript{2}S slave, 48\,kHz, 32-bit (§5.1.4)
|
||||
\item \texttt{AT+I2SCFG=35} --- I\textsuperscript{2}S slave, 48\,kHz, 24-bit (§5.1.4),
|
||||
matching the ADAU1701 serial output word length configured in the
|
||||
SigmaStudio Hardware Configuration
|
||||
\end{itemize}
|
||||
\texttt{AT+AUXCFG=1} (Line-In) does \textbf{not} match the schematic.
|
||||
AGENTS.md treats skipping I\textsuperscript{2}S init as a production bug.
|
||||
@@ -101,16 +115,18 @@ I\textsuperscript{2}S path is ready before Wi-Fi starts.
|
||||
>={Latex}, node distance=3mm]
|
||||
\node[drstep, fill=black!6] (sys) {SYS\_CTL high, RESET\# pulse};
|
||||
\node[drstep, fill=black!6, below=of sys] (uart)
|
||||
{Install UART2 @ 115200, RTS/CTS};
|
||||
\node[drstep, fill=black!8, below=of uart] (at)
|
||||
{Install UART2 @ 115200, flow control disabled};
|
||||
\node[drstep, fill=black!7, below=of uart] (swrst)
|
||||
{Send \texttt{AT+RESET} (best-effort), settle 500\,ms, flush RX};
|
||||
\node[drstep, fill=black!8, below=of swrst] (at)
|
||||
{Send \texttt{AT} --- expect OK};
|
||||
\node[drstep, fill=black!10, below=of at] (aux)
|
||||
{Send \texttt{AT+AUXCFG=3} --- expect OK (I\textsuperscript{2}S)};
|
||||
\node[drstep, fill=black!10, below=of aux] (i2s)
|
||||
{Send \texttt{AT+I2SCFG=67} --- expect OK (slave 48\,kHz)};
|
||||
{Send \texttt{AT+I2SCFG=35} --- expect OK (slave 48\,kHz, 24-bit)};
|
||||
\node[drstep, fill=black!6, below=of i2s] (done)
|
||||
{\texttt{Bt1035Driver::isBooted()} = true};
|
||||
\foreach \a/\b in {sys/uart, uart/at, at/aux, aux/i2s, i2s/done} {
|
||||
\foreach \a/\b in {sys/uart, uart/swrst, swrst/at, at/aux, aux/i2s, i2s/done} {
|
||||
\draw[->] (\a) -- (\b);
|
||||
}
|
||||
\end{tikzpicture}
|
||||
@@ -118,6 +134,17 @@ I\textsuperscript{2}S path is ready before Wi-Fi starts.
|
||||
\label{fig:bt1035-boot}
|
||||
\end{figure}
|
||||
|
||||
\begin{drnote}[AT+RESET is best-effort, not gated]
|
||||
Unlike \texttt{AT+AUXCFG=3}/\texttt{AT+I2SCFG=35} (mandatory,
|
||||
\texttt{core::bootInitSequence()}, boot fails if either is refused),
|
||||
\texttt{AT+RESET} is sent separately and its result is discarded: it is
|
||||
unverified whether this Feasycom firmware acknowledges the command before
|
||||
rebooting, resets silently without a reply, or rejects it outright. Gating
|
||||
boot on an unconfirmed ack would risk breaking an otherwise-working sequence.
|
||||
The 500\,ms settle delay and RX flush after sending it mirror the delay
|
||||
already used after the hardware GPIO reset pulse.
|
||||
\end{drnote}
|
||||
|
||||
Pairing, codec selection, and volume over Bluetooth are handled by the
|
||||
module's own firmware and NVS; DigiRadio firmware currently implements
|
||||
only the I\textsuperscript{2}S bring-up required for the wired audio path.
|
||||
@@ -158,9 +185,10 @@ The firmware enumerates every command it sends. Wire formats follow
|
||||
\begin{tabular}{@{}L{2.4cm}L{3.4cm}L{5.5cm}@{}}
|
||||
\drhead Enum & Line sent & Programming guide \\
|
||||
\midrule
|
||||
\texttt{Reset} & \texttt{AT+RESET} & software reset, best-effort \\
|
||||
\texttt{Ping} & \texttt{AT} & link check \\
|
||||
\texttt{I2sMode} & \texttt{AT+AUXCFG=3} & §5.1.25 Param=3 I2S \\
|
||||
\texttt{I2sSlave48k32} & \texttt{AT+I2SCFG=67} & §5.1.4 slave 48\,kHz 32-bit \\
|
||||
\texttt{I2sSlave48k24} & \texttt{AT+I2SCFG=35} & §5.1.4 slave 48\,kHz 24-bit \\
|
||||
\texttt{PairDiscoverable} & \texttt{AT+PAIR=1} & §5.1.20 enter discoverable \\
|
||||
\texttt{PairHidden} & \texttt{AT+PAIR=0} & §5.1.20 leave discoverable \\
|
||||
\texttt{A2dpStat} & \texttt{AT+A2DPSTAT} & §5.3.1; states 1--5 \\
|
||||
@@ -171,7 +199,9 @@ The firmware enumerates every command it sends. Wire formats follow
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Enumerated AT commands (\texttt{core::Bt1035AtCommand}). Boot
|
||||
uses Ping + I2sMode + I2sSlave48k32; pairing commands are runtime.}
|
||||
sends Reset (best-effort) then the mandatory Ping + I2sMode +
|
||||
I2sSlave48k24 (\texttt{core::bootInitSequence()}); pairing commands are
|
||||
runtime.}
|
||||
\label{tab:bt1035-at}
|
||||
\end{table}
|
||||
|
||||
@@ -258,7 +288,7 @@ bt1035::Bt1035Driver& bt = ...;
|
||||
if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sMode); !r) {
|
||||
// handle Bt1035Error
|
||||
}
|
||||
if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sSlave48k32); !r) {
|
||||
if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sSlave48k24); !r) {
|
||||
// handle Bt1035Error
|
||||
}
|
||||
\end{verbatim}
|
||||
|
||||
@@ -79,11 +79,20 @@ Minimal HTTP server: gzipped setup UI and typed JSON REST handlers:
|
||||
\item Audio: \texttt{/api/audio/*}
|
||||
\item Bluetooth: \texttt{/api/bluetooth/*}
|
||||
\item Presets: \texttt{/api/stations/*}
|
||||
\item Streaming: \texttt{/api/streaming}
|
||||
\end{itemize}
|
||||
JSON parsing and serialisation delegate to the pure core; credentials persist via \texttt{ISecureStore};
|
||||
tuner via \texttt{tuner::TunerService}; audio via
|
||||
\texttt{audio::AudioService}; Bluetooth via \texttt{bluetooth::BluetoothService};
|
||||
presets via \texttt{station::StationService}.
|
||||
presets via \texttt{station::StationService}; streaming via
|
||||
\texttt{webradio::WebRadioService}.
|
||||
|
||||
\section{WifiScanner}\label{cls:WifiScanner}
|
||||
Stateless shell helper wrapping \texttt{esp\_wifi} scan for the setup UI's
|
||||
Wi-Fi tab. \texttt{scanNearby()} temporarily switches an AP-only radio to
|
||||
APSTA mode when needed, runs a blocking scan, dedupes access points by
|
||||
SSID (keeping the strongest RSSI), and maps results to
|
||||
\texttt{core::WifiScannedNetwork}. Called from \texttt{POST /api/wifi/scan}.
|
||||
|
||||
\section{NetBootstrap}\label{cls:NetBootstrap}
|
||||
Owns network resources for setup or STA mode. Initialises encrypted NVS via
|
||||
@@ -168,7 +177,7 @@ index (1--8), 12-digit MAC, and optional friendly name. Parsed by
|
||||
UART driver for the FSC-BT1035 (Chapter~\ref{ch:bt1035}). \texttt{boot()}
|
||||
pulses RESET\#, opens UART2 with RTS/CTS, and runs
|
||||
\texttt{core::bootInitSequence()} (Ping + \texttt{AT+AUXCFG=3} +
|
||||
\texttt{AT+I2SCFG=67}). Returns
|
||||
\texttt{AT+I2SCFG=35}). Returns
|
||||
\texttt{Bt1035Error} on timeout, ERROR response, or UART failure.
|
||||
|
||||
\section{Adau1701Driver}\label{cls:Adau1701Driver}
|
||||
@@ -341,4 +350,12 @@ the inactive OTA slot, validates the app descriptor via
|
||||
\texttt{core::validateOtaAppDescriptor()}, and exposes
|
||||
\texttt{confirmBoot()} for rollback cancellation after a healthy network boot.
|
||||
|
||||
\section{WebRadioService}\label{cls:WebRadioService}
|
||||
Thread-safe holder for the internet radio streaming config (enabled flag +
|
||||
HTTP MP3 URL), backed by \texttt{core::ISecureStore}. A FreeRTOS mutex
|
||||
guards an in-RAM copy so \texttt{setConfig()} from an HTTP handler and
|
||||
\texttt{config()} polled by the streaming task in
|
||||
\texttt{main/web\_radio\_stream.cpp} never race. Exposed on
|
||||
\texttt{/api/streaming}.
|
||||
|
||||
% ------------------------------------------------------------------
|
||||
|
||||
@@ -151,7 +151,7 @@ safeload registers.
|
||||
\paragraph{FSC-BT1035 (Bluetooth).}
|
||||
The module is controlled by AT commands over UART with hardware flow control.
|
||||
The initialisation sequence includes enabling I\textsuperscript{2}S slave mode
|
||||
(\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}), which is required for the
|
||||
(\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=35}), which is required for the
|
||||
wired digital audio path from the ADAU1701 to the module.
|
||||
Command responses are parsed explicitly, with timeouts treated as errors.
|
||||
Full driver API, boot flow, and error codes are in Chapter~\ref{ch:bt1035}.
|
||||
@@ -218,7 +218,7 @@ bring-up:
|
||||
defaults) via ADAU1701 safeload before network bring-up.
|
||||
\item \textbf{FSC-BT1035} (UART): \texttt{bt1035::Bt1035Driver::boot()}
|
||||
enables I\textsuperscript{2}S slave mode (\texttt{AT+AUXCFG=3},
|
||||
\texttt{AT+I2SCFG=67}) after the DSP path is configured
|
||||
\texttt{AT+I2SCFG=35}) after the DSP path is configured
|
||||
(Chapter~\ref{ch:bt1035}).
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ in the module's own non-volatile memory.
|
||||
\begin{drcaution}[I\textsuperscript{2}S slave mode]
|
||||
The PCB routes ADAU1701 \texttt{SDATA\_OUT0} to the module over
|
||||
I\textsuperscript{2}S (BCLK, LRCLK, PCM data). Firmware must initialise
|
||||
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}, not Line-In
|
||||
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=35}, not Line-In
|
||||
(\texttt{AT+AUXCFG=1}). See Chapter~\ref{ch:bt1035}, Section~\ref{sec:bt1035-i2s}.
|
||||
\end{drcaution}
|
||||
|
||||
@@ -525,7 +525,7 @@ ever used.}
|
||||
\midrule
|
||||
UART with flow control & TX/RX + RTS/CTS wired & verified \\
|
||||
I\textsuperscript{2}S init (firmware) &
|
||||
\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=67} & driver rule \\
|
||||
\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=35} & driver rule \\
|
||||
I\textsuperscript{2}S input & receives clocks from ADAU master (slave) & verified \\
|
||||
Reset line & RESET (GPIO17) driven by ESP32 & verified \\
|
||||
\bottomrule
|
||||
|
||||
@@ -21,9 +21,18 @@ the program, not to drive the chip in operation.
|
||||
The whole program can be built and exported offline, without a physical
|
||||
ADAU1701 or a USBi programmer connected. A USBi block is placed in the
|
||||
Hardware Configuration only because SigmaStudio requires a communication
|
||||
channel to compile; it is never used for a real download in this project.
|
||||
channel to compile.
|
||||
\end{drkey}
|
||||
|
||||
\begin{drref}[Live connection is also possible]
|
||||
Since firmware~0.9.0, DigiRadio's \texttt{net::SigmaStudioTcpServer}
|
||||
(Chapter~\ref{ch:adau1701}, Section~\ref{sec:adau1701-sigmastudio-tcp})
|
||||
exposes a TCP:8086 bridge so SigmaStudio can \emph{Connect} and
|
||||
\emph{Link Compile Download} directly against a running board, for live DSP
|
||||
tuning and bench debugging. The export-only workflow below remains the
|
||||
simplest path for ordinary firmware builds.
|
||||
\end{drref}
|
||||
|
||||
\section{Hardware Configuration}
|
||||
\label{sec:ss-hwcfg}
|
||||
|
||||
@@ -270,9 +279,11 @@ The firmware's ADAU1701 driver replays the program data over
|
||||
I\textsuperscript{2}C at boot, and uses the parameter addresses for
|
||||
safeload updates.
|
||||
|
||||
\begin{drnote}[No download needed]
|
||||
Since there is no ADAU1701 attached during development, do \emph{not} use
|
||||
\emph{Link Compile Download} (it would fail trying to reach the chip).
|
||||
\emph{Export System Files} compiles the schematic and writes the files
|
||||
directly.
|
||||
\begin{drnote}[Export System Files vs. Link Compile Download]
|
||||
Without a physical ADAU1701/USBi attached, \emph{Export System Files} is what
|
||||
you want: it compiles the schematic and writes the files directly, with no
|
||||
chip connection required. \emph{Link Compile Download} instead needs a live
|
||||
communication channel to a chip --- either the classic USBi/ICP dongle, or
|
||||
DigiRadio's own TCP:8086 SigmaStudio bridge (Section~\ref{sec:adau1701-sigmastudio-tcp})
|
||||
when connecting live to a running board.
|
||||
\end{drnote}
|
||||
|
||||
Reference in New Issue
Block a user