diff --git a/README.md b/README.md index 59d9170..c76c548 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ DigiRadio/ │ ├── pick-and-place/ Component placement (CPL / centroid) │ ├── easyeda/ EasyEDA source project │ └── 3d/ 3D renders / STEP -└── Software/ Firmware (in development — see Software/README.md) +└── Software/ Firmware (ESP-IDF — see Software/README.md, docs/TODO.md) ``` --- @@ -80,6 +80,9 @@ ADAU1701, FSC-BT1035), the HTTP JSON API, and build instructions. LaTeX sources live in `Software/docs/manual/`; the repository root `docs/` entry is a symbolic link to that folder (single source of truth). +Firmware development backlog and agent tasks: +[`Software/docs/TODO.md`](Software/docs/TODO.md). + --- ## Design Highlights @@ -101,7 +104,7 @@ is a symbolic link to that folder (single source of truth). - ✅ **PCB layout** — 6-layer, DRC clean, plane continuity verified - ✅ **BOM** — finalised (manufacturable / sourced) - 🔜 **Prototype** — in fabrication (PCBWay) -- 🛠️ **Firmware** — in active development (released after bring-up) +- 🛠️ **Firmware** — fw 0.7.0 on `main` (tuner, DSP, BT pairing, presets); see [`Software/docs/TODO.md`](Software/docs/TODO.md) --- diff --git a/Software/README.md b/Software/README.md index 8630f65..0fc3ba3 100644 --- a/Software/README.md +++ b/Software/README.md @@ -2,7 +2,9 @@ Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3. -**Status:** Slice 3 — Si4684 + ADAU1701 boot at power-up (network from Slice 2). +**Status:** fw **0.7.0** — companion-chip boot (Si4684, ADAU1701, BT1035), +Wi-Fi provisioning, tuner/audio REST API, Bluetooth pairing, station presets. +See [`docs/TODO.md`](docs/TODO.md) for the agent task list. ## Quick start @@ -24,7 +26,7 @@ cmake --build build-host ctest --test-dir build-host --output-on-failure ``` -Documentation (must exit 0): +Documentation gates (must exit 0 before merging): ```bash doxygen Doxyfile @@ -37,26 +39,47 @@ Manual PDF (design + HTTP API + class reference): cd docs/manual && latexmk -lualatex manual.tex ``` -## HTTP API (fw 0.3.0) +## HTTP API (fw 0.7.0) | Method | Path | Purpose | |--------|------|---------| -| GET | `/api/health` | `{"status":"ok","fw":"0.3.0"}` | +| GET | `/api/health` | Status, firmware version, companion-chip flags | | POST | `/api/wifi` | Provision STA credentials; reboot on success | +| GET | `/api/tuner/status` | Tuner snapshot (DAB/FM) | +| 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 | +| GET/PUT | `/api/audio/profile` | Read/apply ADAU1701 mixer + EQ profile | +| 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) | +| GET | `/api/bluetooth/status` | BT1035 boot, pairing, A2DP state | +| POST | `/api/bluetooth/pair` | Enter discoverable mode | +| POST | `/api/bluetooth/pair/stop` | Leave discoverable mode | +| POST | `/api/bluetooth/disconnect` | Release A2DP session | +| GET | `/api/stations` | List saved presets | +| POST | `/api/stations` | Add preset | +| POST | `/api/stations/remove` | Remove preset by index | +| POST | `/api/stations/tune` | Recall preset on tuner | Full schemas, error tokens, and boot flow: [`docs/manual/ch-api.tex`](docs/manual/ch-api.tex). -C++ signatures: generate with `doxygen Doxyfile` → `docs/api/html/index.html`. +C++ signatures: `doxygen Doxyfile` → `docs/api/html/index.html`. ## Layout | Path | Role | |------|------| | `Firmware/` | Si4684 `.bin` blobs (DAB+FM) + ADAU1701 SigmaStudio export | -| `components/drivers/si4684/` | AN649 SPI boot driver | -| `components/drivers/adau1701/` | I2C SigmaStudio RAM download | +| `components/core/` | Pure domain (host-tested) | +| `components/drivers/` | Si4684, ADAU1701, BT1035 drivers | +| `components/services/` | Tuner, audio, Bluetooth, station services | +| `components/net/` | Wi-Fi, HTTP server, gzipped web UI | +| `docs/manual/` | LaTeX technical manual (canonical) | +| `docs/TODO.md` | Agent task list (prioritised backlog) | See [`AGENTS.md`](AGENTS.md) §12 and [`instructions.md`](instructions.md) for -the authoritative repo layout, coding rules, and development roadmap. +coding rules and slice roadmap. ## Licence diff --git a/Software/docs/TODO.md b/Software/docs/TODO.md index abca173..2b8f534 100644 --- a/Software/docs/TODO.md +++ b/Software/docs/TODO.md @@ -1,62 +1,145 @@ -# DigiRadio — consolidated TODO (audit 2026-07-06) +# TODO — DigiRadio firmware -Firmware **0.7.0** after BT1035 pairing + Slice 4 presets. This list -cross-checks code, manual, API, UI, and `instructions.md`. +Task list for the coding agent. Work top to bottom; each task is a +vertical slice that keeps `main` building and the host tests green. -## Done in this slice +**Before writing code, read `AGENTS.md`, `.cursor/rules/`, and +`instructions.md`.** Every task below must satisfy the Definition of Done +in `AGENTS.md §10`: Apache header on new files, doc block on every class +and method, `doxygen Doxyfile` exits 0, host tests pass, +`tools/check-manual-sync.py` passes, no primitive obsession, typed +errors, no plaintext secrets. -| Area | Status | -|------|--------| -| BT1035 pairing AT (`AT+PAIR`, `AT+A2DPSTAT`, `AT+A2DPDISC`) | Done | -| `BluetoothService` + `/api/bluetooth/*` + UI | Done | -| `Station`, `StationList`, NVS persistence | Done | -| `StationService` + `/api/stations/*` + UI | Done | -| Host tests (10/10 green) | Done | -| Manual: `ch-api`, `ch-bt1035`, `ch-classes` | Done | +Working directory for all commands is `Software/`. -## High priority (next) +**Current firmware:** `0.7.0` — BT1035 pairing (`/api/bluetooth/*`), +station presets (`/api/stations/*`), host tests green (10/10), manual +sync green (38 classes). -1. **Device flash / HIL** — verify Si4684, ADAU1701, BT1035 on hardware; pairing with real headphones; preset recall across reboot. -2. **DAB preset save from UI** — “Save current tune target” stores ensemble index only; capture `service_id` / `component_id` from the last played DAB service (needs UI state or tuner cache). -3. **FM band switch UX** — Si4684 boots DAB; FM tune may reload FM image; document/limit band changes in UI (auto-reload or explicit band selector). -4. **NVS encryption** — enable `nvs_keys` partition for production (Wi-Fi, presets, future user creds). -5. **Slice 8 integration** — unify tuner + audio + presets in a single “now playing” model; source selection (DAB / FM / BT Line-In). +--- -## Medium priority +## Completed (fw 0.7.0) -6. **IBtModule interface** — AGENTS mentions it; driver is used directly today. Add when a second BT module or host fake is needed. -7. **BT1035 extended AT** — `AT+NAME`, `AT+PLIST`, `AT+A2DPCONN`, event-driven `+PAIRED` / `+A2DPDEV` (UART listener task). -8. **Station list reorder API** — `StationList::move()` exists in core; no HTTP route yet. -9. **EQ UI completeness** — web UI exposes master/mixer/enhance; per-band EQ editing not in UI (API supports full profile PUT). -10. **Si4684 RSQ / scan UX** — driver + HTTP largely done; polish seek, service list refresh, signal display on UI. -11. **User credentials** — `ISecureStore` extension for login (out of scope until product needs it). +- **Station / preset list (T3 core)** — `Station`, `StationList`, NVS key + `station_list`, `StationService`, REST + Presets UI. Remaining polish: + reorder API, save DAB `service_id`/`component_id` from UI, HIL on device. +- **BT1035 pairing** — `AT+PAIR`, `AT+A2DPSTAT`, `AT+A2DPDISC`, + `BluetoothService`, REST + UI (not numbered below; landed with Slice 7). -## Documentation / tooling +--- -12. **Overleaf sync** — GitHub is source of truth; root `docs/` symlink can break Overleaf push; compile from `Software/docs/manual/manual.tex`. -13. **Firmware version single source** — `0.7.0` in `SetupWebServer.cpp`; align `FirmwareVersion` / health test constants if desired. -14. **Doxygen pass** — run `doxygen Doxyfile` on CI host with ESP-IDF toolchain. +## P0 — Fix the build gate (do this first) -## Low priority / ideas +### T1. Clear the 16 Doxygen warnings +**Why:** `doxygen Doxyfile` currently exits non-zero, so the docs gate is +red and CI (once added) will fail. +**What:** +- Replace the invalid `\texttt{...}` with `` `...` `` (backticks) or + `\c word` in: `core/AudioProfileJson.hpp` (l.58, l.80), + `core/AudioEnhancements.hpp` (l.25). +- Fix `\r` interpreted as a command in `core/Bt1035At.hpp` (l.64) — + wrap the AT string in `@code ... @endcode` or escape as `\\r`. +- Add a space after `\ref` in `bt1035/Bt1035Driver.hpp` (l.89) and + `core/AudioEnhancements.hpp` (l.24). +- Document the missing `@param companionChips` in + `net/NetBootstrap.hpp` (`start`) and `net/SetupWebServer.hpp` (`start`). +**Done when:** `doxygen Doxyfile` exits 0 and `docs/api/doxygen-warnings.log` +is empty. -15. Physical preset buttons → map GPIO to `StationService::tuneToIndex` by `PresetSlot`. -16. OTA updates, mDNS hostname (`digiradio.local`), HTTPS on LAN. -17. aptX license note (Feasycom) — commercial firmware variant if needed. +### T2. Add the CI workflow +**Why:** validate every push automatically. +**What:** add `.github/workflows/ci.yml` with three jobs — host build + +`ctest`, `doxygen` (fail on warnings), and `check-manual-sync.py`. A ready +draft was prepared; place it and confirm all three jobs pass. +**Done when:** the workflow is green on `main` after T1. -## Test gaps +--- -| Missing test | Layer | -|--------------|-------| -| `BluetoothService` with fake driver | Host (needs `IBtModule` or inject interface) | -| `StationService` + fake `ISecureStore` | Host | -| `NvsSecureStore` station round-trip | Target / integration | -| BT1035 UART HIL | Hardware-only, marked separate | +## P1 — Missing domain features -## Roadmap alignment (`instructions.md`) +### T3. Station / preset list — polish *(core done in 0.7.0)* +**Status:** CRUD, NVS persistence, tune recall, and basic UI are shipped. +**Remaining:** +- `POST /api/stations/reorder` (core has `StationList::move()`). +- Save DAB presets with `service_id` / `component_id` from last played service. +- Device HIL: preset survives reboot, tune recall on hardware. +**Done when:** the gaps above are closed and covered by tests. -| Slice | Item | State | -|-------|------|-------| -| 4 | Station list + persistence + UI | **Done** (basic CRUD + tune) | -| 5 | Si4684 tuning / RSQ / DAB properties | Mostly done; UI polish open | -| 7 | BT1035 pairing | **Done** (discover + A2DP stat/disconnect) | -| 8 | TunerService + AudioService E2E | Partial — health chips OK; unified UX open | +### T4. Broadcast metadata (RDS / DLS) +**Why:** `TunerStatus` currently carries only a 17-char `label`. Real +radio UX needs the FM RDS station name/radiotext and DAB DLS dynamic +label so the UI can show "what's playing". +**What:** +- Extend the tuner data model with structured metadata (station name, + radiotext/dynamic label), read from the Si4684 in the driver. +- Surface it through `TunerService::refreshStatus` and the + `/api/tuner/status` JSON. +- Keep the Si4684 register/command details in the driver; the core model + stays hardware-free. +**Done when:** the status JSON exposes the metadata and host tests cover +the parsing of raw label bytes into the model. + +### T5. Remove the services stub — integration service +**Why:** `components/services/src/component_stub.cpp` is a Slice-7 +placeholder. Tuner and Audio services exist separately but nothing +orchestrates them together. +**What:** implement the integration layer that binds `TunerService`, +`AudioService`, the station list, and the network layer into the +application flow (e.g. "select a preset → tune → apply the stored audio +profile"). Replace the stub file. +**Done when:** `app_main` drives a real end-to-end flow through this +service; the stub is gone; a manual section documents the new class. + +--- + +## P2 — User interface + +### T6. Complete the configuration Web UI +**Why:** `components/net/www/index.html` is functional but the +requirement is an *elegant, essential* SPA. It should cover: +Wi-Fi provisioning, station list management, tuner control (FM/DAB, +seek, play), live EQ/volume/enhancement control, Bluetooth pairing, and +now-playing metadata. +**What:** a minimal single-page app served gzipped from flash, design +tokens defined once (spacing, type scale, one accent), thin client over +the existing typed JSON API. No heavy frameworks. No business logic in +the UI. +**Done when:** every API capability has a UI control; the page is served +gzipped; no debug endpoint is exposed in a shipping build. + +--- + +## P3 — Procurement & hardening + +### T7. Si4684 firmware blob strategy (legal) +**Why:** the Si4684 images are proprietary (Skyworks). Tools to fetch/ +extract exist under `tools/`, but the `.bin` images must **not** be +committed to the public repo. +**What:** +- Confirm `*.bin` (patch, FM, DAB images) are in `.gitignore` and absent + from git history. +- Document in `Software/Firmware/Si4684-Firmware/README.md` how a builder + obtains the images locally (tools + AN649 reference), without + redistributing proprietary binaries. +**Done when:** no proprietary blob is tracked; the procurement path is +documented and reproducible. + +### T8. Flash/NVS encryption enablement +**Why:** secure storage holds Wi-Fi and user credentials; encryption at +rest was deferred. +**What:** enable NVS encryption on an encrypted partition (with flash +encryption), per current ESP-IDF security docs. Verify the mechanism +before enabling; keep keys out of the repo. +**Done when:** credentials are encrypted at rest and the boot path still +loads them. + +--- + +## Notes for the agent +- Prefer extending existing patterns over inventing new ones: copy the + shape of `AudioProfile` / `AudioProfileJson` / `IAudioProfileStore` for + new persisted models. +- Never invent Si4684 register/command details — cite AN649. +- One logical change per commit; 50/72 commit messages. +- After each task, run: host `ctest`, `doxygen Doxyfile`, + `tools/check-manual-sync.py` — all must pass before moving on.