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:
@@ -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.
@@ -7,7 +7,7 @@
|
|||||||
**Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3**
|
**Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3**
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
@@ -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
|
- **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
|
- 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 |
|
| 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 |
|
| **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 |
|
| **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 |
|
| **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 |
|
| 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.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.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 |
|
| **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 |
|
| **PCB layout** | 6-layer, DRC clean, plane continuity verified |
|
||||||
| **BOM** | Finalised (manufacturable / sourced) |
|
| **BOM** | Finalised (manufacturable / sourced) |
|
||||||
| **Prototype** | In fabrication (PCBWay) |
|
| **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 |
|
| **Web UI** | Tabbed SPA covering every REST endpoint |
|
||||||
| **HIL** | Device validation pending PCB (`Software/docs/security-flash-nvs.md`) |
|
| **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
|
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
|
with turnkey assembly. The FSC-BT1035 Bluetooth module is sourced from Feasycom
|
||||||
(the footprint uses a BT806-compatible, pin-identical land pattern). See the
|
(the footprint uses a BT806-compatible, pin-identical land pattern). Component
|
||||||
[Technical Manual](Software/docs/manual/manual.tex) hardware chapter and manufacturing
|
datasheets for design review live in
|
||||||
notes for fabrication settings and MSL-3 handling of the BT module.
|
[`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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ Full spec: @AGENTS.md §7.1–7.3.
|
|||||||
|
|
||||||
## FSC-BT1035 (QCC3056, AT over UART)
|
## FSC-BT1035 (QCC3056, AT over UART)
|
||||||
- Typed command builder; explicit OK/ERROR/timeout parsing.
|
- Typed command builder; explicit OK/ERROR/timeout parsing.
|
||||||
- AT+AUXCFG=1 (Line-In) is mandatory in the init sequence and covered by
|
- AT+AUXCFG=3 and AT+I2SCFG=67 (I2S slave 48 kHz) are mandatory in the
|
||||||
a test on the command string. Unknown responses are an error, not
|
init sequence and covered by host tests. Unknown responses are an error,
|
||||||
ignored.
|
not ignored.
|
||||||
|
|||||||
+5
-3
@@ -397,9 +397,11 @@ Layered, dependencies point inward only:
|
|||||||
|
|
||||||
- Controlled by AT commands over UART. Build commands with a typed
|
- Controlled by AT commands over UART. Build commands with a typed
|
||||||
builder; parse responses with explicit `OK`/`ERROR`/timeout handling.
|
builder; parse responses with explicit `OK`/`ERROR`/timeout handling.
|
||||||
- **Line-In mode is mandatory:** the `AT+AUXCFG=1` step must be part of
|
- **I\textsuperscript{2}S slave mode is mandatory:** `AT+AUXCFG=3` and
|
||||||
the documented init sequence and covered by a test on the command
|
`AT+I2SCFG=67` (Feasycom programming guide §5.1.25 / §5.1.4) must be part
|
||||||
string. Losing it silently breaks the audio path.
|
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
|
- The AT subset in use is enumerated and documented; unknown responses
|
||||||
are an error value, not ignored.
|
are an error value, not ignored.
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3.
|
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,
|
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
|
**15** host tests, **4** CI jobs. Agent tasks T1–T12 complete; device HIL pending
|
||||||
first PCB.
|
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 |
|
| Tuner | FM/DAB tune, seek up/down, RSQ, RDS, DAB services + DLS |
|
||||||
| Audio | 6-band EQ, mixer, stereo/bass enhance, NVS profile |
|
| Audio | 6-band EQ, mixer, stereo/bass enhance, NVS profile |
|
||||||
| Updates | ESP32 dual-OTA, ADAU1701 `dsp` partition blob replay |
|
| 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:
|
`components/net/www/index.html` · regenerate embed:
|
||||||
`tools/gzip-www.sh`.
|
`tools/gzip-www.sh`.
|
||||||
|
|
||||||
## HTTP API (fw 0.8.4)
|
## HTTP API (fw 0.8.5)
|
||||||
|
|
||||||
| Method | Path | Purpose |
|
| Method | Path | Purpose |
|
||||||
|--------|------|---------|
|
|--------|------|---------|
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ namespace core {
|
|||||||
*/
|
*/
|
||||||
enum class Bt1035AtCommand {
|
enum class Bt1035AtCommand {
|
||||||
Ping, ///< AT — link check.
|
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.
|
PairDiscoverable, ///< AT+PAIR=1 — enter BR/EDR/BLE discoverable mode.
|
||||||
PairHidden, ///< AT+PAIR=0 — leave discoverable mode.
|
PairHidden, ///< AT+PAIR=0 — leave discoverable mode.
|
||||||
A2dpStat, ///< AT+A2DPSTAT — read A2DP link state.
|
A2dpStat, ///< AT+A2DPSTAT — read A2DP link state.
|
||||||
@@ -83,7 +84,10 @@ enum class Bt1035AtResponseKind {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** Number of commands in bootInitSequence(). */
|
/** 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.
|
* @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.
|
* @brief bootInitSequence — mandatory bring-up commands in order.
|
||||||
*
|
*
|
||||||
* @dname bootInitSequence
|
* @dname bootInitSequence
|
||||||
* @return Ping then AuxLineIn (AT+AUXCFG=1).
|
* @return Ping, I2sMode (AUXCFG=3), I2sSlave48k32 (I2SCFG=67).
|
||||||
* @pubstate none
|
* @pubstate none
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
@@ -165,6 +169,21 @@ parseBt1035A2dpStatResponse(std::string_view response);
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] std::string buildBt1035SetAutoConnLine(std::uint8_t times);
|
[[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.
|
* @brief parseBt1035NameResponse — extract +NAME= value.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace core {
|
|||||||
struct CompanionChipStatus {
|
struct CompanionChipStatus {
|
||||||
bool si4684Ready; ///< Si4684 HOST_LOAD completed.
|
bool si4684Ready; ///< Si4684 HOST_LOAD completed.
|
||||||
bool adau1701Ready; ///< ADAU1701 SigmaStudio download 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
|
} // namespace core
|
||||||
|
|||||||
@@ -101,8 +101,10 @@ std::string buildBt1035AtLine(Bt1035AtCommand command)
|
|||||||
switch (command) {
|
switch (command) {
|
||||||
case Bt1035AtCommand::Ping:
|
case Bt1035AtCommand::Ping:
|
||||||
return "AT\r\n";
|
return "AT\r\n";
|
||||||
case Bt1035AtCommand::AuxLineIn:
|
case Bt1035AtCommand::I2sMode:
|
||||||
return "AT+AUXCFG=1\r\n";
|
return "AT+AUXCFG=3\r\n";
|
||||||
|
case Bt1035AtCommand::I2sSlave48k32:
|
||||||
|
return "AT+I2SCFG=67\r\n";
|
||||||
case Bt1035AtCommand::PairDiscoverable:
|
case Bt1035AtCommand::PairDiscoverable:
|
||||||
return "AT+PAIR=1\r\n";
|
return "AT+PAIR=1\r\n";
|
||||||
case Bt1035AtCommand::PairHidden:
|
case Bt1035AtCommand::PairHidden:
|
||||||
@@ -129,11 +131,21 @@ std::string buildBt1035SetAutoConnLine(std::uint8_t times)
|
|||||||
return "AT+AUTOCONN=" + std::to_string(times) + "\r\n";
|
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
|
std::array<Bt1035AtCommand, kBt1035BootInitCommandCount> bootInitSequence() noexcept
|
||||||
{
|
{
|
||||||
return std::array<Bt1035AtCommand, kBt1035BootInitCommandCount>{
|
return std::array<Bt1035AtCommand, kBt1035BootInitCommandCount>{
|
||||||
Bt1035AtCommand::Ping,
|
Bt1035AtCommand::Ping,
|
||||||
Bt1035AtCommand::AuxLineIn,
|
Bt1035AtCommand::I2sMode,
|
||||||
|
Bt1035AtCommand::I2sSlave48k32,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,24 @@ namespace {
|
|||||||
std::cerr << "init sequence size mismatch\n";
|
std::cerr << "init sequence size mismatch\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (sequence[1U] != core::Bt1035AtCommand::AuxLineIn) {
|
if (sequence[1U] != core::Bt1035AtCommand::I2sMode) {
|
||||||
std::cerr << "AUXCFG=1 must be in init sequence\n";
|
std::cerr << "I2S mode must be in init sequence\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
const std::string aux = core::buildBt1035AtLine(core::Bt1035AtCommand::AuxLineIn);
|
if (sequence[2U] != core::Bt1035AtCommand::I2sSlave48k32) {
|
||||||
if (aux != "AT+AUXCFG=1\r\n") {
|
std::cerr << "I2SCFG must be in init sequence\n";
|
||||||
std::cerr << "AUXCFG command line mismatch\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_FAILURE;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -117,6 +128,11 @@ namespace {
|
|||||||
std::cerr << "AUTOCONN command line mismatch\n";
|
std::cerr << "AUTOCONN command line mismatch\n";
|
||||||
return EXIT_FAILURE;
|
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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ struct Bt1035Pins {
|
|||||||
* @dname Bt1035Driver
|
* @dname Bt1035Driver
|
||||||
* @return n/a (type)
|
* @return n/a (type)
|
||||||
* @pubstate Owns UART port after boot(). booted_ true after init sequence
|
* @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
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-07-06
|
||||||
@@ -86,7 +86,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @dname boot
|
* @dname boot
|
||||||
* @return Ok on success, or Bt1035Error.
|
* @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
|
* Sequence: hardware reset, UART @ 115200 with RTS/CTS, then
|
||||||
* core::bootInitSequence() (see manual chapter bt1035).
|
* core::bootInitSequence() (see manual chapter bt1035).
|
||||||
@@ -97,7 +97,7 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, Bt1035Error> boot();
|
[[nodiscard]] std::expected<void, Bt1035Error> boot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief isBooted — query whether Line-In init succeeded.
|
* @brief isBooted — query whether I2S init succeeded.
|
||||||
*
|
*
|
||||||
* @dname isBooted
|
* @dname isBooted
|
||||||
* @return true after successful boot().
|
* @return true after successful boot().
|
||||||
@@ -177,7 +177,7 @@ public:
|
|||||||
* @dname setDeviceName
|
* @dname setDeviceName
|
||||||
* @param name Bluetooth name (Feasycom FSC-BT1035 AT+NAME command).
|
* @param name Bluetooth name (Feasycom FSC-BT1035 AT+NAME command).
|
||||||
* @return Ok on success, or Bt1035Error.
|
* @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
|
* @author Michele Bigi
|
||||||
* @date 2026-07-07
|
* @date 2026-07-07
|
||||||
|
|||||||
@@ -169,10 +169,14 @@ std::expected<void, Bt1035Error> Bt1035Driver::setDeviceName(
|
|||||||
if (auto ready = ensureBooted(); !ready) {
|
if (auto ready = ensureBooted(); !ready) {
|
||||||
return 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);
|
return std::unexpected(Bt1035Error::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
std::string line = std::string("AT+NAME=") + std::string(name) + "\r\n";
|
|
||||||
return transmitAndExpectOk(line);
|
return transmitAndExpectOk(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +319,7 @@ std::expected<void, Bt1035Error> Bt1035Driver::boot()
|
|||||||
}
|
}
|
||||||
|
|
||||||
booted_ = true;
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace net {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr char kTag[] = "SetupWebServer";
|
constexpr char kTag[] = "SetupWebServer";
|
||||||
constexpr char kFirmwareVersion[] = "0.8.4";
|
constexpr char kFirmwareVersion[] = "0.8.5";
|
||||||
constexpr unsigned kRebootDelaySec = 3;
|
constexpr unsigned kRebootDelaySec = 3;
|
||||||
|
|
||||||
extern const uint8_t www_index_html_gz_start[] asm(
|
extern const uint8_t www_index_html_gz_start[] asm(
|
||||||
|
|||||||
+11
-6
@@ -3,7 +3,7 @@
|
|||||||
Agent task list and hardware-in-the-loop backlog. Working directory for all
|
Agent task list and hardware-in-the-loop backlog. Working directory for all
|
||||||
commands is `Software/`.
|
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
|
NVS + flash encryption (dev mode), tabbed Web UI with System uploads, CI gate
|
||||||
(4 jobs).
|
(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 |
|
| 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 |
|
| **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 |
|
| **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
|
Also landed (not numbered): BT1035 pairing (`BluetoothService`), station presets
|
||||||
(fw 0.7.0), companion-chip boot (Slice 3), ADAU1701 runtime (Slice 5).
|
(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)
|
## 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
|
- 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
|
- 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).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ implemented in \texttt{SetupWebServer}. Request bodies are parsed into
|
|||||||
domain types in the pure core (\texttt{components/core}) before any
|
domain types in the pure core (\texttt{components/core}) before any
|
||||||
persistence or driver call. Exact C++ signatures live in the generated
|
persistence or driver call. Exact C++ signatures live in the generated
|
||||||
Doxygen output under \texttt{docs/api/}; this chapter documents the
|
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}
|
\section{Transport and reachability}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Returns a health-check DTO serialised by
|
|||||||
|
|
||||||
\begin{drnote}[Response schema]
|
\begin{drnote}[Response schema]
|
||||||
\begin{drcode}[JSON]
|
\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}}
|
"chips":{"si4684":true,"adau1701":true,"bt1035":true}}
|
||||||
\end{drcode}
|
\end{drcode}
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
|
|||||||
@@ -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
|
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
|
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
|
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}
|
RTS/CTS), why I\textsuperscript{2}S slave mode is mandatory, and how
|
||||||
implements the bring-up sequence.
|
\texttt{bt1035::Bt1035Driver} implements the bring-up sequence.
|
||||||
|
|
||||||
\begin{drref}[Hardware context]
|
\begin{drref}[Hardware context]
|
||||||
Board wiring (UART pins, I\textsuperscript{2}S to the module, flow control)
|
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.
|
accepted and encoded for transmission.
|
||||||
|
|
||||||
Without firmware init the module may stay in a default mode that ignores the
|
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
|
I\textsuperscript{2}S bus from the ADAU1701. The mandatory
|
||||||
auxiliary/Line-In input --- omitting it silently breaks the entire wireless
|
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67} commands select I\textsuperscript{2}S
|
||||||
output (Section~\ref{sec:bt1035-linein}).
|
slave input at 48\,kHz --- omitting them silently breaks the entire wireless
|
||||||
|
output (Section~\ref{sec:bt1035-i2s}).
|
||||||
|
|
||||||
\section{Control interface}
|
\section{Control interface}
|
||||||
\label{sec:bt1035-uart}
|
\label{sec:bt1035-uart}
|
||||||
@@ -67,17 +68,19 @@ Every command expects a module reply containing \texttt{OK} or
|
|||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Host tests in \texttt{components/core/test/bt1035\_at\_test.cpp} lock the
|
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}
|
\section{Mandatory I\textsuperscript{2}S slave mode}
|
||||||
\label{sec:bt1035-linein}
|
\label{sec:bt1035-i2s}
|
||||||
|
|
||||||
\begin{drcaution}[AT+AUXCFG=1 is not optional]
|
\begin{drcaution}[I\textsuperscript{2}S init is not optional]
|
||||||
The documented init sequence must include \texttt{AT+AUXCFG=1} after a
|
The board routes ADAU1701 \texttt{SDATA\_OUT0} (MP6) to the module PCM input
|
||||||
successful \texttt{AT} ping. This tells the QCC3056 firmware to take audio
|
with shared BCLK/LRCLK (see Chapter~\ref{ch:hardware}). The init sequence
|
||||||
from the wired I\textsuperscript{2}S/Line-In port (the ADAU1701 output)
|
must therefore use \texttt{AT+AUXCFG=3} (I\textsuperscript{2}S mode per
|
||||||
rather than an internal source. AGENTS.md and the hardware manual both treat
|
programming guide §5.1.25) followed by \texttt{AT+I2SCFG=67} (I\textsuperscript{2}S
|
||||||
skipping this step as a production bug.
|
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}
|
\end{drcaution}
|
||||||
|
|
||||||
\section{Boot sequence}
|
\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
|
At power-up \texttt{HardwareBootstrap::boot()} runs the Si4684 and ADAU1701
|
||||||
first, applies the saved audio profile, then initialises the BT1035 so the
|
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]
|
\begin{figure}[htbp]
|
||||||
\centering
|
\centering
|
||||||
@@ -99,10 +102,12 @@ Line-In path is ready before Wi-Fi starts.
|
|||||||
\node[drstep, fill=black!8, below=of uart] (at)
|
\node[drstep, fill=black!8, below=of uart] (at)
|
||||||
{Send \texttt{AT} --- expect OK};
|
{Send \texttt{AT} --- expect OK};
|
||||||
\node[drstep, fill=black!10, below=of at] (aux)
|
\node[drstep, fill=black!10, below=of at] (aux)
|
||||||
{Send \texttt{AT+AUXCFG=1} --- expect OK (Line-In)};
|
{Send \texttt{AT+AUXCFG=3} --- expect OK (I\textsuperscript{2}S)};
|
||||||
\node[drstep, fill=black!6, below=of aux] (done)
|
\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};
|
{\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);
|
\draw[->] (\a) -- (\b);
|
||||||
}
|
}
|
||||||
\end{tikzpicture}
|
\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
|
Pairing, codec selection, and volume over Bluetooth are handled by the
|
||||||
module's own firmware and NVS; DigiRadio firmware currently implements
|
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}
|
\section{Software architecture}
|
||||||
\label{sec:bt1035-stack}
|
\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}
|
\section{Supported AT command subset}
|
||||||
\label{sec:bt1035-at}
|
\label{sec:bt1035-at}
|
||||||
|
|
||||||
The firmware enumerates every command it sends. Extending the subset requires
|
The firmware enumerates every command it sends. Wire formats follow
|
||||||
updating \texttt{core::Bt1035AtCommand}, the manual, and a host test.
|
\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]
|
\begin{table}[htbp]
|
||||||
\centering
|
\centering
|
||||||
\begin{tabular}{@{}lll@{}}
|
\small
|
||||||
\drhead Enum & Line sent & Purpose \\
|
\begin{tabular}{@{}llp{5.2cm}@{}}
|
||||||
|
\drhead Enum & Line sent & Programming guide \\
|
||||||
\midrule
|
\midrule
|
||||||
\texttt{Ping} & \texttt{AT} & Verify UART link \\
|
\texttt{Ping} & \texttt{AT} & link check \\
|
||||||
\texttt{AuxLineIn} & \texttt{AT+AUXCFG=1} & Enable Line-In from ADAU \\
|
\texttt{I2sMode} & \texttt{AT+AUXCFG=3} & §5.1.25 Param=3 I2S \\
|
||||||
\texttt{PairDiscoverable} & \texttt{AT+PAIR=1} & Enter discoverable mode \\
|
\texttt{I2sSlave48k32} & \texttt{AT+I2SCFG=67} & §5.1.4 slave 48\,kHz 32-bit \\
|
||||||
\texttt{PairHidden} & \texttt{AT+PAIR=0} & Leave discoverable mode \\
|
\texttt{PairDiscoverable} & \texttt{AT+PAIR=1} & §5.1.20 enter discoverable \\
|
||||||
\texttt{A2dpStat} & \texttt{AT+A2DPSTAT} & Read link state \\
|
\texttt{PairHidden} & \texttt{AT+PAIR=0} & §5.1.20 leave discoverable \\
|
||||||
\texttt{A2dpDisconnect} & \texttt{AT+A2DPDISC} & Release A2DP session \\
|
\texttt{A2dpStat} & \texttt{AT+A2DPSTAT} & §5.3.1; states 1--5 \\
|
||||||
\texttt{QueryName} & \texttt{AT+NAME} & Read module friendly name \\
|
\texttt{A2dpDisconnect} & \texttt{AT+A2DPDISC} & §5.3.3 \\
|
||||||
\texttt{QueryAutoConn} & \texttt{AT+AUTOCONN} & Read auto-reconnect count \\
|
\texttt{QueryName} & \texttt{AT+NAME} & §5.1.16 read \texttt{+NAME=} \\
|
||||||
\texttt{QueryPairedList} & \texttt{AT+PLIST} & List paired remotes \\
|
\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
|
\bottomrule
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\caption{Enumerated AT commands (\texttt{core::Bt1035AtCommand}). Boot
|
\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}
|
\label{tab:bt1035-at}
|
||||||
\end{table}
|
\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}
|
\section{Bt1035Driver API}
|
||||||
\label{sec:bt1035-driver}
|
\label{sec:bt1035-driver}
|
||||||
|
|
||||||
@@ -176,7 +189,7 @@ updating \texttt{core::Bt1035AtCommand}, the manual, and a host test.
|
|||||||
\textbf{Method} & \textbf{Purpose} \\
|
\textbf{Method} & \textbf{Purpose} \\
|
||||||
\midrule
|
\midrule
|
||||||
\texttt{boot()} & Reset, UART init, run \texttt{bootInitSequence()} \\
|
\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{sendCommand(cmd)} & Send one typed command, expect OK \\
|
||||||
\texttt{enterPairingMode()} & \texttt{AT+PAIR=1} \\
|
\texttt{enterPairingMode()} & \texttt{AT+PAIR=1} \\
|
||||||
\texttt{leavePairingMode()} & \texttt{AT+PAIR=0} \\
|
\texttt{leavePairingMode()} & \texttt{AT+PAIR=0} \\
|
||||||
@@ -226,19 +239,22 @@ Boot order:
|
|||||||
\item Si4684 \texttt{boot(Dab)} --- tuner image in RAM.
|
\item Si4684 \texttt{boot(Dab)} --- tuner image in RAM.
|
||||||
\item ADAU1701 \texttt{boot()} --- SigmaStudio program in RAM.
|
\item ADAU1701 \texttt{boot()} --- SigmaStudio program in RAM.
|
||||||
\item \texttt{AudioService::loadAndApply()} --- user mixer/EQ profile.
|
\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}
|
\end{enumerate}
|
||||||
|
|
||||||
\section{Typical usage (firmware developer)}
|
\section{Typical usage (firmware developer)}
|
||||||
\label{sec:bt1035-usage}
|
\label{sec:bt1035-usage}
|
||||||
|
|
||||||
After a successful \texttt{HardwareBootstrap::boot()}, the module is ready;
|
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:
|
after a module reset:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
bt1035::Bt1035Driver& bt = ...;
|
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
|
// handle Bt1035Error
|
||||||
}
|
}
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
@@ -247,9 +263,12 @@ if (auto r = bt.sendCommand(core::Bt1035AtCommand::AuxLineIn); !r) {
|
|||||||
\label{sec:bt1035-reading}
|
\label{sec:bt1035-reading}
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item Feasycom FSC-BT1035 AT command manual (vendor) --- full command set;
|
\item \texttt{Hardware/DATASHEET/FSC-BT1035\_programming\_user\_guide\_1.1.1.pdf}
|
||||||
firmware wraps name, paired list, and auto-reconnect for the Web UI.
|
--- 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:hardware} --- pin map and I\textsuperscript{2}S routing.
|
||||||
\item Chapter~\ref{ch:adau1701} --- DSP output that feeds the module.
|
\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}
|
\end{itemize}
|
||||||
|
|||||||
@@ -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.
|
this chapter in step with the code, so it is always current.
|
||||||
\end{drnote}
|
\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.
|
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}
|
\section{Bt1035Driver}\label{cls:Bt1035Driver}
|
||||||
UART driver for the FSC-BT1035 (Chapter~\ref{ch:bt1035}). \texttt{boot()}
|
UART driver for the FSC-BT1035 (Chapter~\ref{ch:bt1035}). \texttt{boot()}
|
||||||
pulses RESET\#, opens UART2 with RTS/CTS, and runs
|
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.
|
\texttt{Bt1035Error} on timeout, ERROR response, or UART failure.
|
||||||
|
|
||||||
\section{Adau1701Driver}\label{cls:Adau1701Driver}
|
\section{Adau1701Driver}\label{cls:Adau1701Driver}
|
||||||
|
|||||||
@@ -150,9 +150,10 @@ safeload registers.
|
|||||||
|
|
||||||
\paragraph{FSC-BT1035 (Bluetooth).}
|
\paragraph{FSC-BT1035 (Bluetooth).}
|
||||||
The module is controlled by AT commands over UART with hardware flow control.
|
The module is controlled by AT commands over UART with hardware flow control.
|
||||||
The initialisation sequence includes enabling Line-In mode
|
The initialisation sequence includes enabling I\textsuperscript{2}S slave mode
|
||||||
(\texttt{AT+AUXCFG=1}), which is required for the wired audio path from the
|
(\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}), which is required for the
|
||||||
DSP; command responses are parsed explicitly, with timeouts treated as errors.
|
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}.
|
Full driver API, boot flow, and error codes are in Chapter~\ref{ch:bt1035}.
|
||||||
|
|
||||||
\section{Configuration, storage, and user interface}
|
\section{Configuration, storage, and user interface}
|
||||||
@@ -216,7 +217,8 @@ bring-up:
|
|||||||
restores the saved \texttt{core::AudioProfile} from NVS (or factory
|
restores the saved \texttt{core::AudioProfile} from NVS (or factory
|
||||||
defaults) via ADAU1701 safeload before network bring-up.
|
defaults) via ADAU1701 safeload before network bring-up.
|
||||||
\item \textbf{FSC-BT1035} (UART): \texttt{bt1035::Bt1035Driver::boot()}
|
\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}).
|
(Chapter~\ref{ch:bt1035}).
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
responses explicitly, treating timeouts as errors. Some settings are held
|
||||||
in the module's own non-volatile memory.
|
in the module's own non-volatile memory.
|
||||||
|
|
||||||
\begin{drcaution}[Line-In mode]
|
\begin{drcaution}[I\textsuperscript{2}S slave mode]
|
||||||
The initialisation sequence must enable Line-In mode (\texttt{AT+AUXCFG=1})
|
The PCB routes ADAU1701 \texttt{SDATA\_OUT0} to the module over
|
||||||
so the module accepts the wired audio coming from the DSP. Omitting it
|
I\textsuperscript{2}S (BCLK, LRCLK, PCM data). Firmware must initialise
|
||||||
silently breaks the audio path. Driver boot flow and AT subset are documented
|
\texttt{AT+AUXCFG=3} and \texttt{AT+I2SCFG=67}, not Line-In
|
||||||
in Chapter~\ref{ch:bt1035}.
|
(\texttt{AT+AUXCFG=1}). See Chapter~\ref{ch:bt1035}, Section~\ref{sec:bt1035-i2s}.
|
||||||
\end{drcaution}
|
\end{drcaution}
|
||||||
|
|
||||||
\section{The host: ESP32-S3}
|
\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 \\
|
INTB & interrupt line, external pull-up (GPIO39) & verified \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\caption{Si4684 design checks. References: Si4684-A10 datasheet
|
\caption{Si4684 design checks. References:
|
||||||
(power sequencing, SPI, supply levels) and AN851 (bypass and layout).}
|
\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}
|
\label{tab:hw-val-si4684}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
\begin{drref}[Datasheet]
|
\begin{drref}[Datasheet]
|
||||||
Power sequencing and SPI framing: Si4684-A10 data sheet. The user must not
|
Power sequencing and SPI framing:
|
||||||
pulse SSB high between bytes; SSB frames the whole command/reply.
|
\texttt{Hardware/DATASHEET/SI4684-A10.pdf}. The user must not pulse SSB high
|
||||||
Bypass-capacitor values and placement: AN851, \emph{Si468x Schematic and
|
between bytes; SSB frames the whole command/reply.
|
||||||
Layout Guide}. Boot and command protocol: AN649.
|
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}
|
\end{drref}
|
||||||
|
|
||||||
The core, memory, and analogue rails run at 1.8\,V; only VIO is at 3.3\,V
|
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 \\
|
Output master & serial output port in Master Mode & verified \\
|
||||||
MCLK ratio & exactly 256\,$\times$\,f\textsubscript{S} & verified \\
|
MCLK ratio & exactly 256\,$\times$\,f\textsubscript{S} & verified \\
|
||||||
MCLK value & 12.288\,MHz oscillator (256\,$\times$\,48\,kHz) & 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 \\
|
I\textsuperscript{2}C address & 0x34 (ADDR0 = ADDR1 = GND) & verified \\
|
||||||
Self-boot & disabled (host RAM load) & verified \\
|
Self-boot & disabled (host RAM load) & verified \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
@@ -430,17 +433,18 @@ locks up when input data arrives.
|
|||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
\begin{drref}[Datasheet]
|
\begin{drref}[Datasheet]
|
||||||
Master-clock loopback and serial-port modes: ADAU1701 data sheet, Rev.~C,
|
Master-clock loopback and serial-port modes:
|
||||||
Table~63 and the serial-port section. The master clock must be exactly
|
\texttt{Hardware/DATASHEET/adau1701.pdf}, Rev.~C, Table~63 and the serial-port
|
||||||
256\,$\times$\,f\textsubscript{S}. I\textsuperscript{2}C lines require
|
section. The master clock must be exactly 256\,$\times$\,f\textsubscript{S}.
|
||||||
2.2\,k\(\Omega\) pull-ups.
|
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}
|
\end{drref}
|
||||||
|
|
||||||
\begin{drcaution}[Open item --- I\textsuperscript{2}C pull-ups]
|
\begin{drnote}[I\textsuperscript{2}C pull-ups confirmed]
|
||||||
The ADAU1701 datasheet requires 2.2\,k\(\Omega\) pull-up resistors on SDA
|
Schematic \texttt{DigitalRadio.pdf} and BOM list R1 (SCL) and R16 (SDA) as
|
||||||
and SCL. Confirm these are present on the board (shared bus with the
|
2\,k\(\Omega\) to 3V3 on the shared ADAU1701/EEPROM bus.
|
||||||
EEPROM); this is the one ADAU check still to close.
|
\end{drnote}
|
||||||
\end{drcaution}
|
|
||||||
|
|
||||||
\subsection{FSC-BT1035 (QCC3056)}
|
\subsection{FSC-BT1035 (QCC3056)}
|
||||||
\label{sec:hw-val-bt1035}
|
\label{sec:hw-val-bt1035}
|
||||||
@@ -452,17 +456,24 @@ EEPROM); this is the one ADAU check still to close.
|
|||||||
\drhead Check & Requirement & Status \\
|
\drhead Check & Requirement & Status \\
|
||||||
\midrule
|
\midrule
|
||||||
UART with flow control & TX/RX + RTS/CTS wired & verified \\
|
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 \\
|
I\textsuperscript{2}S input & receives clocks from ADAU master (slave) & verified \\
|
||||||
Reset line & RESET (GPIO17) driven by ESP32 & verified \\
|
Reset line & RESET (GPIO17) driven by ESP32 & verified \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\caption{FSC-BT1035 design checks. The module is an I\textsuperscript{2}S
|
\caption{FSC-BT1035 design checks. Datasheet:
|
||||||
slave fed by the ADAU master; audio arrives on its I\textsuperscript{2}S
|
\texttt{Hardware/DATASHEET/FSC-BT1035\_Datasheet\_EN.pdf}; AT commands:
|
||||||
input pins.}
|
\texttt{FSC-BT1035\_programming\_user\_guide\_1.1.1.pdf} §5.}
|
||||||
\label{tab:hw-val-bt1035}
|
\label{tab:hw-val-bt1035}
|
||||||
\end{table}
|
\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}
|
\subsection{System-level summary}
|
||||||
\label{sec:hw-val-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 pull-down, 1.8\,V rails, bypass caps, SPI & verified \\
|
||||||
Si4684 & RSTB release only in \texttt{Si4684Driver::boot()} & verified \\
|
Si4684 & RSTB release only in \texttt{Si4684Driver::boot()} & verified \\
|
||||||
ADAU1701 & clock loopback, master mode, 256$\times$f\textsubscript{S} & 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 \\
|
FSC-BT1035 & UART + flow control, I\textsuperscript{2}S slave & verified \\
|
||||||
Clocking & single 48\,kHz domain, ADAU master & verified \\
|
Clocking & single 48\,kHz domain, ADAU master & verified \\
|
||||||
\bottomrule
|
\bottomrule
|
||||||
@@ -483,6 +494,6 @@ EEPROM); this is the one ADAU check still to close.
|
|||||||
\label{tab:hw-val-summary}
|
\label{tab:hw-val-summary}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
|
||||||
The only open hardware check is the presence of the 2.2\,k\(\Omega\)
|
The consolidated pre-fabrication validation is complete for all
|
||||||
I\textsuperscript{2}C pull-ups on the ADAU/EEPROM bus. All damage-class
|
damage-class items (Si4684 power sequencing and supply levels) and the
|
||||||
items (Si4684 power sequencing and supply levels) are verified.
|
shared I\textsuperscript{2}C bus.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
DigiRadio is an open-source, high-fidelity digital radio receiver. It
|
DigiRadio is an open-source, high-fidelity digital radio receiver. It
|
||||||
receives DAB+ and FM broadcasts, processes the audio through a dedicated
|
receives DAB+ and FM broadcasts, processes the audio through a dedicated
|
||||||
signal processor, and streams the result over Bluetooth using a
|
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
|
storage, a tabbed configuration web UI, and the full REST API documented
|
||||||
in Chapter~\ref{ch:api}. The whole project --- hardware and firmware --- is
|
in Chapter~\ref{ch:api}. The whole project --- hardware and firmware --- is
|
||||||
released as open source for the maker and audio community to study,
|
released as open source for the maker and audio community to study,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
\vfill
|
\vfill
|
||||||
{\color{drInk}\large Michele Bigi\par}
|
{\color{drInk}\large Michele Bigi\par}
|
||||||
\vspace{2mm}
|
\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}
|
\vspace{2mm}
|
||||||
{\color{drGray}Hardware: CERN-OHL-S v2 \quad\textbullet\quad Firmware: Apache-2.0\par}
|
{\color{drGray}Hardware: CERN-OHL-S v2 \quad\textbullet\quad Firmware: Apache-2.0\par}
|
||||||
\vspace{2mm}
|
\vspace{2mm}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Read this together with `AGENTS.md` and everything under
|
|||||||
`.cursor/rules/`. Those define *how* to write code; this file defines
|
`.cursor/rules/`. Those define *how* to write code; this file defines
|
||||||
*what we are building* and the current state on `main`.
|
*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.
|
pending PCB arrival.
|
||||||
|
|
||||||
## What DigiRadio is
|
## What DigiRadio is
|
||||||
@@ -66,7 +66,8 @@ unless the user requests them.
|
|||||||
|
|
||||||
- ESP-IDF `esp32s3`, C++23, `components/core` host-testable.
|
- ESP-IDF `esp32s3`, C++23, `components/core` host-testable.
|
||||||
- SoftAP `DigiRadio-<suffix>` (or setup fallback), gzipped page, `GET /api/health`.
|
- 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)
|
## Slice 2 — Secure store + Wi-Fi STA (complete)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user