Release fw 0.8.5: BT1035 I2S boot init and hardware doc alignment.

Switch BT1035 bring-up from Line-In to I2S slave (AT+AUXCFG=3, AT+I2SCFG=67) to match the ADAU1701 PCM routing, confirm 2 kΩ I2C pull-ups on R1/R16, and sync firmware docs, AGENTS rules, and the DATASHEET bundle.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 14:41:31 +02:00
co-authored by Cursor
parent 3e46b06d4b
commit 0a1188ad66
22 changed files with 244 additions and 125 deletions
+22
View File
@@ -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`.
Binary file not shown.
+13 -8
View File
@@ -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.
---
+3 -3
View File
@@ -29,6 +29,6 @@ Full spec: @AGENTS.md §7.17.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.
+5 -3
View File
@@ -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.
+4 -4
View File
@@ -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 T1T12 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 |
|--------|------|---------|
@@ -37,7 +37,8 @@ namespace core {
*/
enum class Bt1035AtCommand {
Ping, ///< AT — link check.
AuxLineIn, ///< AT+AUXCFG=1wired Line-In from ADAU1701 (mandatory).
I2sMode, ///< AT+AUXCFG=3I2S 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.
*
@@ -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
+15 -3
View File
@@ -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<Bt1035AtCommand, kBt1035BootInitCommandCount> bootInitSequence() noexcept
{
return std::array<Bt1035AtCommand, kBt1035BootInitCommandCount>{
Bt1035AtCommand::Ping,
Bt1035AtCommand::AuxLineIn,
Bt1035AtCommand::I2sMode,
Bt1035AtCommand::I2sSlave48k32,
};
}
@@ -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;
}
@@ -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<void, Bt1035Error> 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
@@ -169,10 +169,14 @@ std::expected<void, Bt1035Error> 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<void, Bt1035Error> 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 {};
}
@@ -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(
+11 -6
View File
@@ -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 (T1T12, fw 0.7.10.8.4)
## Completed agent tasks (T1T12, fw 0.7.10.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).
---
+2 -2
View File
@@ -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}
+59 -40
View File
@@ -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=<identity>,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}
+3 -2
View File
@@ -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}
+6 -4
View File
@@ -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}
+40 -29
View File
@@ -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=<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.
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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}
+3 -2
View File
@@ -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 T1T12 complete; device HIL
**Firmware on `main`:** **0.8.5** — agent tasks T1T12 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-<suffix>` (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)