Release fw 0.8.4: doc sync, System UI, BT/FM polish.
Align README, manual, and backlog to 0.8.4; add Web UI System tab for OTA/DSP uploads, serial in health header, FM seek down, and BT1035 paired list plus auto-reconnect API. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+23
-8
@@ -3,8 +3,9 @@
|
||||
Agent task list and hardware-in-the-loop backlog. Working directory for all
|
||||
commands is `Software/`.
|
||||
|
||||
**Current firmware:** `0.8.3` — NVS + flash encryption (dev mode), tabbed Web
|
||||
UI, integration service, RDS/DLS metadata, CI gate (4 jobs).
|
||||
**Current firmware:** `0.8.4` — dual OTA + DSP blob updates, EEPROM identity,
|
||||
NVS + flash encryption (dev mode), tabbed Web UI with System uploads, CI gate
|
||||
(4 jobs).
|
||||
|
||||
**Before writing code, read `AGENTS.md`, `.cursor/rules/`, and
|
||||
`instructions.md`.** Definition of Done: Apache header, doc blocks,
|
||||
@@ -13,7 +14,7 @@ UI, integration service, RDS/DLS metadata, CI gate (4 jobs).
|
||||
|
||||
---
|
||||
|
||||
## Completed agent tasks (T1–T8, fw 0.7.1–0.8.3)
|
||||
## Completed agent tasks (T1–T12, fw 0.7.1–0.8.4)
|
||||
|
||||
| Task | Version | Summary |
|
||||
|------|---------|---------|
|
||||
@@ -22,9 +23,13 @@ UI, integration service, RDS/DLS metadata, CI gate (4 jobs).
|
||||
| **T3** | 0.7.2 | Preset reorder API/UI, DAB playing ids in status |
|
||||
| **T4** | 0.8.0 | RDS/DLS broadcast metadata |
|
||||
| **T5** | 0.8.1 | `IntegrationService` — startup, preset recall, last-preset NVS |
|
||||
| **T6** | 0.8.2 | Tabbed configuration Web UI (full REST coverage) |
|
||||
| **T6** | 0.8.2 | Tabbed configuration Web UI (REST coverage) |
|
||||
| **T7** | 0.8.2 | Si4684 blob policy — gitignore, docs, `check_si4684_blobs.py` |
|
||||
| **T8** | 0.8.3 | NVS + flash encryption — `initEncryptedStorage`, security docs |
|
||||
| **T9** | 0.8.4 | Dual-OTA partition table + `dsp` blob slot, rollback Kconfig |
|
||||
| **T10** | 0.8.4 | EEPROM EUI-48 identity — SoftAP/BT/hostname/serial |
|
||||
| **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 |
|
||||
|
||||
Also landed (not numbered): BT1035 pairing (`BluetoothService`), station presets
|
||||
(fw 0.7.0), companion-chip boot (Slice 3), ADAU1701 runtime (Slice 5).
|
||||
@@ -44,7 +49,12 @@ reboot, presets and `last_preset` survive power cycle.
|
||||
Si4684 DAB/FM tune, ADAU1701 profile apply, BT1035 A2DP to headphones,
|
||||
now-playing metadata in UI and `/api/tuner/status`.
|
||||
|
||||
### H3. Production flash encryption (optional)
|
||||
### H3. OTA and DSP program update (on hardware)
|
||||
Push a known-good `.bin` via `POST /api/system/ota`, confirm rollback after a
|
||||
deliberately bad image. Upload a DRAD blob via `POST /api/dsp/program` and
|
||||
verify ADAU replay after reboot.
|
||||
|
||||
### H4. Production flash encryption (optional)
|
||||
After H1 passes, trial build with `sdkconfig.defaults.production` overlay on
|
||||
a sacrificial unit; confirm RELEASE mode policy before shipping.
|
||||
|
||||
@@ -52,9 +62,13 @@ a sacrificial unit; confirm RELEASE mode policy before shipping.
|
||||
|
||||
## Open firmware polish (non-blocking)
|
||||
|
||||
- BT1035: device name, paired-device list, auto-reconnect AT (driver stubs open).
|
||||
- Si4684: optional commands (STOP_DIGITAL_SERVICE, ensemble info) if product needs them.
|
||||
- FM seek down (API today is seek-up only).
|
||||
Done in fw 0.8.4 unless noted:
|
||||
|
||||
- FM seek down — `POST /api/tuner/seek` with `{"direction":"down"}`.
|
||||
- BT1035 — query/set name, paired list (`AT+PLIST`), auto-reconnect
|
||||
(`AT+AUTOCONN`) per Feasycom BT1035 manual.
|
||||
- Si4684 — `STOP_DIGITAL_SERVICE` (0x82) before FM band switch when DAB
|
||||
audio is active; ensemble metrics remain via `DAB_DIGRAD_STATUS` in status.
|
||||
|
||||
---
|
||||
|
||||
@@ -76,5 +90,6 @@ After editing the web UI: `tools/gzip-www.sh`.
|
||||
|
||||
- Extend existing patterns (`AudioProfile` / `IAudioProfileStore` shape).
|
||||
- Never invent Si4684 opcodes — cite AN649.
|
||||
- Never invent BT1035 AT strings — cite Feasycom BT1035 programming guide.
|
||||
- One logical change per commit; 50/72 messages.
|
||||
- Update `ch-classes.tex` / `ch-api.tex` when public API or HTTP changes.
|
||||
|
||||
@@ -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.3.
|
||||
wire protocol and behaviour as shipped in firmware~0.8.4.
|
||||
|
||||
\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.3","serialNumber":"0004A3123456",
|
||||
{"status":"ok","fw":"0.8.4","serialNumber":"0004A3123456",
|
||||
"chips":{"si4684":true,"adau1701":true,"bt1035":true}}
|
||||
\end{drcode}
|
||||
\begin{itemize}
|
||||
@@ -180,9 +180,19 @@ Success response: \texttt{\{"status":"playing"\}}. HTTP status:
|
||||
\subsection{\texttt{POST /api/tuner/seek}}
|
||||
\label{sec:api-tuner-seek}
|
||||
|
||||
Seeks FM upward (no request body). Returns
|
||||
\texttt{\{"frequency\_khz":...\}} on success. HTTP status: \textbf{200 OK};
|
||||
\textbf{409} on seek failure.
|
||||
Seeks FM in the requested direction. Optional JSON body parsed by
|
||||
\texttt{core::parseTunerSeekJson()}; an empty body defaults to upward seek.
|
||||
Returns \texttt{\{"frequency\_khz":...\}} on success. HTTP status:
|
||||
\textbf{200 OK}; \textbf{400} for invalid \texttt{direction}; \textbf{409} on
|
||||
seek failure.
|
||||
|
||||
\begin{drnote}[Request schema]
|
||||
\begin{drcode}[JSON]
|
||||
{"direction":"up"}
|
||||
\end{drcode}
|
||||
Use \texttt{"down"} for downward seek. Omit the body or send \texttt{{}}
|
||||
for upward seek (backward compatible).
|
||||
\end{drnote}
|
||||
|
||||
\subsection{\texttt{GET /api/audio/profile}}
|
||||
\label{sec:api-audio-profile-get}
|
||||
@@ -309,13 +319,14 @@ Adjusts bass emphasis via a PEQ overlay on bands 1--2 (100\,Hz /
|
||||
\subsection{\texttt{GET /api/bluetooth/status}}
|
||||
\label{sec:api-bluetooth-status}
|
||||
|
||||
Returns BT1035 boot flag, whether discoverable mode was requested, and the
|
||||
last read A2DP state. Serialised by
|
||||
\texttt{core::serializeBluetoothStatusJson()}.
|
||||
Returns BT1035 boot flag, whether discoverable mode was requested, the
|
||||
last read A2DP state, module friendly name, and auto-reconnect count.
|
||||
Serialised by \texttt{core::serializeBluetoothStatusJson()}.
|
||||
|
||||
\begin{drnote}[Response schema]
|
||||
\begin{drcode}[JSON]
|
||||
{"booted":true,"pairing":false,"a2dp":"standby"}
|
||||
{"booted":true,"pairing":false,"a2dp":"standby",
|
||||
"device_name":"DigiRadio-A1B2","auto_reconnect":3}
|
||||
\end{drcode}
|
||||
\end{drnote}
|
||||
|
||||
@@ -336,6 +347,31 @@ Leaves discoverable mode (\texttt{AT+PAIR=0}). Success:
|
||||
|
||||
Releases the current A2DP session (\texttt{AT+A2DPDISC}).
|
||||
|
||||
\subsection{\texttt{GET /api/bluetooth/paired}}
|
||||
\label{sec:api-bluetooth-paired}
|
||||
|
||||
Returns paired remotes from \texttt{AT+PLIST}, serialised by
|
||||
\texttt{core::serializeBluetoothPairedJson()}.
|
||||
|
||||
\begin{drnote}[Response schema]
|
||||
\begin{drcode}[JSON]
|
||||
{"devices":[{"index":1,"mac":"001122334455","name":"Phone"}]}
|
||||
\end{drcode}
|
||||
\end{drnote}
|
||||
|
||||
\subsection{\texttt{POST /api/bluetooth/auto-reconnect}}
|
||||
\label{sec:api-bluetooth-auto-reconnect}
|
||||
|
||||
Sets the module auto-reconnect retry count (\texttt{AT+AUTOCONN=0..15}).
|
||||
Body parsed by \texttt{core::parseBluetoothAutoReconnectJson()}.
|
||||
|
||||
\begin{drnote}[Request schema]
|
||||
\begin{drcode}[JSON]
|
||||
{"times":3}
|
||||
\end{drcode}
|
||||
Success: \texttt{\{"status":"saved"\}}.
|
||||
\end{drnote}
|
||||
|
||||
% ------------------------------------------------------------------
|
||||
% Station presets (Slice 4)
|
||||
% ------------------------------------------------------------------
|
||||
|
||||
@@ -153,6 +153,9 @@ updating \texttt{core::Bt1035AtCommand}, the manual, and a host test.
|
||||
\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 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Enumerated AT commands (\texttt{core::Bt1035AtCommand}). Boot
|
||||
@@ -179,6 +182,10 @@ updating \texttt{core::Bt1035AtCommand}, the manual, and a host test.
|
||||
\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.}
|
||||
@@ -240,8 +247,8 @@ 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
|
||||
for name, paired-device list, and reconnect not yet wrapped by firmware.
|
||||
\item Feasycom FSC-BT1035 AT command manual (vendor) --- full command set;
|
||||
firmware wraps name, paired list, and auto-reconnect for the Web UI.
|
||||
\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.
|
||||
|
||||
@@ -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.3 on \texttt{main}. Public classes
|
||||
The class reference tracks firmware~0.8.4 on \texttt{main}. Public classes
|
||||
are grouped by layer: domain core, application services, and hardware drivers.
|
||||
|
||||
% ------------------------------------------------------------------
|
||||
@@ -154,6 +154,12 @@ are included. Wrong-band calls return \texttt{Si4684Error::WrongBand}.
|
||||
Register-level opcodes remain private; see \texttt{Si4684Types.hpp} for status
|
||||
DTOs. Full DAB/FM tuning guide: Chapter~\ref{ch:si4684}.
|
||||
|
||||
\section{Bt1035PairedDevice}\label{cls:Bt1035PairedDevice}
|
||||
Plain DTO for one \texttt{+PLIST=} line from the FSC-BT1035: paired-record
|
||||
index (1--8), 12-digit MAC, and optional friendly name. Parsed by
|
||||
\texttt{core::parseBt1035PairedListResponse()} and serialised on
|
||||
\texttt{GET /api/bluetooth/paired}.
|
||||
|
||||
\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
|
||||
|
||||
@@ -319,7 +319,7 @@ The setup UI and REST clients use \texttt{tuner::TunerService}, which wraps
|
||||
FM & \texttt{\{"band":"fm","frequency\_khz":64000..108000\}} \\
|
||||
\texttt{GET /api/tuner/services} & DAB only & Programme list for ensemble \\
|
||||
\texttt{POST /api/tuner/play} & DAB only & Start \texttt{service\_id}/\texttt{component\_id} \\
|
||||
\texttt{POST /api/tuner/seek} & FM only & Seek up, return new kHz \\
|
||||
\texttt{POST /api/tuner/seek} & FM only & Seek up/down, return new kHz \\
|
||||
\texttt{GET /api/tuner/status} & both & Locked state, RSQ or DIGRAD \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
\vfill
|
||||
{\color{drInk}\large Michele Bigi\par}
|
||||
\vspace{2mm}
|
||||
{\color{drGray}Firmware 0.8.3 \quad\textbullet\quad 2026\par}
|
||||
{\color{drGray}Firmware 0.8.4 \quad\textbullet\quad 2026\par}
|
||||
\vspace{2mm}
|
||||
{\color{drGray}Hardware: CERN-OHL-S v2 \quad\textbullet\quad Firmware: Apache-2.0\par}
|
||||
\vspace{2mm}
|
||||
|
||||
Reference in New Issue
Block a user