\chapter{Class Reference} \label{ch:classes} This chapter documents the firmware's public classes at the design level: what each class is responsible for, which collaborators it depends on, its key invariants, and how it fits the system. It complements --- and does not duplicate --- the generated API documentation, which carries the exact method signatures. The HTTP JSON endpoints are documented separately in Chapter~\ref{ch:api}. \begin{drnote}[How this chapter grows] Per the development rules, every public, architecturally-significant class (the drivers, the application services, and the public domain-core types) gets a section here, tagged \texttt{\textbackslash label\{cls:ClassName\}}, 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 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.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. \section{FrequencyKHz}\label{cls:FrequencyKHz} Validated FM centre frequency in kilohertz (64\,000–108\,000). Parsed once from JSON at the HTTP boundary via \texttt{FrequencyKHz::tryFromKhz()}; trusted downstream by \texttt{ITuner}, \texttt{Si4684Driver}, and status DTOs. \section{HealthStatus}\label{cls:HealthStatus} Immutable health-check DTO returned by \texttt{GET /api/health}. Built via \texttt{HealthStatus::ok(FirmwareVersion)} in the pure core; JSON serialisation lives in \texttt{serializeHealthStatusJson()}. No ESP-IDF headers. % ------------------------------------------------------------------ % Network shell (Slice 1) % ------------------------------------------------------------------ \section{SoftApConfig}\label{cls:SoftApConfig} Immutable value type holding SoftAP parameters (SSID, channel, station limit). Factory \texttt{setupDefault()} yields SSID \texttt{DigiRadio-setup} for first-time provisioning. \section{SoftApHost}\label{cls:SoftApHost} RAII wrapper around the ESP-IDF Wi-Fi stack that starts and stops the configured SoftAP. Imperative shell; no business logic. \section{SetupWebServer}\label{cls:SetupWebServer} Minimal HTTP server: gzipped setup UI, \texttt{GET /api/health}, \texttt{POST /api/wifi}, tuner routes (\texttt{/api/tuner/*}), audio routes (\texttt{/api/audio/*}), Bluetooth (\texttt{/api/bluetooth/*}), and station presets (\texttt{/api/stations/*}). JSON parsing and serialisation delegate to the pure core; credentials persist via \texttt{ISecureStore}; tuner via \texttt{tuner::TunerService}; audio via \texttt{audio::AudioService}; Bluetooth via \texttt{bluetooth::BluetoothService}; presets via \texttt{station::StationService}. \section{NetBootstrap}\label{cls:NetBootstrap} 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) % ------------------------------------------------------------------ \section{Secret}\label{cls:Secret} Opaque wrapper for sensitive strings. Buffer is zeroised on destruction; no \texttt{operator<<} and no plaintext serialisation. Shell code uses \texttt{usePlaintext()} transiently for driver APIs only. \section{WifiSsid}\label{cls:WifiSsid} Validated Wi-Fi SSID (1--32 bytes). Parsed once from untrusted JSON at the HTTP boundary before persistence or driver calls. \section{WifiCredentials}\label{cls:WifiCredentials} 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 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 \texttt{esp\_wifi\_init()} was already called by \texttt{NetBootstrap}. \section{NvsSecureStore}\label{cls:NvsSecureStore} \texttt{ISecureStore} implementation backed by an NVS namespace. Passwords are stored as NVS strings and never logged. NVS encryption and flash encryption are enabled in \texttt{sdkconfig.defaults}; initialisation runs in \texttt{secure\_store::initEncryptedStorage()} before network bring-up. % ------------------------------------------------------------------ % Hardware drivers (Slice 3) % ------------------------------------------------------------------ \section{IFirmwareBlobReader}\label{cls:IFirmwareBlobReader} Abstract streaming reader for firmware blobs embedded in flash. The Si4684 driver uses it to HOST\_LOAD patch and application images in bounded SPI chunks without allocating the full image on the heap. Host tests use the same interface over in-memory buffers. \section{EmbeddedBlobReader}\label{cls:EmbeddedBlobReader} Non-owning \texttt{IFirmwareBlobReader} over a contiguous byte range (linker symbols from \texttt{EMBED\_FILES} or test data). Implements offset-based \texttt{read()} with truncation at end-of-blob. \section{Si4684EmbeddedImages}\label{cls:Si4684EmbeddedImages} Owns \texttt{EmbeddedBlobReader} views over \texttt{rom\_patch\_016.bin}, \texttt{dab\_firmware.bin}, and \texttt{fm\_firmware.bin}. Constructed once; passed to \texttt{Si4684Driver}. \texttt{applicationImage(Si4684Band)} selects DAB or FM. \section{Si4684Driver}\label{cls:Si4684Driver} Full SPI driver for the Si4684 DAB+/FM tuner (Chapter~\ref{ch:si4684}). \texttt{boot(Si4684Band)} runs AN649 HOST\_LOAD, configures I\textsuperscript{2}S and band-specific properties, and records \texttt{loadedBand()}. After boot the driver exposes FM tuning/seek/RSQ/RDS, DAB ensemble tuning, digital service list fetch, and \texttt{startDabService}. Property access (\texttt{setProperty}, \texttt{setVolume}) and diagnostics (\texttt{getPartInfo}, \texttt{getSysState}) 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{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 \texttt{core::bootInitSequence()} (Ping + \texttt{AT+AUXCFG=1}). Returns \texttt{Bt1035Error} on timeout, ERROR response, or UART failure. \section{Adau1701Driver}\label{cls:Adau1701Driver} RAII I\textsuperscript{2}C driver for the ADAU1701 SigmaDSP (Chapter~\ref{ch:adau1701}). \texttt{boot()} asserts RESET\#, initialises the shared I2C bus, and replays the SigmaStudio export from \texttt{Firmware/ADAU1701-Firmware/} on every power-up (no EEPROM self-boot on DigiRadio). Runtime mixer, EQ, and master volume updates use the ADAU1701 safeload mechanism via \texttt{applyProfile()} and related methods (Section~\ref{sec:adau1701-driver}). \section{Adau1701Dsp}\label{cls:Adau1701Dsp} \texttt{core::IDsp} adapter over \texttt{Adau1701Driver}. Maps domain-level audio control to safeload I\textsuperscript{2}C transactions without exposing parameter RAM addresses to services (Chapter~\ref{ch:adau1701}, Section~\ref{sec:adau1701-stack}). % ------------------------------------------------------------------ % Domain core — audio (Slice 5) % ------------------------------------------------------------------ \section{GainDb}\label{cls:GainDb} Validated decibel gain/attenuation ($-96$\,dB to $+12$\,dB). Used for input volumes, master level, and EQ band gain. Converted to ADAU 8.23 fixpoint via \texttt{gainDbToLinearFixpoint()} before safeload. \section{FrequencyHz}\label{cls:FrequencyHz} Validated PEQ centre frequency (20--20\,000\,Hz) for the 48\,kHz SigmaStudio project. Parsed at the HTTP boundary before coefficient design. \section{EqBandIndex}\label{cls:EqBandIndex} Strong index into the six-band \texttt{Param EQ1} module (0--5). Maps to parameter RAM via \texttt{adau1701::paramAddrEqBandBase()}. \section{EqProfile}\label{cls:EqProfile} Six-band parametric EQ snapshot aligned with the SigmaStudio map (Chapter~\ref{ch:sigmastudio}): band index~0 is the fixed 20\,Hz high-pass; bands 1--5 are peaking at 100\,Hz--8\,kHz. Designed in the pure core; peaking bands are safeloaded by \texttt{Adau1701Driver::applyEq()} (band~0 is skipped). \section{IDsp}\label{cls:IDsp} Abstract ADAU1701 control boundary. Defines \texttt{applyProfile()}, \texttt{setInputVolume()}, \texttt{setEqBand()}, and related intent-level operations without ESP-IDF types. \section{IAudioProfileStore}\label{cls:IAudioProfileStore} Persistence boundary for \texttt{AudioProfile} (mixer, EQ, master). Device implementation: \texttt{NvsAudioProfileStore}; host tests use fakes. % ------------------------------------------------------------------ % Application services (Slice 4–5) % ------------------------------------------------------------------ \section{ITuner}\label{cls:ITuner} Abstract tuner boundary in the pure core. Defines boot, band selection, status readout, FM/DAB tuning, service list, playback, and volume without ESP-IDF types. \texttt{si4684::Si4684Tuner} implements it on device; host tests can use fakes. \section{TunerService}\label{cls:TunerService} Application service exposing intent-level tuner operations to HTTP and future UI. Holds a reference to \texttt{core::ITuner}, tracks last tune target and volume, and maps driver failures to \texttt{core::TunerError}. \section{Si4684Tuner}\label{cls:Si4684Tuner} \texttt{ITuner} adapter over \texttt{Si4684Driver}. Translates domain calls into SPI commands and maps \texttt{Si4684Error} to \texttt{TunerError}. Constructed once in \texttt{HardwareBootstrap} alongside the driver. \section{AudioService}\label{cls:AudioService} Application service for ADAU1701 mixer, EQ, and master volume (Chapter~\ref{ch:adau1701}, Section~\ref{sec:adau1701-service}). Holds a reference to \texttt{core::IDsp}, tracks the in-memory \texttt{AudioProfile}, loads from \texttt{IAudioProfileStore} after boot, and applies changes via safeload. Stereo depth and bass enhance levels are merged into the effective EQ via \texttt{core::applyEnhancementsToEq()}. Exposed on \texttt{/api/audio/*} and the web UI Audio section. \section{EnhanceLevel}\label{cls:EnhanceLevel} Strong type for 0--100 enhancement intensity (stereo depth and bass boost). Validated at the HTTP boundary by \texttt{core::parseEnhanceLevelJson()}. \section{NvsAudioProfileStore}\label{cls:NvsAudioProfileStore} \texttt{IAudioProfileStore} implementation storing serialised \texttt{AudioProfile} JSON in NVS namespace \texttt{digiradio}. \section{StationName}\label{cls:StationName} Strong type for a preset label (1--32 bytes). Parsed at the HTTP boundary via \texttt{StationName::tryFrom()}. \section{BroadcastLabel}\label{cls:BroadcastLabel} Trimmed on-air label from RDS or DAB chip byte buffers (up to 128 bytes). Built by \texttt{BroadcastLabel::tryFromChipBytes()} in the pure core. \section{RdsMetadataAccumulator}\label{cls:RdsMetadataAccumulator} Stateful RDS decoder assembling program service name and radiotext from raw block~A--D snapshots returned by the Si4684 driver. \section{DabDynamicLabelAccumulator}\label{cls:DabDynamicLabelAccumulator} Reassembles segmented DAB DLS payloads into one dynamic label for \texttt{TunerStatus}. \section{PresetSlot}\label{cls:PresetSlot} Optional hardware preset button index (1--20). Validated by \texttt{PresetSlot::tryFrom()}. \section{Station}\label{cls:Station} Immutable preset value: name, band (DAB/FM), tune coordinates, optional preset slot. FM presets carry \texttt{FrequencyKHz}; DAB presets carry ensemble index and optional service/component ids for playback. \section{StationList}\label{cls:StationList} Pure-domain ordered collection (max 20 entries) with add/remove/move and duplicate tune-target rejection. Persisted as JSON through \texttt{ISecureStore}. \section{StationService}\label{cls:StationService} Application service loading/saving presets from NVS and recalling them via \texttt{TunerService}. Exposed on \texttt{/api/stations/*} and the Presets web UI section. \section{IntegrationService}\label{cls:IntegrationService} End-to-end orchestration at boot and on preset recall: loads the station list, reapplies the saved \texttt{AudioProfile} after tune, and persists the last preset index (NVS key \texttt{last\_preset}). Used by \texttt{app\_main} and \texttt{POST /api/stations/tune}. \section{BluetoothService}\label{cls:BluetoothService} Application service for BT1035 pairing and A2DP status (Chapter~\ref{ch:bt1035}). Delegates to \texttt{Bt1035Driver}; tracks whether discoverable mode was requested. Exposed on \texttt{/api/bluetooth/*}.