diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..012a0d0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + host-tests: + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: Software + steps: + - uses: actions/checkout@v4 + - name: Install build tools + run: sudo apt-get update && sudo apt-get install -y cmake g++-14 + - name: Build and run host tests + run: | + cmake -S components/core/test -B build-host \ + -DCMAKE_CXX_COMPILER=g++-14 + cmake --build build-host + ctest --test-dir build-host --output-on-failure + + doxygen: + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: Software + steps: + - uses: actions/checkout@v4 + - name: Install Doxygen + run: sudo apt-get update && sudo apt-get install -y doxygen + - name: Run Doxygen (fail on warnings) + run: doxygen Doxyfile + + manual-sync: + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: Software + steps: + - uses: actions/checkout@v4 + - name: Check manual class sync + run: python3 tools/check-manual-sync.py --src components --manual docs/manual diff --git a/README.md b/README.md index c76c548..69dd8ac 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Firmware development backlog and agent tasks: - ✅ **PCB layout** — 6-layer, DRC clean, plane continuity verified - ✅ **BOM** — finalised (manufacturable / sourced) - 🔜 **Prototype** — in fabrication (PCBWay) -- 🛠️ **Firmware** — fw 0.7.0 on `main` (tuner, DSP, BT pairing, presets); see [`Software/docs/TODO.md`](Software/docs/TODO.md) +- 🛠️ **Firmware** — fw 0.7.1 on `main` (CI + tuner, DSP, BT, presets); see [`Software/docs/TODO.md`](Software/docs/TODO.md) --- diff --git a/Software/README.md b/Software/README.md index 0fc3ba3..a31c175 100644 --- a/Software/README.md +++ b/Software/README.md @@ -2,8 +2,8 @@ Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3. -**Status:** fw **0.7.0** — companion-chip boot (Si4684, ADAU1701, BT1035), -Wi-Fi provisioning, tuner/audio REST API, Bluetooth pairing, station presets. +**Status:** fw **0.7.1** — CI on `main` (host tests, Doxygen, manual sync); +companion-chip boot, REST API, Bluetooth pairing, station presets. See [`docs/TODO.md`](docs/TODO.md) for the agent task list. ## Quick start @@ -26,7 +26,7 @@ cmake --build build-host ctest --test-dir build-host --output-on-failure ``` -Documentation gates (must exit 0 before merging): +Documentation gates (must exit 0 before merging; also enforced in CI): ```bash doxygen Doxyfile diff --git a/Software/components/core/include/core/AudioEnhancements.hpp b/Software/components/core/include/core/AudioEnhancements.hpp index 8900773..a3a2b80 100644 --- a/Software/components/core/include/core/AudioEnhancements.hpp +++ b/Software/components/core/include/core/AudioEnhancements.hpp @@ -21,8 +21,8 @@ namespace core { * * @dname AudioEnhancements * @return n/a (type) - * @pubstate Mapped to PEQ bands at runtime (Chapter~\ref{ch:sigmastudio}). - * No dedicated SigmaStudio blocks; see \texttt{applyEnhancementsToEq}. + * @pubstate Mapped to PEQ bands at runtime (manual chapter sigmastudio). + * No dedicated SigmaStudio blocks; see \c applyEnhancementsToEq. * * @author Michele Bigi * @date 2026-07-06 diff --git a/Software/components/core/include/core/AudioProfileJson.hpp b/Software/components/core/include/core/AudioProfileJson.hpp index a45a901..cf3b252 100644 --- a/Software/components/core/include/core/AudioProfileJson.hpp +++ b/Software/components/core/include/core/AudioProfileJson.hpp @@ -55,7 +55,7 @@ namespace core { * @brief serializeAudioSavedJson — success response after profile apply. * * @dname serializeAudioSavedJson - * @return JSON object \texttt{\{"status":"saved"\}}. + * @return JSON object with \c status set to \c saved. * @pubstate none * * @author Michele Bigi @@ -77,7 +77,7 @@ namespace core { [[nodiscard]] std::string serializeAudioErrorJson(std::string_view reason); /** - * @brief parseEnhanceLevelJson — parse POST body \texttt{\{"level":0..100\}}. + * @brief parseEnhanceLevelJson — parse POST body with level 0..100. * * @dname parseEnhanceLevelJson * @param json Untrusted request body. diff --git a/Software/components/core/include/core/Bt1035At.hpp b/Software/components/core/include/core/Bt1035At.hpp index 1f5665e..a926fd3 100644 --- a/Software/components/core/include/core/Bt1035At.hpp +++ b/Software/components/core/include/core/Bt1035At.hpp @@ -85,7 +85,7 @@ inline constexpr std::size_t kBt1035BootInitCommandCount = 2U; * * @dname buildBt1035AtLine * @param command Typed AT command. - * @return Full line including \r\n suffix. + * @return Full line including CRLF suffix. * @pubstate none * * @author Michele Bigi diff --git a/Software/components/core/include/core/Station.hpp b/Software/components/core/include/core/Station.hpp index 97db6be..8a35310 100644 --- a/Software/components/core/include/core/Station.hpp +++ b/Software/components/core/include/core/Station.hpp @@ -59,12 +59,67 @@ public: std::optional fmFrequency, std::optional presetSlot); + /** + * @brief name — read the preset display label. + * + * @dname name + * @return Station name value. + * @pubstate reads name_. + */ [[nodiscard]] const StationName& name() const noexcept; + + /** + * @brief band — read the target RF band. + * + * @dname band + * @return Dab or Fm band selector. + * @pubstate reads band_. + */ [[nodiscard]] TunerBand band() const noexcept; + + /** + * @brief dabFreqIndex — read the Band III ensemble index. + * + * @dname dabFreqIndex + * @return Ensemble index 0–37. + * @pubstate reads dabFreqIndex_. + */ [[nodiscard]] std::uint8_t dabFreqIndex() const noexcept; + + /** + * @brief dabServiceId — read optional DAB service id for play(). + * + * @dname dabServiceId + * @return Service id when stored, otherwise empty. + * @pubstate reads dabServiceId_. + */ [[nodiscard]] std::optional dabServiceId() const noexcept; + + /** + * @brief dabComponentId — read optional DAB component id for play(). + * + * @dname dabComponentId + * @return Component id when stored, otherwise empty. + * @pubstate reads dabComponentId_. + */ [[nodiscard]] std::optional dabComponentId() const noexcept; + + /** + * @brief fmFrequency — read FM centre frequency when band is Fm. + * + * @dname fmFrequency + * @return Frequency when stored, otherwise empty. + * @pubstate reads fmFrequency_. + */ [[nodiscard]] std::optional fmFrequency() const noexcept; + + /** + * @brief presetSlot — read optional hardware preset button slot. + * + * @dname presetSlot + * @return Preset slot when assigned, otherwise empty. + * @pubstate reads presetSlot_. + */ [[nodiscard]] std::optional presetSlot() const noexcept; /** diff --git a/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp b/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp index 518ae6d..1e63426 100644 --- a/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp +++ b/Software/components/drivers/bt1035/include/bt1035/Bt1035Driver.hpp @@ -86,7 +86,7 @@ public: * @pubstate sets booted_ after Ping + AT+AUXCFG=1 both return OK. * * Sequence: hardware reset, UART @ 115200 with RTS/CTS, then - * core::bootInitSequence() (Chapter~\ref{ch:bt1035}). + * core::bootInitSequence() (see manual chapter bt1035). * * @author Michele Bigi * @date 2026-07-06 diff --git a/Software/components/net/include/net/NetBootstrap.hpp b/Software/components/net/include/net/NetBootstrap.hpp index 18893f9..62c8c47 100644 --- a/Software/components/net/include/net/NetBootstrap.hpp +++ b/Software/components/net/include/net/NetBootstrap.hpp @@ -63,9 +63,12 @@ public: * @brief start — init platform and join stored Wi-Fi or open SoftAP. * * @dname start - * @param store Secure store consulted for saved STA credentials. - * @param tuner Tuner service exposed by the HTTP API. - * @param audio Audio service exposed by the HTTP API. + * @param store Secure store consulted for saved STA credentials. + * @param tuner Tuner service exposed by the HTTP API. + * @param audio Audio service exposed by the HTTP API. + * @param bluetooth Bluetooth pairing service for REST routes. + * @param stations Station preset service for REST routes. + * @param companionChips Boot flags exposed on GET /api/health. * @return NetBootstrap on success, or a NetError. * @pubstate none * diff --git a/Software/components/net/include/net/SetupWebServer.hpp b/Software/components/net/include/net/SetupWebServer.hpp index df66dcc..278ebbd 100644 --- a/Software/components/net/include/net/SetupWebServer.hpp +++ b/Software/components/net/include/net/SetupWebServer.hpp @@ -137,8 +137,9 @@ public: * @param netState Active network phase exposed to handlers. * @param tuner Tuner service for the tuner REST routes. * @param audio Audio service for the audio REST routes. - * @param bluetooth Bluetooth service for pairing REST routes. - * @param stations Station preset service for list REST routes. + * @param bluetooth Bluetooth service for pairing REST routes. + * @param stations Station preset service for list REST routes. + * @param companionChips Boot flags for GET /api/health. * @return Ok on success, or NetError::HttpServerStartFailed. * @pubstate writes server_, store_, netState_, and service pointers on success. * diff --git a/Software/docs/TODO.md b/Software/docs/TODO.md index 2b8f534..088417d 100644 --- a/Software/docs/TODO.md +++ b/Software/docs/TODO.md @@ -12,9 +12,8 @@ errors, no plaintext secrets. Working directory for all commands is `Software/`. -**Current firmware:** `0.7.0` — BT1035 pairing (`/api/bluetooth/*`), -station presets (`/api/stations/*`), host tests green (10/10), manual -sync green (38 classes). +**Current firmware:** `0.7.1` — CI green gate (Doxygen + host tests + +manual sync), BT1035 pairing, station presets. --- @@ -30,28 +29,14 @@ sync green (38 classes). ## P0 — Fix the build gate (do this first) -### T1. Clear the 16 Doxygen warnings -**Why:** `doxygen Doxyfile` currently exits non-zero, so the docs gate is -red and CI (once added) will fail. -**What:** -- Replace the invalid `\texttt{...}` with `` `...` `` (backticks) or - `\c word` in: `core/AudioProfileJson.hpp` (l.58, l.80), - `core/AudioEnhancements.hpp` (l.25). -- Fix `\r` interpreted as a command in `core/Bt1035At.hpp` (l.64) — - wrap the AT string in `@code ... @endcode` or escape as `\\r`. -- Add a space after `\ref` in `bt1035/Bt1035Driver.hpp` (l.89) and - `core/AudioEnhancements.hpp` (l.24). -- Document the missing `@param companionChips` in - `net/NetBootstrap.hpp` (`start`) and `net/SetupWebServer.hpp` (`start`). -**Done when:** `doxygen Doxyfile` exits 0 and `docs/api/doxygen-warnings.log` -is empty. +### T1. Clear the 16 Doxygen warnings — **DONE (fw 0.7.1)** +Fixed invalid `\texttt`/`\r`/`\ref` in doc blocks; documented +`Station` accessors and `NetBootstrap`/`SetupWebServer` parameters. +`doxygen Doxyfile` exits 0 with an empty warnings log. -### T2. Add the CI workflow -**Why:** validate every push automatically. -**What:** add `.github/workflows/ci.yml` with three jobs — host build + -`ctest`, `doxygen` (fail on warnings), and `check-manual-sync.py`. A ready -draft was prepared; place it and confirm all three jobs pass. -**Done when:** the workflow is green on `main` after T1. +### T2. Add the CI workflow — **DONE (fw 0.7.1)** +`.github/workflows/ci.yml`: host `ctest`, Doxygen, manual sync on every +push/PR to `main`. --- diff --git a/Software/docs/manual/ch-build.tex b/Software/docs/manual/ch-build.tex index 80b54c5..e4fa63e 100644 --- a/Software/docs/manual/ch-build.tex +++ b/Software/docs/manual/ch-build.tex @@ -62,6 +62,20 @@ doxygen Doxyfile python3 tools/check-manual-sync.py \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 +jobs, all from the \texttt{Software/} directory): + +\begin{enumerate} + \item \textbf{Host tests} --- \texttt{cmake} + \texttt{ctest} on + \texttt{components/core/test} (C++23, \texttt{g++-14} on Ubuntu). + \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}. +\end{enumerate} + To rebuild the PDF manual (requires a LaTeX installation): \begin{drcode}[Manual PDF]