BT1035Driver::boot() had zero retry on the reset+AT-init sequence — a
single hardware RESET# pulse followed immediately by AT commands,
with no second attempt if the module didn't come up in time. This is
the most plausible explanation for the intermittent "no spontaneous
UART bytes after hardware reset" / "AT init failed" boot failures
logged in docs/si4684-rf-investigation-report.md and observed again
live this morning on otherwise-identical hardware/wiring — classic
power-up timing jitter, not a permanent fault. Extracted the reset+
init sequence into resetAndInitOnce() and wrapped it in a 3-attempt
retry loop with a short delay between attempts; the one-time GPIO
config and UART driver install stay outside the loop since they don't
need repeating. Root cause of the underlying jitter is still open.
BluetoothJson.hpp was the only *Json.hpp module in the core with zero
host test coverage (status/scan/paired serialisation, auto-reconnect/
connect/speaker parsing) — every sibling module already has one.
Added bluetooth_json_test.cpp following the existing tuner_json_test
pattern; ctest now covers 20 suites instead of 19.
Documentation catch-up, found doing a full firmware re-review at the
user's request:
- POST /api/tuner/calibrate-antenna and the antcap field on
POST /api/tuner/tune (added in a previous commit, never documented)
are now in ch-api.tex.
- kFirmwareVersion was still hardcoded "0.8.5" despite the RF fixes,
BLE provisioning, phone streaming, antenna calibration, and generic
DSP param API landed since that version's actual release commit
(0a1188a). Bumped to 0.9.0 everywhere it's mentioned (health JSON,
the manual's title page, intro, classes, and API chapters).
- instructions.md and docs/TODO.md still described the firmware as
frozen at 0.8.5 awaiting hardware-in-the-loop testing that has since
happened extensively; docs/TODO.md's H5 verdict specifically still
said "suspect U6 RF ground (re-open PCBWay)" for a bug that turned
out to be firmware, not hardware — actively misleading, corrected.
Both files now summarise the post-0.8.5 HIL findings and current
open items (BT1035 root cause, intermittent HTTP unresponsiveness
under load, antenna-limited signal quality, possibly-undersized 24 KB
nvs partition).
Verified: idf.py build, doxygen (0 warnings), check-manual-sync,
check_si4684_blobs, ctest (20/20), two-pass xelatex manual build all
green. Flashed and confirmed live: fw reports 0.9.0, BT1035 booted on
the first attempt post-flash.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
150 lines
7.5 KiB
Markdown
150 lines
7.5 KiB
Markdown
# instructions.md — DigiRadio firmware, agent kickoff
|
||
|
||
Read this together with `AGENTS.md` and everything under
|
||
`.cursor/rules/`. Those define *how* to write code; this file defines
|
||
*what we are building* and the current state on `main`.
|
||
|
||
**Firmware on `main`:** **0.9.0** — agent tasks T1–T12 complete; device HIL
|
||
now largely done on the first real PCB (see below), not pending anymore.
|
||
|
||
## What DigiRadio is
|
||
|
||
An open-source Hi-Fi DAB+/FM digital radio board. Firmware runs on an
|
||
ESP32-S3 and coordinates three companion chips:
|
||
- **Si4684** — DAB+/FM tuner (delivers the audio stream).
|
||
- **ADAU1701** — SigmaDSP: equaliser + input mixer between the Si4684
|
||
and the ESP32 audio path. Program is written to DSP RAM at every boot
|
||
(no self-boot EEPROM).
|
||
- **FSC-BT1035 (QCC3056)** — Bluetooth 5.2 out with aptX Adaptive,
|
||
controlled by AT commands over UART.
|
||
|
||
Plus: tabbed web UI for provisioning and control; **encrypted NVS** for
|
||
Wi-Fi credentials, presets, audio profiles, and last-preset index.
|
||
|
||
Repository: https://github.com/manvalan/DigiRadio
|
||
|
||
## Confirmed technical decisions (do not re-litigate)
|
||
|
||
| Area | Decision |
|
||
|-------------|-------------------------------------------------------|
|
||
| Framework | ESP-IDF v5.5.x (native, not Arduino) |
|
||
| Language | C++23, pinned `-std=gnu++23` |
|
||
| Errors | `std::expected<T, Error>` (native); exceptions OFF |
|
||
| DSP boot | ESP32 writes ADAU1701 RAM at every boot (no EEPROM) |
|
||
| Architecture| Functional core (pure, host-tested) + imperative shell|
|
||
| Security | NVS + flash encryption (dev mode); see `docs/security-flash-nvs.md` |
|
||
| Docs | Doxygen + LaTeX manual sync (CI enforced) |
|
||
| HW licence | CERN-OHL-S v2 · FW licence: Apache-2.0 |
|
||
|
||
## Roadmap status
|
||
|
||
| Slice / task | Status | Notes |
|
||
|--------------|--------|-------|
|
||
| 1 Walking skeleton | Done | SoftAP, gzipped UI, `/api/health` |
|
||
| 2 Secure store + Wi-Fi | Done | `ISecureStore`, STA provisioning |
|
||
| 3 Companion-chip boot | Done | Si4684 + ADAU1701 from `Firmware/` |
|
||
| 4 Station presets | Done (0.7.0) | NVS `station_list`, full `/api/stations/*` |
|
||
| 5 ADAU1701 runtime | Done | EQ, mixer, enhancements, audio API |
|
||
| 6 Si4684 tuning | Done | FM/DAB tune, seek, RSQ, RDS, DAB services/DLS |
|
||
| 7 BT1035 | Done (0.8.4) | Pairing, A2DP, name/plist/auto-reconnect AT |
|
||
| 8 Integration | Done (0.8.1) | `IntegrationService`, last-preset NVS |
|
||
| T6 Web UI | Done (0.8.4) | Tabbed SPA + System tab (OTA/DSP upload) |
|
||
| T7 Si4684 blobs | Done (0.8.2) | Local-only `.bin`, CI policy check |
|
||
| T8 NVS encryption | Done (0.8.3) | `initEncryptedStorage`; HIL when PCB ready |
|
||
| T9–T12 Platform | Done (0.8.4) | Dual OTA, EEPROM identity, DSP + firmware OTA |
|
||
|
||
## Post-0.8.5 HIL work (first real PCB, not in the table above)
|
||
|
||
The board arrived and most of Slice 3–8's HIL assumptions turned out to be
|
||
wrong in ways that needed real fixes, not just testing. Summary (full
|
||
detail in `docs/si4684-rf-investigation-report.md`):
|
||
|
||
- **Si4684 total RF blackout, root-caused and fixed.** `writeCommand()`'s
|
||
ARG1 byte was mis-offset across FM/DAB tune, seek, DAB service commands,
|
||
and several ARG1-only status/ack commands — the chip answered every
|
||
command correctly but never actually tuned. Real FM lock, real DAB
|
||
ensemble lock (3+ ensembles), real audio confirmed live.
|
||
- **Si4684→ADAU1701 digital audio silence, fixed.** `PIN_CONFIG_ENABLE`
|
||
had both I2SOUTEN and DACOUTEN set (chip falls back to unused analog
|
||
out per AN649); `SerialInputRegister` IBP polarity was wrong (ADAU
|
||
sampling on the wrong BCLK edge).
|
||
- **DAB service list, two rounds of offset bugs fixed.** Response-parsing
|
||
offsets were wrong in a way that made `GET_DIGITAL_SERVICE_LIST` return
|
||
empty/garbled; confirmed live with 22 real, correctly-decoded station
|
||
labels. Also found `DAB_EVENT_INTERRUPT_SOURCE` (property 0xB300) was
|
||
never configured, so the service-list-ready event could never fire.
|
||
- **BT1035 boot failure is intermittent, not fixed at the root** — the
|
||
module sometimes sends zero UART bytes after a hardware reset. Made
|
||
non-fatal early on; a retry loop (up to 3 reset+init attempts) was added
|
||
once the timing-jitter theory held up, but the underlying cause is
|
||
still open.
|
||
- **FM front-end calibration.** The board's actual matching network
|
||
differs from the AN851 reference the chip's auto-tune constants assume.
|
||
Swept ANTCAP (AN851 Appendix A) and found a fixed override that beats
|
||
auto-tune by 6–11 dB RSSI/SNR across the whole band; persisted to the
|
||
24AA025E48 EEPROM (`POST /api/tuner/calibrate-antenna`) and applied by
|
||
default to every FM tune.
|
||
- **New features, not in the original Slice plan:** full FM band scan
|
||
(`POST /api/tuner/scan/full`), generic ADAU1701 parameter access
|
||
(`GET`/`PUT /api/dsp/param`, an escape hatch onto any SigmaStudio cell
|
||
beyond the curated mixer/EQ API), phone PCM streaming
|
||
(`PUT /api/stream/phone`), BLE Wi-Fi provisioning
|
||
(`net::ble_provisioning`, ESP-IDF's own `wifi_provisioning` over the
|
||
ESP32-S3's onboard BLE, additive alongside the SoftAP), web radio
|
||
streaming stutter fix (batched I2S writes).
|
||
- **Still open**: BT1035 root cause; intermittent multi-second HTTP
|
||
unresponsiveness under load (candidate cause: a blocking Si4684 SPI wait
|
||
colliding with `max_open_sockets=3`); DAB signal quality still
|
||
antenna-limited even after calibration; NVS partition (24 KB) may be
|
||
undersized given the accumulated write traffic (`saveProfile()`
|
||
`store_failed` seen intermittently, never root-caused).
|
||
|
||
Next work: keep chasing the open items above as the user prioritises them,
|
||
not new features unless requested.
|
||
|
||
- **Blockers first** — state risks before solutions.
|
||
- **One vertical slice at a time** — `main` always builds; host tests green.
|
||
- Apache header + Doxygen doc blocks on every file/class/method.
|
||
- Never invent register/opcode/boot steps — cite the datasheet or stop.
|
||
- After changes: `ctest`, `doxygen`, `check-manual-sync.py`, `check_si4684_blobs.py`.
|
||
|
||
## Slice 1 — Walking skeleton (complete)
|
||
|
||
- ESP-IDF `esp32s3`, C++23, `components/core` host-testable.
|
||
- SoftAP `DigiRadio-<suffix>` (or setup fallback), gzipped page, `GET /api/health`.
|
||
- Health JSON includes `fw` (today **0.8.5**), `serialNumber`, companion-chip flags.
|
||
- BT1035 boot must send `AT+AUXCFG=3` and `AT+I2SCFG=67` (I2S from ADAU1701).
|
||
|
||
## Slice 2 — Secure store + Wi-Fi STA (complete)
|
||
|
||
- `ISecureStore`, `NvsSecureStore`, `StaClient`, `NetBootstrap`.
|
||
- `POST /api/wifi` + Wi-Fi tab in web UI.
|
||
- NVS encryption enabled in fw 0.8.3 via `initEncryptedStorage()`.
|
||
|
||
## Slice 3 — Companion-chip boot (complete)
|
||
|
||
- Si4684 blobs local-only (`tools/fetch_si4684_firmware.py`).
|
||
- `Si4684Driver`, `Adau1701Driver`, `HardwareBootstrap` before network.
|
||
- Device flash: pending HIL on first PCB.
|
||
|
||
## Slices 4–8 — Presets, audio, tuner, BT, integration (complete)
|
||
|
||
- Presets: `StationService`, reorder, integration recall with audio profile.
|
||
- Audio: six-band EQ, enhancements, `NvsAudioProfileStore`.
|
||
- Tuner: RDS/DLS metadata in status JSON and Now Playing UI.
|
||
- Bluetooth: `BluetoothService`, pairing REST + UI.
|
||
- Integration: boot loads last preset; `POST /api/stations/tune` orchestrates tune + audio + NVS.
|
||
|
||
## Quality gates (from `Software/`)
|
||
|
||
```bash
|
||
cmake -S components/core/test -B build-host && cmake --build build-host
|
||
ctest --test-dir build-host --output-on-failure
|
||
doxygen Doxyfile
|
||
python3 tools/check-manual-sync.py
|
||
python3 tools/check_si4684_blobs.py
|
||
```
|
||
|
||
First device flash with encryption: `idf.py erase-flash flash` — see
|
||
`docs/security-flash-nvs.md`.
|