diff --git a/Hardware/DATASHEET/README.md b/Hardware/DATASHEET/README.md new file mode 100644 index 0000000..4229c1d --- /dev/null +++ b/Hardware/DATASHEET/README.md @@ -0,0 +1,22 @@ +# Component datasheets (DigiRadio hardware) + +Authoritative vendor PDFs used for schematic/PCB design review and firmware +bring-up. Paths are relative to this repository root (`DigiRadio/`). + +| File | Component | Use in DigiRadio | +|------|-----------|------------------| +| `SI4684-A10.pdf` | Silicon Labs Si4684-A10 | RSTB power sequencing, SPI framing, supply levels, SMODE | +| `adau1701.pdf` | Analog Devices ADAU1701 | I²C pull-ups (2 kΩ R1/R16 on PCB; datasheet 2.2 kΩ), MCLK = 256×fS, serial-port master/slave loopback (MP10→MP4, MP11→MP5) | +| `AN851.pdf` | Silicon Labs AN851 | Si4684 bypass capacitors, antenna layout, schematic guidance | +| `FSC-BT1035_Datasheet_EN.pdf` | Feasycom FSC-BT1035 | Module pinout, electrical and mechanical specs (v1.0) | +| `FSC-BT1035_programming_user_guide_1.1.1.pdf` | Feasycom FSC-BT1035 | **AT command reference** — firmware `core::Bt1035At*` maps to §5/§6 | + +## Firmware cross-reference + +| Manual section | Primary datasheet | +|----------------|-------------------| +| `ch-hardware.tex` §validation | `SI4684-A10.pdf`, `AN851.pdf`, `adau1701.pdf`, BT1035 datasheets | +| `ch-bt1035.tex` §AT commands | `FSC-BT1035_programming_user_guide_1.1.1.pdf` §5 | + +When extending BT1035 commands, cite the programming guide section in code comments +and add a host test in `Software/components/core/test/bt1035_at_test.cpp`. diff --git a/Hardware/DATASHEET/SI4684-A10.pdf b/Hardware/DATASHEET/SI4684-A10.pdf new file mode 100644 index 0000000..e851446 Binary files /dev/null and b/Hardware/DATASHEET/SI4684-A10.pdf differ diff --git a/README.md b/README.md index cd6e886..27f3249 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ **Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3** ![Status](https://img.shields.io/badge/hardware-verified-brightgreen) -![Firmware](https://img.shields.io/badge/firmware-0.8.3-blue) +![Firmware](https://img.shields.io/badge/firmware-0.8.5-blue) ![CI](https://github.com/manvalan/DigiRadio/actions/workflows/ci.yml/badge.svg) ![PCB](https://img.shields.io/badge/PCB-6--layer-blue) ![Hardware License](https://img.shields.io/badge/hardware-CERN--OHL--S-lightgrey) @@ -57,11 +57,11 @@ no analogue conversions are introduced between the tuner and the wireless link. - **6-layer impedance-controlled PCB**, 50 × 90 mm, two ground planes - Three antennas (ESP32 2.4 GHz, BT1035 2.4 GHz, FM/DAB SMA) with proper keep-outs -### Firmware (ESP-IDF, C++23 — fw **0.8.3**) +### Firmware (ESP-IDF, C++23 — fw **0.8.5**) | Area | Capability | |------|------------| -| **Boot & chips** | Si4684 DAB+FM firmware load (HOST_LOAD blobs), ADAU1701 RAM program at every boot, BT1035 UART init | +| **Boot & chips** | Si4684 DAB+FM firmware load (HOST_LOAD blobs), ADAU1701 RAM program at every boot, BT1035 I2S slave init (`AT+AUXCFG=3`, `AT+I2SCFG=67`) | | **Tuner** | FM tune/seek, DAB ensemble tune, service scan & play, RSQ/RDS, DAB event status | | **Now playing** | FM RDS (PS, RadioText, PI, PTY) and DAB Dynamic Label (DLS) in status JSON and web UI | | **Audio** | 6-band EQ, input mixer, stereo/bass enhancement overlays, profile persist in encrypted NVS | @@ -83,6 +83,8 @@ Vertical slices landed on `main` (newest first): | Version | Highlights | |---------|------------| +| **0.8.5** | BT1035 I2S slave boot init (schematic-aligned); I2C pull-ups R1/R16 confirmed 2 kΩ; `Hardware/DATASHEET/` bundle | +| **0.8.4** | Dual OTA + DSP blob updates, EEPROM identity, System tab OTA/DSP upload | | **0.8.3** | NVS + flash encryption (development mode), `initEncryptedStorage`, security docs | | **0.8.2** | Tabbed Web UI (now-playing, 6-band EQ, all REST); Si4684 blob CI policy | | **0.8.1** | `IntegrationService` — boot preset recall, tune orchestration (tuner + audio + NVS `last_preset`); services stub removed | @@ -228,10 +230,10 @@ Generated C++ API reference: run `doxygen Doxyfile` → `Software/docs/api/html/ | **PCB layout** | 6-layer, DRC clean, plane continuity verified | | **BOM** | Finalised (manufacturable / sourced) | | **Prototype** | In fabrication (PCBWay) | -| **Firmware** | **0.8.3** on `main` — encrypted NVS, full Web UI, integration, RDS/DLS | +| **Firmware** | **0.8.5** on `main` — BT1035 I2S boot init, dual OTA, encrypted NVS, full Web UI | | **Web UI** | Tabbed SPA covering every REST endpoint | | **HIL** | Device validation pending PCB (`Software/docs/security-flash-nvs.md`) | -| **Agent backlog** | Feature-complete on `main` (fw 0.8.3); see [`Software/docs/TODO.md`](Software/docs/TODO.md) | +| **Agent backlog** | Feature-complete on `main` (fw 0.8.5); see [`Software/docs/TODO.md`](Software/docs/TODO.md) | --- @@ -239,9 +241,12 @@ Generated C++ API reference: run `doxygen Doxyfile` → `Software/docs/api/html/ The board is manufactured with **PCBWay** as a 6-layer, impedance-controlled PCB with turnkey assembly. The FSC-BT1035 Bluetooth module is sourced from Feasycom -(the footprint uses a BT806-compatible, pin-identical land pattern). See the -[Technical Manual](Software/docs/manual/manual.tex) hardware chapter and manufacturing -notes for fabrication settings and MSL-3 handling of the BT module. +(the footprint uses a BT806-compatible, pin-identical land pattern). Component +datasheets for design review live in +[`Hardware/DATASHEET/`](Hardware/DATASHEET/) (Si4684-A10, AN851, ADAU1701, FSC-BT1035). +See the [Technical Manual](Software/docs/manual/manual.tex) hardware chapter +and manufacturing notes for fabrication settings and MSL-3 handling of the BT +module. --- diff --git a/Software/.cursor/rules/20-drivers.mdc b/Software/.cursor/rules/20-drivers.mdc index 7b24947..005ed56 100644 --- a/Software/.cursor/rules/20-drivers.mdc +++ b/Software/.cursor/rules/20-drivers.mdc @@ -29,6 +29,6 @@ Full spec: @AGENTS.md §7.1–7.3. ## FSC-BT1035 (QCC3056, AT over UART) - Typed command builder; explicit OK/ERROR/timeout parsing. -- AT+AUXCFG=1 (Line-In) is mandatory in the init sequence and covered by - a test on the command string. Unknown responses are an error, not - ignored. +- AT+AUXCFG=3 and AT+I2SCFG=67 (I2S slave 48 kHz) are mandatory in the + init sequence and covered by host tests. Unknown responses are an error, + not ignored. diff --git a/Software/AGENTS.md b/Software/AGENTS.md index 6539fb3..ec9e386 100644 --- a/Software/AGENTS.md +++ b/Software/AGENTS.md @@ -397,9 +397,11 @@ Layered, dependencies point inward only: - Controlled by AT commands over UART. Build commands with a typed builder; parse responses with explicit `OK`/`ERROR`/timeout handling. -- **Line-In mode is mandatory:** the `AT+AUXCFG=1` step must be part of - the documented init sequence and covered by a test on the command - string. Losing it silently breaks the audio path. +- **I\textsuperscript{2}S slave mode is mandatory:** `AT+AUXCFG=3` and + `AT+I2SCFG=67` (Feasycom programming guide §5.1.25 / §5.1.4) must be part + of the documented init sequence and covered by host tests. The PCB routes + ADAU I\textsuperscript{2}S to the module; `AT+AUXCFG=1` (Line-In) does not + match the schematic. - The AT subset in use is enumerated and documented; unknown responses are an error value, not ignored. diff --git a/Software/README.md b/Software/README.md index 0220e76..306d520 100644 --- a/Software/README.md +++ b/Software/README.md @@ -2,14 +2,14 @@ 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, +**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 T1–T12 complete; device HIL pending first PCB. -| Area | Shipped in 0.8.4 | +| Area | Shipped in 0.8.5 | |------|------------------| -| Boot | Si4684 HOST_LOAD, ADAU1701 RAM program, BT1035 Line-In init | +| 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 | @@ -85,7 +85,7 @@ 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) +## HTTP API (fw 0.8.5) | Method | Path | Purpose | |--------|------|---------| diff --git a/Software/components/core/include/core/Bt1035At.hpp b/Software/components/core/include/core/Bt1035At.hpp index 1d0ffa4..9d6606e 100644 --- a/Software/components/core/include/core/Bt1035At.hpp +++ b/Software/components/core/include/core/Bt1035At.hpp @@ -37,7 +37,8 @@ namespace core { */ enum class Bt1035AtCommand { Ping, ///< AT — link check. - AuxLineIn, ///< AT+AUXCFG=1 — wired Line-In from ADAU1701 (mandatory). + I2sMode, ///< AT+AUXCFG=3 — I2S input from ADAU1701 (mandatory). + I2sSlave48k32, ///< AT+I2SCFG=67 — I2S slave 48 kHz 32-bit (§5.1.4). PairDiscoverable, ///< AT+PAIR=1 — enter BR/EDR/BLE discoverable mode. PairHidden, ///< AT+PAIR=0 — leave discoverable mode. A2dpStat, ///< AT+A2DPSTAT — read A2DP link state. @@ -83,7 +84,10 @@ enum class Bt1035AtResponseKind { }; /** Number of commands in bootInitSequence(). */ -inline constexpr std::size_t kBt1035BootInitCommandCount = 2U; +inline constexpr std::size_t kBt1035BootInitCommandCount = 3U; + +/** Feasycom programming guide §5.1.4: I2S slave, 48 kHz, 32-bit. */ +inline constexpr std::uint8_t kBt1035I2sSlave48k32Param = 67U; /** * @brief buildBt1035AtLine — serialise a command with CRLF terminator. @@ -102,7 +106,7 @@ inline constexpr std::size_t kBt1035BootInitCommandCount = 2U; * @brief bootInitSequence — mandatory bring-up commands in order. * * @dname bootInitSequence - * @return Ping then AuxLineIn (AT+AUXCFG=1). + * @return Ping, I2sMode (AUXCFG=3), I2sSlave48k32 (I2SCFG=67). * @pubstate none * * @author Michele Bigi @@ -165,6 +169,21 @@ parseBt1035A2dpStatResponse(std::string_view response); */ [[nodiscard]] std::string buildBt1035SetAutoConnLine(std::uint8_t times); +/** + * @brief buildBt1035SetNameLine — AT+NAME with optional MAC suffix flag. + * + * @dname buildBt1035SetNameLine + * @param name BR/EDR local name (1--31 ASCII bytes per Feasycom §5.1.16). + * @param enableMacSuffix false sends Param2=0 (disable module suffix). + * @return Full AT line including CRLF. + * @pubstate none + * + * @author Michele Bigi + * @date 2026-07-08 + */ +[[nodiscard]] std::string buildBt1035SetNameLine(std::string_view name, + bool enableMacSuffix = false); + /** * @brief parseBt1035NameResponse — extract +NAME= value. * diff --git a/Software/components/core/include/core/CompanionChipStatus.hpp b/Software/components/core/include/core/CompanionChipStatus.hpp index 7dc760f..f3fc396 100644 --- a/Software/components/core/include/core/CompanionChipStatus.hpp +++ b/Software/components/core/include/core/CompanionChipStatus.hpp @@ -27,7 +27,7 @@ namespace core { struct CompanionChipStatus { bool si4684Ready; ///< Si4684 HOST_LOAD completed. bool adau1701Ready; ///< ADAU1701 SigmaStudio download completed. - bool bt1035Ready; ///< BT1035 Line-In init (AT+AUXCFG=1) completed. + bool bt1035Ready; ///< BT1035 I2S init (AUXCFG=3 + I2SCFG=67) completed. }; } // namespace core diff --git a/Software/components/core/src/Bt1035At.cpp b/Software/components/core/src/Bt1035At.cpp index 8b5b186..8af6881 100644 --- a/Software/components/core/src/Bt1035At.cpp +++ b/Software/components/core/src/Bt1035At.cpp @@ -101,8 +101,10 @@ std::string buildBt1035AtLine(Bt1035AtCommand command) switch (command) { case Bt1035AtCommand::Ping: return "AT\r\n"; - case Bt1035AtCommand::AuxLineIn: - return "AT+AUXCFG=1\r\n"; + case Bt1035AtCommand::I2sMode: + return "AT+AUXCFG=3\r\n"; + case Bt1035AtCommand::I2sSlave48k32: + return "AT+I2SCFG=67\r\n"; case Bt1035AtCommand::PairDiscoverable: return "AT+PAIR=1\r\n"; case Bt1035AtCommand::PairHidden: @@ -129,11 +131,21 @@ std::string buildBt1035SetAutoConnLine(std::uint8_t times) return "AT+AUTOCONN=" + std::to_string(times) + "\r\n"; } +std::string buildBt1035SetNameLine(std::string_view name, bool enableMacSuffix) +{ + if (name.empty() || name.size() > 31U) { + return {}; + } + return std::string("AT+NAME=") + std::string(name) + "," + + (enableMacSuffix ? "1" : "0") + "\r\n"; +} + std::array bootInitSequence() noexcept { return std::array{ Bt1035AtCommand::Ping, - Bt1035AtCommand::AuxLineIn, + Bt1035AtCommand::I2sMode, + Bt1035AtCommand::I2sSlave48k32, }; } diff --git a/Software/components/core/test/bt1035_at_test.cpp b/Software/components/core/test/bt1035_at_test.cpp index bdc24b1..b6f9300 100644 --- a/Software/components/core/test/bt1035_at_test.cpp +++ b/Software/components/core/test/bt1035_at_test.cpp @@ -28,13 +28,24 @@ namespace { std::cerr << "init sequence size mismatch\n"; return EXIT_FAILURE; } - if (sequence[1U] != core::Bt1035AtCommand::AuxLineIn) { - std::cerr << "AUXCFG=1 must be in init sequence\n"; + if (sequence[1U] != core::Bt1035AtCommand::I2sMode) { + std::cerr << "I2S mode must be in init sequence\n"; return EXIT_FAILURE; } - const std::string aux = core::buildBt1035AtLine(core::Bt1035AtCommand::AuxLineIn); - if (aux != "AT+AUXCFG=1\r\n") { - std::cerr << "AUXCFG command line mismatch\n"; + if (sequence[2U] != core::Bt1035AtCommand::I2sSlave48k32) { + std::cerr << "I2SCFG must be in init sequence\n"; + return EXIT_FAILURE; + } + const std::string i2sMode = + core::buildBt1035AtLine(core::Bt1035AtCommand::I2sMode); + if (i2sMode != "AT+AUXCFG=3\r\n") { + std::cerr << "AUXCFG=3 command line mismatch\n"; + return EXIT_FAILURE; + } + const std::string i2sCfg = + core::buildBt1035AtLine(core::Bt1035AtCommand::I2sSlave48k32); + if (i2sCfg != "AT+I2SCFG=67\r\n") { + std::cerr << "I2SCFG=67 command line mismatch\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; @@ -117,6 +128,11 @@ namespace { std::cerr << "AUTOCONN command line mismatch\n"; return EXIT_FAILURE; } + if (core::buildBt1035SetNameLine("DigiRadio-A1B2", false) + != "AT+NAME=DigiRadio-A1B2,0\r\n") { + std::cerr << "NAME set command line mismatch\n"; + return EXIT_FAILURE; + } return EXIT_SUCCESS; } diff --git a/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp b/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp index b1eebb4..2c62d70 100644 --- a/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp +++ b/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp @@ -48,7 +48,7 @@ struct Bt1035Pins { * @dname Bt1035Driver * @return n/a (type) * @pubstate Owns UART port after boot(). booted_ true after init sequence - * including AT+AUXCFG=1 (Line-In from ADAU1701). + * including AT+AUXCFG=3 and AT+I2SCFG=67 (I2S slave from ADAU1701). * * @author Michele Bigi * @date 2026-07-06 @@ -86,7 +86,7 @@ public: * * @dname boot * @return Ok on success, or Bt1035Error. - * @pubstate sets booted_ after Ping + AT+AUXCFG=1 both return OK. + * @pubstate sets booted_ after Ping + I2S init both return OK. * * Sequence: hardware reset, UART @ 115200 with RTS/CTS, then * core::bootInitSequence() (see manual chapter bt1035). @@ -97,7 +97,7 @@ public: [[nodiscard]] std::expected boot(); /** - * @brief isBooted — query whether Line-In init succeeded. + * @brief isBooted — query whether I2S init succeeded. * * @dname isBooted * @return true after successful boot(). @@ -177,7 +177,7 @@ public: * @dname setDeviceName * @param name Bluetooth name (Feasycom FSC-BT1035 AT+NAME command). * @return Ok on success, or Bt1035Error. - * @pubstate sends AT+NAME after boot; does not alter AT+AUXCFG=1 init. + * @pubstate sends AT+NAME after boot; does not alter I2S init sequence. * * @author Michele Bigi * @date 2026-07-07 diff --git a/Software/components/drivers/bt1035/src/Bt1035Driver.cpp b/Software/components/drivers/bt1035/src/Bt1035Driver.cpp index 5d3723f..4be7b23 100644 --- a/Software/components/drivers/bt1035/src/Bt1035Driver.cpp +++ b/Software/components/drivers/bt1035/src/Bt1035Driver.cpp @@ -169,10 +169,14 @@ std::expected Bt1035Driver::setDeviceName( if (auto ready = ensureBooted(); !ready) { return ready; } - if (name.empty() || name.size() > 32U) { + if (name.empty() || name.size() > 31U) { + return std::unexpected(Bt1035Error::UnexpectedResponse); + } + const std::string line = + core::buildBt1035SetNameLine(name, false); + if (line.empty()) { return std::unexpected(Bt1035Error::UnexpectedResponse); } - std::string line = std::string("AT+NAME=") + std::string(name) + "\r\n"; return transmitAndExpectOk(line); } @@ -315,7 +319,7 @@ std::expected Bt1035Driver::boot() } booted_ = true; - ESP_LOGI(kTag, "Line-In mode enabled (AT+AUXCFG=1)"); + ESP_LOGI(kTag, "I2S slave mode enabled (AT+AUXCFG=3, AT+I2SCFG=67)"); return {}; } diff --git a/Software/components/net/src/SetupWebServer.cpp b/Software/components/net/src/SetupWebServer.cpp index 40ff5a5..c8685aa 100644 --- a/Software/components/net/src/SetupWebServer.cpp +++ b/Software/components/net/src/SetupWebServer.cpp @@ -60,7 +60,7 @@ namespace net { namespace { constexpr char kTag[] = "SetupWebServer"; -constexpr char kFirmwareVersion[] = "0.8.4"; +constexpr char kFirmwareVersion[] = "0.8.5"; constexpr unsigned kRebootDelaySec = 3; extern const uint8_t www_index_html_gz_start[] asm( diff --git a/Software/docs/TODO.md b/Software/docs/TODO.md index 37d4397..e0ec57a 100644 --- a/Software/docs/TODO.md +++ b/Software/docs/TODO.md @@ -3,7 +3,7 @@ Agent task list and hardware-in-the-loop backlog. Working directory for all commands is `Software/`. -**Current firmware:** `0.8.4` — dual OTA + DSP blob updates, EEPROM identity, +**Current firmware:** `0.8.5` — BT1035 I2S slave boot init, dual OTA + DSP blob updates, EEPROM identity, NVS + flash encryption (dev mode), tabbed Web UI with System uploads, CI gate (4 jobs). @@ -14,7 +14,7 @@ NVS + flash encryption (dev mode), tabbed Web UI with System uploads, CI gate --- -## Completed agent tasks (T1–T12, fw 0.7.1–0.8.4) +## Completed agent tasks (T1–T12, fw 0.7.1–0.8.5) | Task | Version | Summary | |------|---------|---------| @@ -31,6 +31,10 @@ NVS + flash encryption (dev mode), tabbed Web UI with System uploads, CI gate | **T11** | 0.8.4 | Updatable ADAU1701 program — `POST /api/dsp/program`, DRAD blob | | **T12** | 0.8.4 | ESP32 OTA — `POST /api/system/ota`, rollback confirm on boot | +**0.8.5** (hardware/doc alignment): BT1035 boot uses `AT+AUXCFG=3` + +`AT+I2SCFG=67` (I2S from ADAU1701, not Line-In); I2C pull-ups R1/R16 +confirmed 2\,kΩ; `Hardware/DATASHEET/` bundle + manual cross-refs. + Also landed (not numbered): BT1035 pairing (`BluetoothService`), station presets (fw 0.7.0), companion-chip boot (Slice 3), ADAU1701 runtime (Slice 5). @@ -62,13 +66,14 @@ a sacrificial unit; confirm RELEASE mode policy before shipping. ## Open firmware polish (non-blocking) -Done in fw 0.8.4 unless noted: +Done in fw 0.8.5 unless noted: -- FM seek down — `POST /api/tuner/seek` with `{"direction":"down"}`. +- BT1035 boot — I2S slave init (`AT+AUXCFG=3`, `AT+I2SCFG=67`) per PCB routing (0.8.5). +- FM seek down — `POST /api/tuner/seek` with `{"direction":"down"}` (0.8.4). - BT1035 — query/set name, paired list (`AT+PLIST`), auto-reconnect - (`AT+AUTOCONN`) per Feasycom BT1035 manual. + (`AT+AUTOCONN`) per Feasycom BT1035 manual (0.8.4). - Si4684 — `STOP_DIGITAL_SERVICE` (0x82) before FM band switch when DAB - audio is active; ensemble metrics remain via `DAB_DIGRAD_STATUS` in status. + audio is active; ensemble metrics remain via `DAB_DIGRAD_STATUS` in status (0.8.4). --- diff --git a/Software/docs/manual/ch-api.tex b/Software/docs/manual/ch-api.tex index 3922b41..d13caa1 100644 --- a/Software/docs/manual/ch-api.tex +++ b/Software/docs/manual/ch-api.tex @@ -6,7 +6,7 @@ implemented in \texttt{SetupWebServer}. Request bodies are parsed into domain types in the pure core (\texttt{components/core}) before any persistence or driver call. Exact C++ signatures live in the generated Doxygen output under \texttt{docs/api/}; this chapter documents the -wire protocol and behaviour as shipped in firmware~0.8.4. +wire protocol and behaviour as shipped in firmware~0.8.5. \section{Transport and reachability} @@ -36,7 +36,7 @@ Returns a health-check DTO serialised by \begin{drnote}[Response schema] \begin{drcode}[JSON] -{"status":"ok","fw":"0.8.4","serialNumber":"0004A3123456", +{"status":"ok","fw":"0.8.5","serialNumber":"0004A3123456", "chips":{"si4684":true,"adau1701":true,"bt1035":true}} \end{drcode} \begin{itemize} diff --git a/Software/docs/manual/ch-bt1035.tex b/Software/docs/manual/ch-bt1035.tex index 0d83fa5..591fe1a 100644 --- a/Software/docs/manual/ch-bt1035.tex +++ b/Software/docs/manual/ch-bt1035.tex @@ -9,8 +9,8 @@ The Feasycom FSC-BT1035 (Qualcomm QCC3056) is the wireless output stage of DigiRadio: it receives PCM from the ADAU1701 over I\textsuperscript{2}S and streams Bluetooth audio with aptX, aptX~HD, and aptX~Adaptive. This chapter documents how the ESP32-S3 controls the module over UART (AT commands with -RTS/CTS), why Line-In mode is mandatory, and how \texttt{bt1035::Bt1035Driver} -implements the bring-up sequence. +RTS/CTS), why I\textsuperscript{2}S slave mode is mandatory, and how +\texttt{bt1035::Bt1035Driver} implements the bring-up sequence. \begin{drref}[Hardware context] Board wiring (UART pins, I\textsuperscript{2}S to the module, flow control) @@ -29,9 +29,10 @@ samples for Bluetooth; it only configures the module so the wired path is accepted and encoded for transmission. Without firmware init the module may stay in a default mode that ignores the -Line-In from the DSP. The mandatory \texttt{AT+AUXCFG=1} command selects -auxiliary/Line-In input --- omitting it silently breaks the entire wireless -output (Section~\ref{sec:bt1035-linein}). +I\textsuperscript{2}S bus from the ADAU1701. The mandatory +\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67} commands select I\textsuperscript{2}S +slave input at 48\,kHz --- omitting them silently breaks the entire wireless +output (Section~\ref{sec:bt1035-i2s}). \section{Control interface} \label{sec:bt1035-uart} @@ -67,17 +68,19 @@ Every command expects a module reply containing \texttt{OK} or \end{itemize} Host tests in \texttt{components/core/test/bt1035\_at\_test.cpp} lock the -init sequence (including \texttt{AT+AUXCFG=1}) and the parser. +init sequence (\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=67}) and the parser. -\section{Mandatory Line-In mode} -\label{sec:bt1035-linein} +\section{Mandatory I\textsuperscript{2}S slave mode} +\label{sec:bt1035-i2s} -\begin{drcaution}[AT+AUXCFG=1 is not optional] -The documented init sequence must include \texttt{AT+AUXCFG=1} after a -successful \texttt{AT} ping. This tells the QCC3056 firmware to take audio -from the wired I\textsuperscript{2}S/Line-In port (the ADAU1701 output) -rather than an internal source. AGENTS.md and the hardware manual both treat -skipping this step as a production bug. +\begin{drcaution}[I\textsuperscript{2}S init is not optional] +The board routes ADAU1701 \texttt{SDATA\_OUT0} (MP6) to the module PCM input +with shared BCLK/LRCLK (see Chapter~\ref{ch:hardware}). The init sequence +must therefore use \texttt{AT+AUXCFG=3} (I\textsuperscript{2}S mode per +programming guide §5.1.25) followed by \texttt{AT+I2SCFG=67} (I\textsuperscript{2}S +slave, 48\,kHz, 32-bit per §5.1.4). \texttt{AT+AUXCFG=1} (Line-In) does +\textbf{not} match the schematic. AGENTS.md treats skipping I\textsuperscript{2}S +init as a production bug. \end{drcaution} \section{Boot sequence} @@ -85,7 +88,7 @@ skipping this step as a production bug. At power-up \texttt{HardwareBootstrap::boot()} runs the Si4684 and ADAU1701 first, applies the saved audio profile, then initialises the BT1035 so the -Line-In path is ready before Wi-Fi starts. +I\textsuperscript{2}S path is ready before Wi-Fi starts. \begin{figure}[htbp] \centering @@ -99,10 +102,12 @@ Line-In path is ready before Wi-Fi starts. \node[drstep, fill=black!8, below=of uart] (at) {Send \texttt{AT} --- expect OK}; \node[drstep, fill=black!10, below=of at] (aux) - {Send \texttt{AT+AUXCFG=1} --- expect OK (Line-In)}; - \node[drstep, fill=black!6, below=of aux] (done) + {Send \texttt{AT+AUXCFG=3} --- expect OK (I\textsuperscript{2}S)}; + \node[drstep, fill=black!10, below=of aux] (i2s) + {Send \texttt{AT+I2SCFG=67} --- expect OK (slave 48\,kHz)}; + \node[drstep, fill=black!6, below=of i2s] (done) {\texttt{Bt1035Driver::isBooted()} = true}; - \foreach \a/\b in {sys/uart, uart/at, at/aux, aux/done} { + \foreach \a/\b in {sys/uart, uart/at, at/aux, aux/i2s, i2s/done} { \draw[->] (\a) -- (\b); } \end{tikzpicture} @@ -112,7 +117,7 @@ Line-In path is ready before Wi-Fi starts. Pairing, codec selection, and volume over Bluetooth are handled by the module's own firmware and NVS; DigiRadio firmware currently implements -only the Line-In bring-up required for the wired audio path. +only the I\textsuperscript{2}S bring-up required for the wired audio path. \section{Software architecture} \label{sec:bt1035-stack} @@ -139,30 +144,38 @@ only the Line-In bring-up required for the wired audio path. \section{Supported AT command subset} \label{sec:bt1035-at} -The firmware enumerates every command it sends. Extending the subset requires -updating \texttt{core::Bt1035AtCommand}, the manual, and a host test. +The firmware enumerates every command it sends. Wire formats follow +\texttt{Hardware/DATASHEET/FSC-BT1035\_programming\_user\_guide\_1.1.1.pdf} +(§5 commands, §6 events). Extending the subset requires updating +\texttt{core::Bt1035AtCommand}, the manual, and a host test. \begin{table}[htbp] \centering - \begin{tabular}{@{}lll@{}} - \drhead Enum & Line sent & Purpose \\ + \small + \begin{tabular}{@{}llp{5.2cm}@{}} + \drhead Enum & Line sent & Programming guide \\ \midrule - \texttt{Ping} & \texttt{AT} & Verify UART link \\ - \texttt{AuxLineIn} & \texttt{AT+AUXCFG=1} & Enable Line-In from ADAU \\ - \texttt{PairDiscoverable} & \texttt{AT+PAIR=1} & Enter discoverable mode \\ - \texttt{PairHidden} & \texttt{AT+PAIR=0} & Leave discoverable mode \\ - \texttt{A2dpStat} & \texttt{AT+A2DPSTAT} & Read link state \\ - \texttt{A2dpDisconnect} & \texttt{AT+A2DPDISC} & Release A2DP session \\ - \texttt{QueryName} & \texttt{AT+NAME} & Read module friendly name \\ - \texttt{QueryAutoConn} & \texttt{AT+AUTOCONN} & Read auto-reconnect count \\ - \texttt{QueryPairedList} & \texttt{AT+PLIST} & List paired remotes \\ + \texttt{Ping} & \texttt{AT} & link check \\ + \texttt{I2sMode} & \texttt{AT+AUXCFG=3} & §5.1.25 Param=3 I2S \\ + \texttt{I2sSlave48k32} & \texttt{AT+I2SCFG=67} & §5.1.4 slave 48\,kHz 32-bit \\ + \texttt{PairDiscoverable} & \texttt{AT+PAIR=1} & §5.1.20 enter discoverable \\ + \texttt{PairHidden} & \texttt{AT+PAIR=0} & §5.1.20 leave discoverable \\ + \texttt{A2dpStat} & \texttt{AT+A2DPSTAT} & §5.3.1; states 1--5 \\ + \texttt{A2dpDisconnect} & \texttt{AT+A2DPDISC} & §5.3.3 \\ + \texttt{QueryName} & \texttt{AT+NAME} & §5.1.16 read \texttt{+NAME=} \\ + \texttt{QueryAutoConn} & \texttt{AT+AUTOCONN} & §5.1.11 read \texttt{+AUTOCONN=} \\ + \texttt{QueryPairedList} & \texttt{AT+PLIST} & §5.1.22; ends with \texttt{+PLIST=E} \\ \bottomrule \end{tabular} \caption{Enumerated AT commands (\texttt{core::Bt1035AtCommand}). Boot - uses Ping + AuxLineIn only; pairing commands are runtime.} + uses Ping + I2sMode + I2sSlave48k32; pairing commands are runtime.} \label{tab:bt1035-at} \end{table} +Boot also calls \texttt{AT+NAME=,0} and \texttt{AT+AUTOCONN=3} +from \texttt{hardware\_bootstrap.cpp} (§5.1.16 suffix disabled, §5.1.11 +reconnect count). + \section{Bt1035Driver API} \label{sec:bt1035-driver} @@ -176,7 +189,7 @@ updating \texttt{core::Bt1035AtCommand}, the manual, and a host test. \textbf{Method} & \textbf{Purpose} \\ \midrule \texttt{boot()} & Reset, UART init, run \texttt{bootInitSequence()} \\ - \texttt{isBooted()} & \texttt{true} after Line-In init succeeded \\ + \texttt{isBooted()} & \texttt{true} after I\textsuperscript{2}S init succeeded \\ \texttt{sendCommand(cmd)} & Send one typed command, expect OK \\ \texttt{enterPairingMode()} & \texttt{AT+PAIR=1} \\ \texttt{leavePairingMode()} & \texttt{AT+PAIR=0} \\ @@ -226,19 +239,22 @@ Boot order: \item Si4684 \texttt{boot(Dab)} --- tuner image in RAM. \item ADAU1701 \texttt{boot()} --- SigmaStudio program in RAM. \item \texttt{AudioService::loadAndApply()} --- user mixer/EQ profile. - \item BT1035 \texttt{boot()} --- Line-In enabled for wireless output. + \item BT1035 \texttt{boot()} --- I\textsuperscript{2}S slave enabled for wireless output. \end{enumerate} \section{Typical usage (firmware developer)} \label{sec:bt1035-usage} After a successful \texttt{HardwareBootstrap::boot()}, the module is ready; -no further calls are required for basic listening. To re-send Line-In config +no further calls are required for basic listening. To re-send I\textsuperscript{2}S config after a module reset: \begin{verbatim} bt1035::Bt1035Driver& bt = ...; -if (auto r = bt.sendCommand(core::Bt1035AtCommand::AuxLineIn); !r) { +if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sMode); !r) { + // handle Bt1035Error +} +if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sSlave48k32); !r) { // handle Bt1035Error } \end{verbatim} @@ -247,9 +263,12 @@ if (auto r = bt.sendCommand(core::Bt1035AtCommand::AuxLineIn); !r) { \label{sec:bt1035-reading} \begin{itemize} - \item Feasycom FSC-BT1035 AT command manual (vendor) --- full command set; - firmware wraps name, paired list, and auto-reconnect for the Web UI. + \item \texttt{Hardware/DATASHEET/FSC-BT1035\_programming\_user\_guide\_1.1.1.pdf} + --- authoritative AT command and event reference (§5--§6). + \item \texttt{Hardware/DATASHEET/FSC-BT1035\_Datasheet\_EN.pdf} --- module + electrical and pinout specification. \item Chapter~\ref{ch:hardware} --- pin map and I\textsuperscript{2}S routing. \item Chapter~\ref{ch:adau1701} --- DSP output that feeds the module. - \item \texttt{components/core/test/bt1035\_at\_test.cpp} --- init sequence test. + \item \texttt{components/core/test/bt1035\_at\_test.cpp} --- init sequence + and parser host tests. \end{itemize} diff --git a/Software/docs/manual/ch-classes.tex b/Software/docs/manual/ch-classes.tex index 6ad6764..8b34c2e 100644 --- a/Software/docs/manual/ch-classes.tex +++ b/Software/docs/manual/ch-classes.tex @@ -16,7 +16,7 @@ added in the same change that introduces the class. A tooling check keeps this chapter in step with the code, so it is always current. \end{drnote} -The class reference tracks firmware~0.8.4 on \texttt{main}. Public classes +The class reference tracks firmware~0.8.5 on \texttt{main}. Public classes are grouped by layer: domain core, application services, and hardware drivers. % ------------------------------------------------------------------ @@ -163,7 +163,8 @@ index (1--8), 12-digit MAC, and optional friendly name. Parsed by \section{Bt1035Driver}\label{cls:Bt1035Driver} UART driver for the FSC-BT1035 (Chapter~\ref{ch:bt1035}). \texttt{boot()} pulses RESET\#, opens UART2 with RTS/CTS, and runs -\texttt{core::bootInitSequence()} (Ping + \texttt{AT+AUXCFG=1}). Returns +\texttt{core::bootInitSequence()} (Ping + \texttt{AT+AUXCFG=3} + +\texttt{AT+I2SCFG=67}). Returns \texttt{Bt1035Error} on timeout, ERROR response, or UART failure. \section{Adau1701Driver}\label{cls:Adau1701Driver} diff --git a/Software/docs/manual/ch-firmware.tex b/Software/docs/manual/ch-firmware.tex index 6dda091..55e782a 100644 --- a/Software/docs/manual/ch-firmware.tex +++ b/Software/docs/manual/ch-firmware.tex @@ -150,9 +150,10 @@ safeload registers. \paragraph{FSC-BT1035 (Bluetooth).} The module is controlled by AT commands over UART with hardware flow control. -The initialisation sequence includes enabling Line-In mode -(\texttt{AT+AUXCFG=1}), which is required for the wired audio path from the -DSP; command responses are parsed explicitly, with timeouts treated as errors. +The initialisation sequence includes enabling I\textsuperscript{2}S slave mode +(\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}), which is required for the +wired digital audio path from the ADAU1701 to the module. +Command responses are parsed explicitly, with timeouts treated as errors. Full driver API, boot flow, and error codes are in Chapter~\ref{ch:bt1035}. \section{Configuration, storage, and user interface} @@ -216,7 +217,8 @@ bring-up: restores the saved \texttt{core::AudioProfile} from NVS (or factory defaults) via ADAU1701 safeload before network bring-up. \item \textbf{FSC-BT1035} (UART): \texttt{bt1035::Bt1035Driver::boot()} - enables Line-In (\texttt{AT+AUXCFG=1}) after the DSP path is configured + enables I\textsuperscript{2}S slave mode (\texttt{AT+AUXCFG=3}, + \texttt{AT+I2SCFG=67}) after the DSP path is configured (Chapter~\ref{ch:bt1035}). \end{enumerate} diff --git a/Software/docs/manual/ch-hardware.tex b/Software/docs/manual/ch-hardware.tex index 6ddd3e4..0fb0bba 100644 --- a/Software/docs/manual/ch-hardware.tex +++ b/Software/docs/manual/ch-hardware.tex @@ -128,11 +128,11 @@ control (RTS/CTS). The host sends commands and parses the module's responses explicitly, treating timeouts as errors. Some settings are held in the module's own non-volatile memory. -\begin{drcaution}[Line-In mode] -The initialisation sequence must enable Line-In mode (\texttt{AT+AUXCFG=1}) -so the module accepts the wired audio coming from the DSP. Omitting it -silently breaks the audio path. Driver boot flow and AT subset are documented -in Chapter~\ref{ch:bt1035}. +\begin{drcaution}[I\textsuperscript{2}S slave mode] +The PCB routes ADAU1701 \texttt{SDATA\_OUT0} to the module over +I\textsuperscript{2}S (BCLK, LRCLK, PCM data). Firmware must initialise +\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}, not Line-In +(\texttt{AT+AUXCFG=1}). See Chapter~\ref{ch:bt1035}, Section~\ref{sec:bt1035-i2s}. \end{drcaution} \section{The host: ESP32-S3} @@ -379,16 +379,19 @@ satisfying the +10\,\textmu s requirement in practice. INTB & interrupt line, external pull-up (GPIO39) & verified \\ \bottomrule \end{tabular} - \caption{Si4684 design checks. References: Si4684-A10 datasheet - (power sequencing, SPI, supply levels) and AN851 (bypass and layout).} + \caption{Si4684 design checks. References: + \texttt{Hardware/DATASHEET/SI4684-A10.pdf} (power sequencing, SPI, + supply levels) and \texttt{Hardware/DATASHEET/AN851.pdf} (bypass and layout).} \label{tab:hw-val-si4684} \end{table} \begin{drref}[Datasheet] -Power sequencing and SPI framing: Si4684-A10 data sheet. The user must not -pulse SSB high between bytes; SSB frames the whole command/reply. -Bypass-capacitor values and placement: AN851, \emph{Si468x Schematic and -Layout Guide}. Boot and command protocol: AN649. +Power sequencing and SPI framing: +\texttt{Hardware/DATASHEET/SI4684-A10.pdf}. The user must not pulse SSB high +between bytes; SSB frames the whole command/reply. +Bypass-capacitor values and placement: \texttt{Hardware/DATASHEET/AN851.pdf}, +\emph{Si468x Schematic and Layout Guide}. Boot and command protocol: AN649. +Local copies: \texttt{Hardware/DATASHEET/} (see \texttt{README.md}). \end{drref} The core, memory, and analogue rails run at 1.8\,V; only VIO is at 3.3\,V @@ -419,7 +422,7 @@ locks up when input data arrives. Output master & serial output port in Master Mode & verified \\ MCLK ratio & exactly 256\,$\times$\,f\textsubscript{S} & verified \\ MCLK value & 12.288\,MHz oscillator (256\,$\times$\,48\,kHz) & verified \\ - I\textsuperscript{2}C pull-ups & 2.2\,k\(\Omega\) on SDA and SCL & to verify \\ + I\textsuperscript{2}C pull-ups & 2\,k\(\Omega\) on SDA/SCL (R16/R1) & verified \\ I\textsuperscript{2}C address & 0x34 (ADDR0 = ADDR1 = GND) & verified \\ Self-boot & disabled (host RAM load) & verified \\ \bottomrule @@ -430,17 +433,18 @@ locks up when input data arrives. \end{table} \begin{drref}[Datasheet] -Master-clock loopback and serial-port modes: ADAU1701 data sheet, Rev.~C, -Table~63 and the serial-port section. The master clock must be exactly -256\,$\times$\,f\textsubscript{S}. I\textsuperscript{2}C lines require -2.2\,k\(\Omega\) pull-ups. +Master-clock loopback and serial-port modes: +\texttt{Hardware/DATASHEET/adau1701.pdf}, Rev.~C, Table~63 and the serial-port +section. The master clock must be exactly 256\,$\times$\,f\textsubscript{S}. +I\textsuperscript{2}C lines require pull-up resistors on SDA and SCL. +DigiRadio uses \textbf{2\,k\(\Omega\)} (schematic R1/R16; ADAU1701 datasheet +recommends 2.2\,k\(\Omega\) --- acceptable at 3.3\,V). \end{drref} -\begin{drcaution}[Open item --- I\textsuperscript{2}C pull-ups] -The ADAU1701 datasheet requires 2.2\,k\(\Omega\) pull-up resistors on SDA -and SCL. Confirm these are present on the board (shared bus with the -EEPROM); this is the one ADAU check still to close. -\end{drcaution} +\begin{drnote}[I\textsuperscript{2}C pull-ups confirmed] +Schematic \texttt{DigitalRadio.pdf} and BOM list R1 (SCL) and R16 (SDA) as +2\,k\(\Omega\) to 3V3 on the shared ADAU1701/EEPROM bus. +\end{drnote} \subsection{FSC-BT1035 (QCC3056)} \label{sec:hw-val-bt1035} @@ -452,17 +456,24 @@ EEPROM); this is the one ADAU check still to close. \drhead Check & Requirement & Status \\ \midrule UART with flow control & TX/RX + RTS/CTS wired & verified \\ - Line-In enable (firmware) & \texttt{AT+AUXCFG=1} in init & driver rule \\ + I\textsuperscript{2}S init (firmware) & \texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=67} & driver rule \\ I\textsuperscript{2}S input & receives clocks from ADAU master (slave) & verified \\ Reset line & RESET (GPIO17) driven by ESP32 & verified \\ \bottomrule \end{tabular} - \caption{FSC-BT1035 design checks. The module is an I\textsuperscript{2}S - slave fed by the ADAU master; audio arrives on its I\textsuperscript{2}S - input pins.} + \caption{FSC-BT1035 design checks. Datasheet: + \texttt{Hardware/DATASHEET/FSC-BT1035\_Datasheet\_EN.pdf}; AT commands: + \texttt{FSC-BT1035\_programming\_user\_guide\_1.1.1.pdf} §5.} \label{tab:hw-val-bt1035} \end{table} +\begin{drref}[Programming guide defaults] +Module defaults (Feasycom release 1.1.1): BR/EDR name \texttt{FSC-BT1035}, +UART 115200/8/N/1, SSP on, auto-reconnect configurable via +\texttt{AT+AUTOCONN=0..15}. DigiRadio overrides the name from EEPROM identity +(\texttt{AT+NAME=,0} disables the module MAC suffix). +\end{drref} + \subsection{System-level summary} \label{sec:hw-val-summary} @@ -474,7 +485,7 @@ EEPROM); this is the one ADAU check still to close. Si4684 & RSTB pull-down, 1.8\,V rails, bypass caps, SPI & verified \\ Si4684 & RSTB release only in \texttt{Si4684Driver::boot()} & verified \\ ADAU1701 & clock loopback, master mode, 256$\times$f\textsubscript{S} & verified \\ - ADAU1701 & I\textsuperscript{2}C 2.2\,k\(\Omega\) pull-ups & to verify \\ + ADAU1701 & I\textsuperscript{2}C 2\,k\(\Omega\) pull-ups (R1/R16) & verified \\ FSC-BT1035 & UART + flow control, I\textsuperscript{2}S slave & verified \\ Clocking & single 48\,kHz domain, ADAU master & verified \\ \bottomrule @@ -483,6 +494,6 @@ EEPROM); this is the one ADAU check still to close. \label{tab:hw-val-summary} \end{table} -The only open hardware check is the presence of the 2.2\,k\(\Omega\) -I\textsuperscript{2}C pull-ups on the ADAU/EEPROM bus. All damage-class -items (Si4684 power sequencing and supply levels) are verified. +The consolidated pre-fabrication validation is complete for all +damage-class items (Si4684 power sequencing and supply levels) and the +shared I\textsuperscript{2}C bus. diff --git a/Software/docs/manual/ch-intro.tex b/Software/docs/manual/ch-intro.tex index 338076c..3274d99 100644 --- a/Software/docs/manual/ch-intro.tex +++ b/Software/docs/manual/ch-intro.tex @@ -4,7 +4,7 @@ DigiRadio is an open-source, high-fidelity digital radio receiver. It receives DAB+ and FM broadcasts, processes the audio through a dedicated signal processor, and streams the result over Bluetooth using a -high-resolution codec. Firmware~0.8.4 on \texttt{main} provides encrypted +high-resolution codec. Firmware~0.8.5 on \texttt{main} provides encrypted storage, a tabbed configuration web UI, and the full REST API documented in Chapter~\ref{ch:api}. The whole project --- hardware and firmware --- is released as open source for the maker and audio community to study, diff --git a/Software/docs/manual/manual.tex b/Software/docs/manual/manual.tex index 43f41b9..da0af5c 100644 --- a/Software/docs/manual/manual.tex +++ b/Software/docs/manual/manual.tex @@ -30,7 +30,7 @@ \vfill {\color{drInk}\large Michele Bigi\par} \vspace{2mm} -{\color{drGray}Firmware 0.8.4 \quad\textbullet\quad 2026\par} +{\color{drGray}Firmware 0.8.5 \quad\textbullet\quad 2026\par} \vspace{2mm} {\color{drGray}Hardware: CERN-OHL-S v2 \quad\textbullet\quad Firmware: Apache-2.0\par} \vspace{2mm} diff --git a/Software/instructions.md b/Software/instructions.md index 1579287..a3f0af2 100644 --- a/Software/instructions.md +++ b/Software/instructions.md @@ -4,7 +4,7 @@ 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.8.4** — agent tasks T1–T12 complete; device HIL +**Firmware on `main`:** **0.8.5** — agent tasks T1–T12 complete; device HIL pending PCB arrival. ## What DigiRadio is @@ -66,7 +66,8 @@ unless the user requests them. - ESP-IDF `esp32s3`, C++23, `components/core` host-testable. - SoftAP `DigiRadio-` (or setup fallback), gzipped page, `GET /api/health`. -- Health JSON includes `fw` (today **0.8.4**), `serialNumber`, companion-chip flags. +- 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)