% ============================================================ % DigiRadio — Manual chapter: FSC-BT1035 Bluetooth % ============================================================ \chapter{FSC-BT1035 Bluetooth Transmitter} \label{ch:bt1035} 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 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) is in Chapter~\ref{ch:hardware}, Section~\ref{sec:hw-bt1035}. The ADAU1701 master clock and limiter settings that feed the module are in Chapter~\ref{ch:adau1701} and Chapter~\ref{ch:sigmastudio}. \end{drref} \section{Role in the audio chain} \label{sec:bt1035-role} The BT1035 is an I\textsuperscript{2}S \textbf{slave} at 48\,kHz: LRCLK and BCLK come from the ADAU1701; PCM arrives on \texttt{SDATA\_OUT0} (ADAU MP6 $\rightarrow$ module pin~5). The ESP32 does not process audio 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 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} \subsection{UART parameters} \begin{table}[htbp] \centering \small \begin{tabular}{@{}L{4.2cm}L{9.2cm}@{}} \drhead Setting & Value \\ \midrule Port & UART2 (not the console UART) \\ Baud rate & 115200 \\ Data format & 8N1 \\ Flow control & Hardware RTS/CTS (required) \\ Reset & GPIO active-low pulse at boot \\ SYS\_CTL & Held active to enable the module \\ \bottomrule \end{tabular} \caption{FSC-BT1035 UART and control lines (\texttt{board\_pins.hpp}).} \label{tab:bt1035-uart} \end{table} \subsection{Response handling} Every command expects a module reply containing \texttt{OK} or \texttt{ERROR}. The driver: \begin{itemize} \item builds lines in the pure core via \texttt{core::buildBt1035AtLine()}; \item classifies replies with \texttt{core::parseBt1035AtResponse()}; \item treats timeouts and unexpected payloads as errors (never ignored). \end{itemize} Host tests in \drpath{components/core/test/bt1035_at_test.cpp} lock the init sequence (\texttt{AT+AUXCFG=3}, \texttt{AT+I2SCFG=67}) and the parser. \section{Mandatory I\textsuperscript{2}S slave mode} \label{sec:bt1035-i2s} \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 use: \begin{itemize} \item \texttt{AT+AUXCFG=3} --- I\textsuperscript{2}S mode (§5.1.25) \item \texttt{AT+I2SCFG=67} --- I\textsuperscript{2}S slave, 48\,kHz, 32-bit (§5.1.4) \end{itemize} \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} \label{sec:bt1035-boot} At power-up \texttt{HardwareBootstrap::boot()} runs the Si4684 and ADAU1701 first, applies the saved audio profile, then initialises the BT1035 so the I\textsuperscript{2}S path is ready before Wi-Fi starts. \begin{figure}[htbp] \centering \begin{tikzpicture}[ drstep/.style={draw, rounded corners, minimum width=9.5cm, minimum height=0.9cm, align=center, font=\small}, >={Latex}, node distance=3mm] \node[drstep, fill=black!6] (sys) {SYS\_CTL high, RESET\# pulse}; \node[drstep, fill=black!6, below=of sys] (uart) {Install UART2 @ 115200, RTS/CTS}; \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=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/i2s, i2s/done} { \draw[->] (\a) -- (\b); } \end{tikzpicture} \caption{FSC-BT1035 AT init on DigiRadio.} \label{fig:bt1035-boot} \end{figure} Pairing, codec selection, and volume over Bluetooth are handled by the module's own firmware and NVS; DigiRadio firmware currently implements only the I\textsuperscript{2}S bring-up required for the wired audio path. \section{Software architecture} \label{sec:bt1035-stack} \begin{table}[htbp] \centering \small \begin{tabular}{@{}L{2.5cm}L{3.8cm}L{6.2cm}@{}} \drhead Layer & Type & Responsibility \\ \midrule Bootstrap & \texttt{HardwareBootstrap} & Constructs driver, calls \texttt{boot()} after ADAU1701 \\ Driver & \texttt{bt1035::Bt1035Driver} & UART, reset, AT init sequence \\ Pure core & \texttt{core::Bt1035AtCommand}, parser & Host-testable command strings and OK/ERROR classification \\ \bottomrule \end{tabular} \caption{BT1035 control stack (Slice~6--7). Pairing and A2DP status are exposed on \texttt{/api/bluetooth/*} via \texttt{BluetoothService}.} \label{tab:bt1035-stack} \end{table} \section{Supported AT command subset} \label{sec:bt1035-at} The firmware enumerates every command it sends. Wire formats follow \drpath{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 \small \begin{tabular}{@{}L{2.4cm}L{3.4cm}L{5.5cm}@{}} \drhead Enum & Line sent & Programming guide \\ \midrule \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 + 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} \texttt{Bt1035Driver} owns UART and GPIO reset. All methods return \texttt{std::expected}. \begin{table}[htbp] \centering \small \begin{tabular}{@{}L{4.2cm}L{9.2cm}@{}} \toprule \textbf{Method} & \textbf{Purpose} \\ \midrule \texttt{boot()} & Reset, UART init, run \texttt{bootInitSequence()} \\ \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} \\ \texttt{queryA2dpState()} & \texttt{AT+A2DPSTAT}, parse \texttt{+A2DPSTAT=} \\ \texttt{disconnectA2dp()} & \texttt{AT+A2DPDISC} \\ \texttt{queryDeviceName()} & \texttt{AT+NAME}, parse \texttt{+NAME=} \\ \texttt{queryAutoReconnect()} & \texttt{AT+AUTOCONN}, parse \texttt{+AUTOCONN=} \\ \texttt{setAutoReconnect(times)} & \texttt{AT+AUTOCONN=n} (0--15) \\ \texttt{queryPairedList()} & \texttt{AT+PLIST}, parse \texttt{+PLIST=} lines \\ \bottomrule \end{tabular} \caption{Public driver API.} \label{tab:bt1035-api} \end{table} \subsection{Error codes} \begin{table}[htbp] \centering \small \begin{tabular}{@{}L{4.2cm}L{9.2cm}@{}} \drhead \texttt{Bt1035Error} & Typical cause \\ \midrule \texttt{ResetFailed} & GPIO configuration failure \\ \texttt{UartInitFailed} & \texttt{uart\_driver\_install} / pins \\ \texttt{NotBooted} & \texttt{sendCommand} before \texttt{boot()} \\ \texttt{AtTimeout} & No OK/ERROR within 2\,s \\ \texttt{AtError} & Module returned ERROR \\ \texttt{UnexpectedResponse} & Unrecognised payload (future use) \\ \bottomrule \end{tabular} \caption{Driver error enumeration.} \label{tab:bt1035-errors} \end{table} \section{Integration at power-up} \label{sec:bt1035-integration} \texttt{main/hardware\_bootstrap.cpp} constructs a static \texttt{Bt1035Driver} and calls \texttt{boot()} after \texttt{AudioService::loadAndApply()}. Failure returns \texttt{HardwareBootError::Bt1035BootFailed} and \texttt{app\_main} halts before network bring-up (fail-closed, same as Si4684/ADAU1701). Boot order: \begin{enumerate} \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()} --- 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 I\textsuperscript{2}S config after a module reset: \begin{verbatim} bt1035::Bt1035Driver& bt = ...; if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sMode); !r) { // handle Bt1035Error } if (auto r = bt.sendCommand(core::Bt1035AtCommand::I2sSlave48k32); !r) { // handle Bt1035Error } \end{verbatim} \section{Further reading} \label{sec:bt1035-reading} \begin{itemize} \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 and parser host tests. \end{itemize}