# ADR-005: Embedded platform choices and C/C++ conventions

## Status
Accepted

## Context
The "from math to metal" pipeline requires embedded C/C++ implementations alongside the Python prototypes. Three topics already have `embedded.qmd` pages targeting STM32F4 and ESP32. We need to standardize on specific, purchasable hardware and establish conventions before scaling to more topics.

Key constraints:
- The STM32F4-Discovery board (used in the original DSBC practicum source material) is **discontinued** — students can no longer buy it new.
- ADR-002 establishes Python as the primary language. C/C++ is a companion for `embedded.qmd` pages, not a replacement.
- Target general-purpose MCUs, not dedicated DSP chips (per CLAUDE.md).

## Decision

### 1. Two-platform strategy

| Platform | Board | MCU | Clock | FPU | DSP library |
|----------|-------|-----|-------|-----|-------------|
| **ESP32-S3** | ESP32-S3-DevKitC | Xtensa LX7 dual-core | 240 MHz | Single-precision | ESP-DSP |
| **STM32F4** | NUCLEO-F446RE | Cortex-M4F | 180 MHz | Single-precision | CMSIS-DSP |

- **ESP32-S3** for accessible prototyping: cheap (~EUR 8), built-in WiFi/BT and I2S, ESP-DSP library with optimized FIR/FFT.
- **NUCLEO-F446RE** for professional embedded DSP: CMSIS-DSP (the industry-standard ARM DSP library), SIMD for Q15/Q31 fixed-point, deterministic real-time behavior.
- **RP2350 / Pico 2** (Cortex-M33, $5) is referenced in platform comparison tables as a budget alternative but does not get its own code sections.
- **Teensy, dedicated DSP chips (TI C6x, SHARC), FPGAs** are excluded.

Audio I/O: pair either board with INMP441 I2S MEMS mic (~EUR 2) and PCM5102/MAX98357A I2S DAC (~EUR 3).

#### 1a. Extended capability ladder (amended 2026-06-15)

The two-platform pair above remains the **default** for embedded pages. But where an algorithm's *portability and feasibility across capability tiers* is itself the lesson, a page may use a wider **capability ladder** instead, drawn from the HAN Embedded Machine Learning (EML) course board set (Arends and Veen, `gitlab.han.nl/aea/ese/eml`). The ladder spans, from bottom to top:

| Tier | Board | Core | Clock | FPU | RAM |
|------|-------|------|------:|-----|----:|
| 8-bit, no FPU | ATmega328P Xplained Mini | AVR | 16 MHz | none | 2 KB |
| M0+, no FPU | FRDM-KL25Z | Cortex-M0+ | 48 MHz | none | 16 KB |
| M4F | NUCLEO-F411RE | Cortex-M4F | 100 MHz | single-prec | 128 KB |
| M33 + NPU | FRDM-MCXN947 | Cortex-M33 ×2 + eIQ Neutron NPU | 150 MHz | single-prec | 512 KB |

Rationale: the same portable C runs on every rung, but memory and arithmetic differ by more than an order of magnitude, so "same algorithm, different feasibility" becomes a teachable result rather than an aside. First applied in `topics/gabor-filters/embedded.qmd` (2-D convolution). The shared-`lib/`, per-target-glue, multi-toolchain (PlatformIO / Keil / MCUXpresso / Microchip Studio) structure is also borrowed from the EML course. Conventions 2-6 below (float-first, write-your-own-then-library, inline code, negated IIR coefficients) still apply on whichever boards a page uses.

### 2. C/C++ is companion, not replacement

ADR-002 is **not superseded**. Python remains the primary language for theory, prototyping, and analysis. C/C++ code appears only in `embedded.qmd` pages within topic directories. Each `embedded.qmd` links back to its topic's `index.qmd` for the mathematical derivation and Python implementation.

### 3. Float first, fixed-point as optimization

All implementations use `float` (single-precision) by default. Fixed-point (Q15/Q31) is shown as an optimization section within the same page — not as a separate implementation. Both target MCUs have hardware single-precision FPU; fixed-point is relevant for power-constrained or FPU-less targets.

### 4. Write your own first, then benchmark against library

Each embedded page shows a hand-written C implementation first (teaches the algorithm), then the CMSIS-DSP or ESP-DSP equivalent, followed by a performance comparison. This mirrors the existing pattern in the biquad, adaptive-filtering, and matched-filtering pages.

### 5. Code inline, no shared directory

C/C++ code lives inline in `embedded.qmd` files as fenced code blocks. There is no shared `embedded/` source directory. Each code block is self-contained with `#include` statements, type definitions, and function signatures so a reader can paste it into a PlatformIO or CubeIDE project.

### 6. IIR coefficient convention

All IIR filter code uses **negated denominator coefficients** (CMSIS-DSP convention): `w[0] = x + a1*w[1]` where the stored `a1` is $-a_1$ from the transfer function. When porting from SciPy, negate `a1` and `a2`. This matches the existing biquad `embedded.qmd` and avoids a subtraction in the inner loop.

## Consequences
- **Positive**: Students can buy the recommended hardware today. Both boards are actively manufactured and widely available.
- **Positive**: Two platforms cover complementary use cases (IoT/prototyping vs. professional/industrial) without overwhelming contributors with three or more toolchains.
- **Positive**: Float-first approach means students write their first filter without learning Q-format notation.
- **Negative**: NUCLEO-F446RE lacks built-in audio hardware (unlike the old Discovery board's CS43L22 codec). External I2S breakouts add ~EUR 5 to the BOM.
- **Negative**: Excluding RP2350 code sections means Pico 2 users must adapt STM32 CMSIS-DSP code themselves (both are ARM Cortex-M, so this is straightforward).
