# DigiRadio Firmware Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3. **Status:** fw **0.8.4** on `main` — 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 T1–T12 complete; device HIL pending first PCB. | Area | Shipped in 0.8.4 | |------|------------------| | Boot | Si4684 HOST_LOAD, ADAU1701 RAM program, BT1035 Line-In init | | 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`](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`](AGENTS.md) · backlog: [`docs/TODO.md`](docs/TODO.md) ## Prerequisites **Si4684 blobs** (proprietary, local only — not in git): ```bash 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`](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. ```bash idf.py set-target esp32s3 idf.py build idf.py erase-flash flash # once when first enabling encryption (0.8.3+) idf.py -p 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): ```bash 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/`: ```bash 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**, **Wi‑Fi**, **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.4) | 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 (0–100) | | POST | `/api/audio/bass-enhance` | Bass enhance overlay (0–100) | | 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 (0–15) | | 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`](docs/manual/ch-api.tex) · C++ API: `doxygen Doxyfile` → `docs/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`](LICENSE).