Sync all project docs for firmware 0.8.3 completion.

Update READMEs, manual chapters, agent guides, CONTRIBUTING, and TODO to reflect T1–T8 done, encrypted NVS, CI gates, and pending HIL checklist.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 07:49:07 +02:00
co-authored by Cursor
parent a8f2fd1c6c
commit 8439ec4055
15 changed files with 283 additions and 260 deletions
+1 -1
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.5.0 (Slices~1--5).
wire protocol and behaviour as shipped in firmware~0.8.3.
\section{Transport and reachability}
+37 -4
View File
@@ -14,6 +14,7 @@ builds and is unit-tested on the host machine.
Framework & ESP-IDF v5.5.x (native) \\
Language & C++23 (\texttt{-std=gnu++23}) \\
Error model & \texttt{std::expected}; exceptions off \\
Security & NVS + flash encryption (dev mode); see \texttt{docs/security-flash-nvs.md} \\
Documentation & Doxygen (build must pass) \\
\bottomrule
\end{tabular}
@@ -21,14 +22,32 @@ builds and is unit-tested on the host machine.
\label{tab:build-toolchain}
\end{table}
\section{Si4684 firmware blobs (local only)}
Proprietary Si4684 images are \textbf{not} in git. Before the first device
build:
\begin{drcode}[Populate blobs (from Software/)]
python3 tools/fetch_si4684_firmware.py --dab-only
python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware
python3 tools/check_si4684_blobs.py
\end{drcode}
See \texttt{Firmware/Si4684-Firmware/README.md} for procurement options.
\section{Device build}
First flash after enabling encryption (fw~0.8.3+) requires a one-time erase:
\begin{drcode}[Build, flash, monitor]
idf.py set-target esp32s3
idf.py build
idf.py -p <port> flash monitor
idf.py erase-flash flash monitor
\end{drcode}
Production flash-encryption release mode uses \texttt{sdkconfig.defaults.production}
as an overlay --- irreversible on the chip; see the security doc before use.
\section{Host unit tests}
The pure core is tested on the host, with no board attached. On macOS the
@@ -42,9 +61,12 @@ cmake --build build-host
ctest --test-dir build-host --output-on-failure
\end{drcode}
Thirteen test executables cover JSON parsing, audio design, station list,
integration service, and broadcast metadata accumulators.
\section{Documentation}
Documentation has two enforced checks, run from the \texttt{Software/}
Documentation has enforced checks, run from the \texttt{Software/}
directory:
\begin{enumerate}
@@ -55,17 +77,21 @@ directory:
\texttt{\textbackslash label\{cls:ClassName\}} section in
\texttt{docs/manual/ch-classes.tex}. Design-level HTTP API
documentation lives in Chapter~\ref{ch:api}.
\item \textbf{Si4684 blob policy} --- \texttt{tools/check\_si4684\_blobs.py}
ensures no proprietary \texttt{.bin} is tracked in git.
\end{enumerate}
\begin{drcode}[Docs (from Software/)]
doxygen Doxyfile
python3 tools/check-manual-sync.py
python3 tools/check_si4684_blobs.py
python3 tools/gzip-www.sh # after editing components/net/www/index.html
\end{drcode}
\section{Continuous integration}
Every push and pull request to \texttt{main} runs
\texttt{.github/workflows/ci.yml} at the repository root (three parallel
\texttt{.github/workflows/ci.yml} at the repository root (four parallel
jobs, all from the \texttt{Software/} directory):
\begin{enumerate}
@@ -74,6 +100,7 @@ jobs, all from the \texttt{Software/} directory):
\item \textbf{Doxygen} --- must exit 0 with an empty
\texttt{docs/api/doxygen-warnings.log}.
\item \textbf{Manual sync} --- \texttt{tools/check-manual-sync.py}.
\item \textbf{Si4684 blobs} --- \texttt{tools/check\_si4684\_blobs.py}.
\end{enumerate}
To rebuild the PDF manual (requires a LaTeX installation):
@@ -84,8 +111,14 @@ latexmk -lualatex manual.tex
\end{drcode}
\begin{drcaution}[Keep it green]
Both checks are part of the definition of done. A firmware change that
All checks are part of the definition of done. A firmware change that
adds or modifies a public class, a REST endpoint, or its behaviour must
update the Doxygen doc blocks, \texttt{ch-classes.tex} (for classes), and
\texttt{ch-api.tex} (for HTTP) in the same change.
\end{drcaution}
\section{Hardware validation}
Automated CI does not attach to a board. When the PCB is available, run the
checklist in \texttt{docs/security-flash-nvs.md} and the HIL items in
\texttt{docs/TODO.md} (section P4).
+12 -12
View File
@@ -16,16 +16,15 @@ 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 firmware is under active development. As each public class lands, its
section appears below, grouped by layer: domain core, application
services, and hardware drivers.
The class reference tracks firmware~0.8.3 on \texttt{main}. Public classes
are grouped by layer: domain core, application services, and hardware drivers.
% ------------------------------------------------------------------
% Domain core (Slice 1)
% ------------------------------------------------------------------
\section{FirmwareVersion}\label{cls:FirmwareVersion}
Strong type wrapping the firmware release identifier (e.g.\ \texttt{0.1.0}).
Strong type wrapping the firmware release identifier (e.g.\ \texttt{0.8.3}).
Used by \texttt{HealthStatus} and the \texttt{/api/health} endpoint so
version strings are never passed as bare \texttt{char*} across module
boundaries. Invariant: non-empty at construction.
@@ -65,11 +64,10 @@ tuner via \texttt{tuner::TunerService}; audio via
presets via \texttt{station::StationService}.
\section{NetBootstrap}\label{cls:NetBootstrap}
Owns network resources for setup or STA mode.
\texttt{start(store, tuner)} initialises the platform, joins stored Wi-Fi
when credentials exist, or falls back to the \texttt{DigiRadio-setup}
SoftAP. Must outlive \texttt{app\_main} for the process lifetime.
\texttt{start(store, tuner, audio)} also wires the audio REST routes.
Owns network resources for setup or STA mode. Initialises encrypted NVS via
\texttt{secure\_store::initEncryptedStorage()}, then Wi-Fi and the HTTP server.
Wires tuner, audio, Bluetooth, station, and integration services into REST
handlers. Must outlive \texttt{app\_main} for the process lifetime.
% ------------------------------------------------------------------
% Domain core + secure store (Slice 2)
@@ -89,9 +87,11 @@ Domain value pairing \texttt{WifiSsid} with a \texttt{Secret} password.
Open networks use an empty password; WPA-PSK requires 8--63 characters.
\section{ISecureStore}\label{cls:ISecureStore}
Abstract persistence boundary for credentials at rest. Slice~2 implements
Wi-Fi credential save/load/clear; station list and user credentials arrive
in later slices. Host tests use fakes; the shell uses \texttt{NvsSecureStore}.
Abstract persistence boundary for credentials and preset data at rest.
Wi-Fi credentials, station list JSON, and last-preset index use
\texttt{NvsSecureStore}; audio profiles use \texttt{NvsAudioProfileStore}.
Host tests use fakes; the shell uses NVS backends after
\texttt{initEncryptedStorage()}.
\section{StaClient}\label{cls:StaClient}
RAII STA join helper with an explicit connect timeout. Assumes
+24 -15
View File
@@ -64,7 +64,8 @@ itself.
\node[layer, fill=black!8, below=of shell] (services) {%
\textbf{Application services}\\[2pt]
TunerService \textbullet\ AudioService \textbullet\
ConfigService \textbullet\ NetworkService};
StationService \textbullet\ BluetoothService \textbullet\
IntegrationService};
\node[layer, fill=black!12, below=of services] (core) {%
\textbf{Domain core (pure, host-tested)}\\[2pt]
Station \textbullet\ Frequency \textbullet\ EqProfile \textbullet\
@@ -165,23 +166,31 @@ through \texttt{core::ISecureStore}, and the device reboots into STA mode
on the next boot. The HTTP endpoints and JSON schemas are documented in
Chapter~\ref{ch:api}.
\paragraph{Implemented (Slices~1--2).}
\paragraph{Implemented (fw 0.8.3).}
\begin{itemize}
\item \texttt{GET /api/health} --- health DTO
(\texttt{core::HealthStatus}, serialised in the pure core).
\item \texttt{POST /api/wifi} --- Wi-Fi provisioning
(\texttt{core::WifiCredentials} via \texttt{parseWifiProvisionJson}).
\item \texttt{secure\_store::NvsSecureStore} --- NVS persistence for
SSID and PSK (\texttt{core::Secret}); station list and user
credentials arrive in later slices on the same
\texttt{ISecureStore} interface.
\item \textbf{Network} --- \texttt{GET /api/health}, \texttt{POST /api/wifi},
SoftAP/STA state machine (\texttt{NetState}), tabbed gzipped web UI.
\item \textbf{Tuner} --- \texttt{/api/tuner/*} (FM/DAB tune, seek, services,
play); RDS and DAB dynamic labels in status JSON.
\item \textbf{Audio} --- \texttt{/api/audio/*} (profile, reset, stereo/bass
enhance); six-band EQ via \texttt{AudioService}.
\item \textbf{Bluetooth} --- \texttt{/api/bluetooth/*} (pair, stop, disconnect,
status).
\item \textbf{Presets} --- \texttt{/api/stations/*} including reorder and
integrated recall via \texttt{IntegrationService}.
\item \textbf{Storage} --- \texttt{NvsSecureStore}, \texttt{NvsAudioProfileStore},
encrypted NVS + flash encryption (development mode); keys in
\texttt{nvs\_keys} partition; init via \texttt{initEncryptedStorage()}.
\end{itemize}
Sensitive data --- Wi-Fi credentials today; user credentials and the
station/frequency list in later slices --- uses \texttt{core::Secret} so
values cannot be logged or implicitly converted to a string; buffers are
cleared on destruction. Firmware~0.8.3+ enables NVS and flash encryption at
rest via \texttt{secure\_store::initEncryptedStorage()} (see
\paragraph{Legacy note (Slices 1--2).}
The first slices introduced health, Wi-Fi provisioning, and the secure-store
boundary; the bullets above supersede the original slice-scoped list.
Sensitive data uses \texttt{core::Secret} where applicable so values cannot
be logged or implicitly converted to a string; buffers are cleared on
destruction. Firmware~0.8.3 enables NVS and flash encryption at rest via
\texttt{secure\_store::initEncryptedStorage()} (see
\texttt{docs/security-flash-nvs.md} and Chapter~\ref{sec:api-storage}).
\section{Companion-chip boot at power-up}
+3 -1
View File
@@ -4,7 +4,9 @@
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. The whole project --- hardware and firmware --- is
high-resolution codec. Firmware~0.8.3 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,
build, and improve.
+1 -1
View File
@@ -218,7 +218,7 @@ SigmaStudio export (Section~\ref{sec:ss-export}).
\label{sec:ss-enhancements}
The SigmaStudio export does not include dedicated stereo widener or bass
boost blocks. Firmware~0.5.0 maps enhancement levels (0--100) onto the
boost blocks. Firmware maps enhancement levels (0--100) onto the
existing Param EQ1 bands at runtime:
\begin{itemize}
+4 -3
View File
@@ -29,9 +29,10 @@
\vfill
{\color{drInk}\large Michele Bigi\par}
\vspace{1mm}
{\color{drGray} 2026 \quad\textbullet\quad Hardware: CERN-OHL-S v2
\quad\textbullet\quad Firmware: Apache-2.0\par}
\vspace{2mm}
{\color{drGray}Firmware 0.8.3 \quad\textbullet\quad 2026\par}
\vspace{2mm}
{\color{drGray}Hardware: CERN-OHL-S v2 \quad\textbullet\quad Firmware: Apache-2.0\par}
\vspace{2mm}
{\color{drGray}\small \url{https://github.com/manvalan/DigiRadio}\par}
\end{titlepage}