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
+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}