Decouple the SigmaStudio RAM download from compiled-in adau1701_program.c: DRAD v1 blobs in the dsp partition with embedded fallback, POST /api/dsp/program, and host-tested blob parse/serialize helpers. Co-authored-by: Cursor <cursoragent@cursor.com>
326 lines
16 KiB
TeX
326 lines
16 KiB
TeX
\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, CompanionChipStatus, serialNumber)}
|
||
in the pure core; JSON serialisation lives in \texttt{serializeHealthStatusJson()}.
|
||
The \texttt{serialNumber} field carries the EEPROM-derived serial or
|
||
\texttt{unknown} when the EUI-48 read fails. No ESP-IDF headers.
|
||
|
||
\section{Eui48}\label{cls:Eui48}
|
||
Strong type for the factory-programmed six-byte EUI-48 read from the
|
||
24AA025E48 at word address \texttt{0xFA} (Microchip DS20001191). Exposes
|
||
\texttt{serialNumber()} (12 uppercase hex digits) and \texttt{shortSuffix()}
|
||
(last three bytes) for SSID and hostname derivation.
|
||
|
||
\section{DeviceIdentity}\label{cls:DeviceIdentity}
|
||
Per-board identity derived from \texttt{Eui48}: SoftAP SSID
|
||
\texttt{DigiRadio-<suffix>}, Bluetooth name, STA hostname
|
||
\texttt{digiradio-<suffix>}, and canonical serial. \texttt{unknown()} yields
|
||
documented fallbacks (\texttt{DigiRadio-setup}, serial \texttt{unknown}).
|
||
|
||
\section{IDeviceIdentitySource}\label{cls:IDeviceIdentitySource}
|
||
Port for reading \texttt{DeviceIdentity} from board storage. Implemented in
|
||
the shell by \texttt{eeprom24aa::Eeprom24aa} on the shared I2C bus.
|
||
|
||
% ------------------------------------------------------------------
|
||
% Network shell (Slice 1)
|
||
% ------------------------------------------------------------------
|
||
|
||
\section{SoftApConfig}\label{cls:SoftApConfig}
|
||
Immutable value type holding SoftAP parameters (SSID, channel, station
|
||
limit). \texttt{forSsid()} builds setup parameters from
|
||
\texttt{DeviceIdentity::softApSsid()}; \texttt{setupDefault()} keeps the
|
||
\texttt{DigiRadio-setup} fallback when the EEPROM read fails.
|
||
|
||
\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). Exposes \texttt{i2cBusHandle()} after boot for the shared
|
||
24AA025E48 EEPROM. Runtime mixer, EQ, and master volume updates use the
|
||
ADAU1701 safeload mechanism via \texttt{applyProfile()} and related methods
|
||
(Section~\ref{sec:adau1701-driver}).
|
||
|
||
\section{Eeprom24aa}\label{cls:Eeprom24aa}
|
||
I\textsuperscript{2}C shell driver for the 24AA025E48 identity EEPROM
|
||
(Chapter~\ref{ch:hardware}, address \texttt{0x52}). Implements
|
||
\texttt{IDeviceIdentitySource}: reads six bytes at word address \texttt{0xFA}
|
||
per Microchip DS20001191 and builds \texttt{DeviceIdentity}. Called from
|
||
\texttt{HardwareBootstrap} after \texttt{Adau1701Driver::boot()}.
|
||
|
||
\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}).
|
||
|
||
\section{RegisterWrite}\label{cls:RegisterWrite}
|
||
One SigmaStudio \texttt{SIGMA\_WRITE\_REGISTER\_BLOCK} step: 16-bit address
|
||
plus payload bytes. Aggregated by \texttt{DspProgram}.
|
||
|
||
\section{DspProgram}\label{cls:DspProgram}
|
||
Ordered ADAU1701 RAM download script (pure domain). Parsed from a framed
|
||
\texttt{DRAD} flash blob or built by \texttt{EmbeddedDspProgramSource}.
|
||
|
||
\section{IDspProgramSource}\label{cls:IDspProgramSource}
|
||
Port supplying \texttt{DspProgram} to \texttt{Adau1701Driver::boot()}.
|
||
Implementations: flash partition, embedded export, and fallback composite.
|
||
|
||
\section{EmbeddedDspProgramSource}\label{cls:EmbeddedDspProgramSource}
|
||
Builds the factory \texttt{DspProgram} from \texttt{DigiRadio\_IC\_1.h}
|
||
(five writes matching \texttt{default\_download\_IC\_1()}).
|
||
|
||
\section{FlashDspProgramSource}\label{cls:FlashDspProgramSource}
|
||
Reads and parses the \texttt{dsp} flash partition; \texttt{storeBlob()} erases
|
||
and writes a validated blob from \texttt{POST /api/dsp/program}.
|
||
|
||
\section{FallbackDspProgramSource}\label{cls:FallbackDspProgramSource}
|
||
Tries \texttt{FlashDspProgramSource} first, then
|
||
\texttt{EmbeddedDspProgramSource} when the partition is empty or invalid.
|
||
|
||
% ------------------------------------------------------------------
|
||
% 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/*}.
|