# Contributing to DigiRadio Thanks for your interest in DigiRadio. This document covers the firmware development conventions. The full, authoritative rules live in [`Software/AGENTS.md`](Software/AGENTS.md); this is the human-facing summary. **Current firmware:** **0.8.3** on `main` — agent tasks T1–T8 complete; device HIL pending first PCB. DigiRadio is dual-licensed: hardware under **CERN-OHL-S v2**, firmware under **Apache-2.0**. By contributing, you agree your contributions are licensed under the same terms as the part of the project they touch. ## Toolchain | Item | Choice | |-----------|------------------------------------------| | Framework | ESP-IDF v5.5.x (native, not Arduino) | | Language | C++23, pinned `-std=gnu++23` | | Errors | `std::expected`; exceptions off| | Security | NVS + flash encryption (dev mode) | | Docs | Doxygen + LaTeX manual sync (CI enforced)| On macOS, the host unit tests need a C++23 standard library: use Homebrew `llvm` (>= 18) or `gcc-14`, not the system Apple Clang. ## Build, test, docs Si4684 blobs (local only, not in git): ```bash cd Software python3 tools/fetch_si4684_firmware.py --dab-only python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware ``` Device build / flash / monitor (first encrypted flash: include `erase-flash`): ```bash idf.py set-target esp32s3 idf.py build idf.py erase-flash flash monitor ``` Host unit tests (pure core, no hardware): ```bash cmake -S components/core/test -B build-host \ -DCMAKE_CXX_COMPILER="$(brew --prefix llvm)/bin/clang++" cmake --build build-host ctest --test-dir build-host --output-on-failure ``` Documentation and policy checks (must exit 0; run from `Software/`): ```bash doxygen Doxyfile python3 tools/check-manual-sync.py python3 tools/check_si4684_blobs.py ``` After editing the web UI: `python3 tools/gzip-www.sh`. The LaTeX manual lives in `Software/docs/manual/` (canonical). Design and HTTP JSON API: `ch-api.tex`; security: `docs/security-flash-nvs.md`. Rebuild the PDF with `latexmk -lualatex manual.tex` inside `Software/docs/manual/`. ## Coding conventions The guiding idea is *Code That Fits in Your Head*: code must fit in human working memory at every zoom level. - **Complexity `<= 7`** per method; **methods fit an 80x24 box** (<= 80 columns, <= 24 lines). One method does one thing. - **Strong typing.** No primitive obsession: domain quantities are their own types. `enum class` always; no booleans for mode selection. Validate untrusted input once at the boundary. - **RAII and `const` by default.** Wrap every hardware/OS handle; no raw `new`/`delete`; borrow with `std::span`, not pointer + length. - **Functional core, imperative shell.** Pure logic in `components/core` (no ESP-IDF headers, host-tested); hardware access in the shell. - **No silent failure.** Fallible operations return `std::expected`; every timeout is an explicit error. - **Embedded discipline.** No dynamic allocation in audio or ISR paths; no virtual calls in IRAM-safe ISRs; every wait has a timeout. - **Never invent** a register address, opcode, or boot sequence — cite the datasheet section in a comment, or stop and ask. ## File headers and documentation Every source file starts with the Apache-2.0 header (see [`Software/apache-header.txt`](Software/apache-header.txt)), with `@file`, `@author`, and `@date` filled in. Every class and method carries a Doxygen block with, in order: name (`@dname`), parameters (`@param`), return (`@return`), public state used (`@pubstate`), a description of intent (not a restatement of the code), and `@author` / `@date`. The `doxygen Doxyfile` build enforces this — an undocumented symbol fails the build. ## Commits and pull requests - **Small, focused commits.** Each commit compiles and keeps host tests green. One logical change per commit. - **Commit messages: 50/72.** Summary line <= 50 characters, imperative mood; blank line; body wrapped at 72 explaining *why*. - Keep `main` always building. Use feature branches for work in progress; no commented-out code in commits. ## Definition of Done Before opening a PR, confirm: - [ ] Compiles with warnings-as-errors; clang-tidy clean. - [ ] Every file has the Apache-2.0 header. - [ ] Every class and method has its documentation block. - [ ] `doxygen Doxyfile` exits 0 with an empty warnings log. - [ ] `python3 tools/check-manual-sync.py` passes. - [ ] `python3 tools/check_si4684_blobs.py` passes. - [ ] Manual updated: `ch-classes.tex` for new/changed public classes; `ch-api.tex` for new/changed HTTP endpoints. - [ ] Every method fits 80x24 and complexity <= 7. - [ ] Fallible paths return typed results; no silent failure. - [ ] Pure-core logic has passing host unit tests. - [ ] No secret is loggable or stored in plaintext. - [ ] Register-level decisions cite the datasheet section. - [ ] No dynamic allocation in audio/ISR paths. - [ ] Web UI changes regenerate `index.html.gz` via `tools/gzip-www.sh`. ## Editor setup (optional) The repository ships Cursor rules under `Software/.cursor/rules/`. If you use Cursor, open the `Software/` directory as the project so the rules and `AGENTS.md` are picked up automatically.