Files
DigiRadio/Software
micheleandClaude Sonnet 5 7e65394100 Fix web radio streaming stutter: batch I2S writes, widen DMA buffer
writeFrame() called i2s_channel_write() once per decoded PCM sample (up to
1152 separate driver calls per MP3 frame), each with its own locking/DMA
bookkeeping overhead. Now accumulates a whole frame into one buffer and
writes it in a single call.

The I2S TX channel also used the ESP-IDF default DMA config (6 descriptors
x 240 frames = ~30 ms of buffering at 48 kHz), leaving almost no headroom
against network jitter in this single-task fetch+decode+play pipeline.
Widened to 12 x 480 (~120 ms) so a brief HTTP stall doesn't immediately
starve the DMA and audibly crackle.

Both are static/architectural fixes verified by build only — not confirmed
live yet (board disconnected this session).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
2026-08-18 07:50:48 +02:00
..
2026-07-06 01:12:37 +02:00
2026-07-06 01:12:37 +02:00

DigiRadio Firmware

Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3.

Status: fw 0.8.5 on main — BT1035 I2S slave boot init, dual OTA + DSP blob updates, EEPROM identity, NVS + flash encryption (development mode), tabbed Web UI with System uploads, 15 host tests, 4 CI jobs. Agent tasks T1T12 complete; device HIL pending first PCB.

Area Shipped in 0.8.5
Boot Si4684 HOST_LOAD, ADAU1701 RAM program, BT1035 I2S slave init (AUXCFG=3, I2SCFG=67)
Tuner FM/DAB tune, seek up/down, RSQ, RDS, DAB services + DLS
Audio 6-band EQ, mixer, stereo/bass enhance, NVS profile
Updates ESP32 dual-OTA, ADAU1701 dsp partition blob replay
Identity 24AA025E48 EUI-48 → serial, SoftAP/BT name suffix
Presets CRUD, reorder, integrated recall + last-preset at boot
Network SoftAP/STA, tabbed gzipped SPA, typed JSON REST API
Security initEncryptedStorage() — see docs/security-flash-nvs.md
Quality Doxygen, manual sync, Si4684 blob policy check

Architecture: functional core (components/core, host-tested, no ESP-IDF) + imperative shell (drivers, services, net, secure_store). Rules: AGENTS.md · backlog: docs/TODO.md

Prerequisites

Si4684 blobs (proprietary, local only — not in git):

python3 tools/fetch_si4684_firmware.py --dab-only
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware
python3 tools/check_si4684_blobs.py

See Firmware/Si4684-Firmware/README.md.

ESP-IDF v5.5.x, target esp32s3.

Quick start

Open this directory (Software/) as the Cursor project so AGENTS.md and .cursor/rules/ load automatically.

idf.py set-target esp32s3
idf.py build
idf.py erase-flash flash   # once when first enabling encryption (0.8.3+)
idf.py -p <port> monitor

Production flash-encryption release mode: overlay sdkconfig.defaults.production (see security doc — irreversible on chip).

Host tests

Pure core, no hardware (needs C++23 compiler on macOS — Homebrew llvm or GCC 14):

cmake -S components/core/test -B build-host \
      -DCMAKE_CXX_COMPILER="$(brew --prefix llvm)/bin/clang++"
cmake --build build-host
ctest --test-dir build-host --output-on-failure

Quality gates (CI on every push to main)

Run from Software/:

doxygen Doxyfile
python3 tools/check-manual-sync.py
python3 tools/check_si4684_blobs.py
tools/gzip-www.sh   # after editing components/net/www/index.html

CI jobs: host-tests, doxygen, manual-sync, si4684-blobs (.github/workflows/ci.yml).

Web UI

Gzipped single-page app at / — tabs: Now (RDS/DLS), Radio, Presets, Audio (6-band EQ), BT, WiFi, System (firmware OTA + DSP upload). Header shows EEPROM serial from /api/health. Source: components/net/www/index.html · regenerate embed: tools/gzip-www.sh.

HTTP API (fw 0.8.5)

Method Path Purpose
GET /api/health Status, firmware version, serial, companion-chip flags
POST /api/wifi Provision STA credentials; reboot on success
GET /api/tuner/status Tuner snapshot (DAB/FM, RDS/DLS metadata)
GET /api/tuner/services DAB service list for current ensemble
POST /api/tuner/tune Tune DAB ensemble or FM frequency
POST /api/tuner/play Start DAB service playback
POST /api/tuner/seek FM seek up or down ({"direction":"up|down"})
GET/PUT /api/audio/profile Read/apply ADAU1701 mixer + 6-band EQ
POST /api/audio/reset Factory-flat audio profile
POST /api/audio/stereo-enhance Stereo depth overlay (0100)
POST /api/audio/bass-enhance Bass enhance overlay (0100)
POST /api/dsp/program Upload ADAU1701 DRAD blob (raw bytes, reboot)
POST /api/system/ota Upload ESP32 firmware image (raw bytes, reboot)
GET /api/bluetooth/status BT1035 boot, pairing, A2DP, name, auto-reconnect
GET /api/bluetooth/paired Paired-device list from module
POST /api/bluetooth/pair Enter discoverable mode
POST /api/bluetooth/pair/stop Leave discoverable mode
POST /api/bluetooth/disconnect Release A2DP session
POST /api/bluetooth/auto-reconnect Set auto-reconnect count (015)
GET /api/stations List saved presets
POST /api/stations Add preset
POST /api/stations/remove Remove preset by index
POST /api/stations/reorder Move preset (from/to indices)
POST /api/stations/tune Recall preset (tuner + audio profile + NVS)

Wire schemas: docs/manual/ch-api.tex · C++ API: doxygen Doxyfiledocs/api/html/index.html.

Layout

Path Role
Firmware/ Si4684 .bin (local) + ADAU1701 SigmaStudio export
components/core/ Pure domain (host-tested)
components/drivers/ Si4684, ADAU1701, BT1035
components/services/ Tuner, audio, Bluetooth, station, integration
components/secure_store/ Encrypted NVS: credentials, profiles, presets
components/net/ Wi-Fi, HTTP server, gzipped web UI
sdkconfig.defaults C++23, NVS + flash encryption (dev mode)
partitions.csv nvs, otadata, dual OTA (ota_0/ota_1), dsp blob, nvs_keys
docs/manual/ LaTeX technical manual
docs/security-flash-nvs.md Encryption + device HIL checklist
docs/TODO.md Completed tasks + HIL backlog
tools/ Blob fetch, CI policy checks, UI gzip

Manual PDF: cd docs/manual && latexmk -lualatex manual.tex

Licence

Apache-2.0 — see LICENSE.