4.3 KiB
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 what to do first.
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: an elegant, essential web UI for network configuration; encrypted storage for Wi-Fi and user credentials and the station list.
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 |
| Docs | Doxygen, build must exit 0 (enforced) |
| HW licence | CERN-OHL-S v2 · FW licence: Apache-2.0 |
Working agreement
- Confirm understanding before writing code. On kickoff, summarise the plan and list any blockers or unclear hardware invariants first.
- Blockers first, always. State risks before solutions.
- One vertical slice at a time.
mainalways builds and runs. - Every file gets the Apache header; every class/method its doc block;
doxygen Doxyfilestays green. Small commits, 50/72 messages. - Never invent a register/opcode/boot step — cite the datasheet or stop.
Roadmap (slices, in order)
- Walking skeleton — boot, a task, SoftAP, web server, one JSON endpoint, one host test, docs green. No chip drivers yet. (Spec below.)
- Secure store (
ISecureStore) + Wi-Fi provisioning UI (STA join). - Station/frequency list model + persistence + UI.
- Si4684 driver: power-up, load image, tune, read RSQ.
- ADAU1701 driver: RAM boot, then safeload EQ + input mixer.
- FSC-BT1035 driver: AT init (incl.
AT+AUXCFG=1), audio out. - Integration: TunerService + AudioService end to end.
Slice 1 — Walking skeleton (the first task)
Goal: exercise the whole toolchain end to end with zero chip hardware, so every later slice drops into a working frame.
Build:
- Top-level ESP-IDF project targeting
esp32s3. sdkconfig.defaultssets C++23, exceptions off, and the flash/NVS encryption options (leave encryption keys/enablement documented, not hard-enabled, until we decide on the secure-store slice).- The
components/corecomponent compiles both under ESP-IDF and standalone on the host.
Behaviour:
app_mainstarts a FreeRTOS task that logs a heartbeat on a timer.- Bring up SoftAP with a known SSID (e.g.
DigiRadio-setup). - Start an HTTP server serving one minimal gzipped page from flash.
- Expose
GET /api/healthreturning a typed DTO serialised by the pure core, e.g.{"status":"ok","fw":"0.1.0"}.
Acceptance criteria:
idf.py buildsucceeds;flash monitorshows the heartbeat.- A phone/laptop can join the SoftAP, load the page, and get a valid
JSON response from
/api/health. - The health DTO is defined and serialised in
components/core, with a host unit test that passes underctest. doxygen Doxyfileexits 0 with an empty warnings log.- Every file has the Apache header; every class/method its doc block.
- No ESP-IDF headers included from
components/core.
Out of scope for Slice 1: any Si4684 / ADAU1701 / BT1035 code, real credentials, encryption enablement. Those come in later slices.
First message to the agent
Ask it to read AGENTS.md, .cursor/rules/, and this file, then
respond with: (1) the confirmed stack, (2) the exact repo layout it will
create, (3) how it will satisfy each Slice 1 acceptance criterion, and
(4) any blockers or questions — before writing code.