# NEUROPOD — T0 Hardware & Toolchain Audit Date: 2026-08-06 Hardware: Electrosmith Daisy Pod **Rev5** (Seed onboard), connected via USB during audit. Rev identified by the physical microSD slot (Gustav, visual confirmation) — Rev4 and earlier have no slot. libDaisy: `85172e2b` (v5.4.0-22-g85172e2b, vendored submodule) DaisySP: `a0494a3` (V1.0.0, vendored submodule) All API claims below are cited against the vendored submodule sources, not memory. Hardware claims are cited against the **Pod Rev5 schematic** (`ES_Daisy_Pod_Rev5.pdf`, 2022-10-27, ) and the libDaisy Pod BSP (`libDaisy/src/daisy_pod.cpp`). Note: the older Pod databrief v1.0 (2021) describes Rev4 and lists no SD slot — it is stale for this board; an early draft of this audit was wrong because of it. --- ## 1. microSD / SDMMC — **AVAILABLE on Pod Rev5. Spec §4.7 / T9 / T10 stand as written.** **Finding.** The Pod Rev5 schematic (sheet 1/2, "microSD" block) shows an SMT horizontal microSD slot wired for **full 4-bit SDMMC1**: | SD slot pin | Net | Seed pin | STM32 pin | |---|---|---|---| | DAT0 | SD_D0 | GPIO4 / D4 | PC8 | | DAT1 | SD_D1 | GPIO3 / D3 | PC9 | | DAT2 | SD_D2 | GPIO2 / D2 | PC10 | | CD/DAT3 | SD_D3 | GPIO1 / D1 | PC11 | | CMD | SD_CMD | GPIO5 / D5 | PD2 | | CLK | SD_CK | GPIO6 / D6 | PC12 | with 47 kΩ pull-ups to +3V3_D on the data/CMD lines (R1–R4, R16). These are **exactly** libDaisy's fixed SDMMC1 pins (`per/sdmmc.h:20-28`, `daisy_seed.h:198-203`), so `SdmmcHandler` with `BusWidth::BITS_4` + `Speed::FAST` (50 MHz) and the FatFS/IDMA path work as the spec assumes. At 4-bit 50 MHz, loading 31 MB of foley is comfortably inside the T9 "<5 s" gate (sequential read ≥ ~10 MB/s real-world). **No re-plan needed.** One residual caveat: the SDMMC pins double as Seed GPIO D1–D6, which the Pod BSP doesn't otherwise use — no conflict with any Pod control. QSPI (8 MB, app region 7936 KB @ `0x90040000`, `core/STM32H750IB_qspi.lds:20`) remains reserved for the spec §9 preset bank; internal flash is 128 KB (`STM32H750IB_qspi.lds:11`) so firmware size must be watched from T1 (fallback: `APP_TYPE=BOOT_QSPI`). SDRAM is 64 MB as spec'd (`STM32H750IB_qspi.lds:19`) — the 32-slot × 10 s ≈ 31 MB foley budget fits with headroom. --- ## 2. MIDI and USB CDC telemetry **TRS MIDI in only — confirmed at schematic level.** Rev5 sheet 1/2 "MIDI" block: one TRS input jack (J1) through an input protection/level circuit into `USART1_RX`; there is no MIDI output circuit anywhere on the schematic. Tip/ring polarity is selected by 0R resistors — MIDI_IN_A populated, MIDI_IN_B DNI (R78/R79 vs R83/R84), i.e. **Type A default**, switchable to Type B by moving the 0R links. Consequence stands: **no device→host telemetry over MIDI.** **UART details** (`hid/midi.cpp:10-17`): default transport is USART1, RX = PB7 = Seed `D14`, TX = PB6, 31250 baud, DMA RX into a 256-byte buffer in `DMA_BUFFER_MEM_SECTION` (`hid/midi.h:41-56`). The Pod BSP inits this in `DaisyPod::InitMidi()` (`daisy_pod.cpp:248-252`). ⚠ **Pin-sharing quirk to verify at first flash:** encoder click = `D13` = **PB6** (`daisy_pod.cpp:17`), which is also the default MIDI **TX** pin, and `MidiUartTransport::Init` hard-codes `Mode::TX_RX` (`hid/midi.h:70`), claiming PB6 as USART1_TX after the encoder was initialized (`DaisyPod::Init` order, `daisy_pod.cpp:81-85`). The Rev5 schematic confirms this is a hardware-level share: `ENC_CLICK` is wired to Seed GPIO13, whose alternate function is `USART1_TX` (sheet 1/2, Seed symbol pin 14). GPIO input reads (IDR) still work in AF mode and all shipped Pod examples live with this, but NEUROPOD should either init MIDI RX-only or re-init the encoder click pin after MIDI init. **RESOLVED at T1 (2026-08-06):** `main.cpp` re-inits the encoder after `DaisyPod::Init()` to reclaim PB6; verified on hardware — telemetry `enc` counter registered 7 physical clicks with USB MIDI and UART MIDI both active. **USB CDC — confirmed working concurrently with an app, on this very unit.** `daisy::Logger` provides USB CDC printing (`hid/logger.h:63-80`). During this audit the connected Pod (running its current firmware) enumerated as "Daisy Seed Built In", PID `0x5740` (STM VCP) — live proof that runtime CDC enumeration works on this hardware. DFU remains available at boot via the STM system bootloader (BOOT button) independent of what the app does at runtime; additionally `System::ResetToBootloader()` (`sys/system.h:158-168`) lets the app jump to DFU from software, so the harness can reflash without physical button presses once NEUROPOD ships that hook (recommended: CDC command or CC-triggered). **USB MIDI (added per Gustav's direction, spec v1.2 changelog #1).** libDaisy v5.4.0-22 ships `MidiUsbHandler` (`hid/midi.h:250`, `hid/usb_midi.h`), a class-compliant USB-MIDI device: `usbd_mode = USBD_MODE_MIDI` (`hid/usb_midi.cpp:92`) makes the patched CDC middleware serve MIDI descriptors (`Middlewares/.../usbd_cdc.c:788,813,838`). CoreMIDI will see the Pod directly — the host rig needs **no TRS MIDI interface**. Two independent USB device peripherals exist: - `FS_INTERNAL` = OTG_FS = the **Seed's onboard microUSB** (the DFU/flash port); - `FS_EXTERNAL` = OTG_HS-as-FS on PB14/PB15 = Seed D29/D30 (`daisy_seed.h:226-227`, `hid/usb.cpp:62-82`), which the Pod Rev5 schematic wires to the **Pod's own microUSB port**. **Hard constraint: CDC and USB-MIDI cannot coexist**, even split across the two ports — `usbd_mode` is one global consumed by the shared class driver, so both ports serve the same descriptor set. Verified unchanged in upstream **v8.1.0** (`usb_midi.cpp:117` still sets the same global), so upgrading doesn't lift the constraint. **Verdict (spec v1.2):** USB MIDI on `FS_INTERNAL` — one cable does DFU (boot), MIDI in (runtime), and telemetry out (runtime, as 7-bit-packed SysEx `F0 7D 4E 50 … F7` at 10 Hz behind `NP_DEV_TELEMETRY`). `MidiUsbTransport::Tx` provides the device→host direction. Plain CDC logging remains available behind `NP_TELEMETRY_CDC` for bring-up builds that don't init USB MIDI. To verify on hardware in T1: SysEx Tx path (`MidiToUsb` packetization of arbitrary-length SysEx) and USB-MIDI clock jitter (USB FS 1 ms framing vs. 14.5 ms tick interval at 172 BPM — expected fine; T7 measures it). --- ## 3. Controls, LEDs, ADC — Pod BSP reality Pin map (all from `daisy_pod.cpp:12-27`, Rev3/Rev4 pinout): | Control | Seed pin | |---|---| | Button 1 / 2 | D27 / D28 | | Encoder A / B / click | D26 / D25 / D13 (⚠ PB6, see §2) | | Knob 1 / 2 | D21 / D15 (ADC) | | LED1 R/G/B | D20 / D19 / D18 | | LED2 R/G/B | D17 / D24 / D23 | - ADC: 2 channels, single conversion each, **32× hardware oversampling** (`daisy_pod.cpp:233-239`). - `AnalogControl` knob smoothing runs at `AudioCallbackRate()` — at 48 kHz / block 16 that is 3 kHz (`daisy_pod.cpp:95-101`, update rate reset on every block-size change). Knob reads from the main loop via `ProcessAnalogControls()`. Fine for spec §8. - RGB LEDs are **software-PWM'd**: `Led::Init(..., samplerate)` defaults to 1 kHz and dims via `Update()` duty cycling (`hid/led.h:31-33`); the BSP comment "on/off for now" (`daisy_pod.cpp:222`) refers to color presets, not a hardware limit. Page colors and the dim-amber clock state in spec §1 are achievable if `UpdateLeds()` is called regularly from the main loop. --- ## 4. Audio codec and I/O **True stereo line IN and OUT — confirmed at schematic level.** Rev5 sheet 1/2: J2 = stereo LINE IN → `AUDIO_IN_L/R`, J3 = stereo LINE OUT ← `AUDIO_OUT_L/R`, J4 = headphone. Sheet 2/2: the headphone path is an analog tap of AUDIO_OUT_L/R through a dual-gang volume pot into a **TPA6110** headphone amp at gain 1.5 V/V — no third DAC channel involved, so OUT L/R measurements are unaffected by the headphone volume. Spec §1's IN L (EXT resample) and headphone monitor are real. **Codec is board-revision-dependent, detected at runtime** (`daisy_seed.cpp:282-334`, `CheckBoardVersion()` at `:356-377`): | Seed rev | Codec | |---|---| | Daisy Seed | AK4556 (reset-pin init, no I2C) | | Daisy Seed 1.1 | WM8731 (I2C-configured) | | Daisy Seed 2 DFM / 1.2 | PCM3060 | Which rev sits on Gustav's Pod is not determinable from software-at-rest; the `NP_DEV_TELEMETRY` boot line should report `BoardVersion` so `docs/CAL.md` records it. **Consequence for the harness:** absolute dBFS↔voltage mapping and input gain structure differ per codec (none of them expose a runtime input-gain knob through the Pod BSP — line level, fixed gain). This is exactly why `nphost cal` measures the mapping empirically with a loopback tone rather than trusting a datasheet number. No spec conflict. **Sample rate:** SAI configured for nominal 48 kHz (`SaiHandle::Config::SampleRate`, set via `SetAudioSampleRate`, `daisy_pod.cpp:139-143`). The `SAMPLE_RATE 48014.f` constant at `daisy_pod.cpp:5` is legacy dead code — actual rate comes from the SAI clock tree. Real-world SAI vs. host-interface clock offset will be a few ppm–tens of ppm; `nphost cal` should measure the device's actual tone frequency error once so FFT-bin-exact assertions (T4 alias floor) can compensate. Block size: BSP defaults to 48 (`daisy_pod.cpp:86`); NEUROPOD sets 16 per spec §1 via `SetAudioBlockSize(16)` — supported API (`daisy_pod.h:82`). --- ## 5. Toolchain | Tool | Version / source | |---|---| | `arm-none-eabi-gcc` | **10.3.1** (GNU Arm Embedded Toolchain 10.3-2021.10), `/usr/local/bin` | | `dfu-util` | **0.11**, Homebrew `/opt/homebrew/bin` | | `make` | GNU make, `/usr/bin/make` (Xcode CLT) | | libDaisy | submodule `85172e2b` (v5.4.0-22), prebuilt `build/libdaisy.a` present | | DaisySP | submodule `a0494a3` (V1.0.0), prebuilt `build/libdaisysp.a` present | **Build/flash commands (actual, verified against `libDaisy/core/Makefile`):** - `make` — builds an example (verified: `pod/SimpleLed` compiles clean, ELF/hex/bin produced; FLASH usage well under 128 KB). - `make program-dfu` — flashes the app over USB DFU to `0x08000000` (`core/Makefile:343-344`). - `make program-boot` — flashes the Daisy bootloader binary (`core/Makefile:346-347`). - ⚠ **There is no `program-app` target in this libDaisy version** (the build prompt assumed one). The equivalent flow is `APP_TYPE=BOOT_QSPI` in the app Makefile, then `make program-dfu` targets the QSPI app region through the Daisy bootloader (`core/Makefile:215-240`). Not needed unless we outgrow 128 KB internal flash or choose foley Option A with a QSPI app. **Flash status:** blinky **built and flashed** ✅. `pod/SimpleLed` (68 856 B) downloaded to `0x08000000` via `make program-dfu`, "File downloaded successfully". The trailing `dfu-util: Error during download get_status` (exit 74) on the leave request is the known-benign H750 quirk — the device resets and re-enumerates before answering the final status poll. The harness's future auto-flash step must treat exit 74 + "File downloaded successfully" as success. **Version currency (checked 2026-08-06 via GitHub releases):** | Component | Installed / pinned | Latest upstream | Action | |---|---|---|---| | DaisyToolchain (gcc) | 0.2.0 (`/Library/DaisyToolchain/0.2.0`, gcc 10.3.1) | v0.3.3 (2022-05) | Optional minor bump; not required — 10.3.1 builds everything here | | libDaisy | `85172e2b` (v5.4.0-22) | **v8.1.0** (2026-02) | **Stay pinned.** Three major versions ahead with API breaks; every existing sketch in this repo builds against v5.4.0-22, and NEUROPOD codes against the vendored headers per contract. Revisit only if a needed fix (MIDI parser / USB) is missing from v5.4 | | DaisySP | `a0494a3` (V1.0.0) | V1.0.0 | Current | | dfu-util | 0.11 (Homebrew) | 0.11 | Current | **Repo remote:** `origin` = `github.com/mokafari/DaisySeed-DSP-Playground` (Gustav's fork of DaisyExamples for personal sketches), `main` in sync with `origin/main` at audit time. NEUROPOD lives at `NEUROPOD/` inside this repo; stage commits land here. --- ## 6. Summary — spec vs. reality 1. **§4.7 / T9 / T10 (SD-card foley): VIABLE as written.** Pod Rev5 has a microSD slot on full 4-bit SDMMC1 with the exact fixed pins libDaisy requires (§1 above). No re-plan. (Rev4 and earlier Pods lack the slot — this firmware is Rev5+.) 2. **`make program-app` doesn't exist** in libDaisy v5.4.0-22 — use `APP_TYPE=BOOT_QSPI` + `make program-dfu` if a bootloader app is ever needed. 3. **Encoder-click / MIDI-TX pin sharing (PB6)** — confirmed on the Rev5 schematic; NEUROPOD must init MIDI RX-only or re-init the click pin, and T1 must verify the click on hardware. 4. Everything else in spec §1 checks out: TRS MIDI in (Type A, 0R-switchable), stereo line in/out, headphone monitor (TPA6110, post-line-out — doesn't affect measurements), 2 knobs / 2 buttons / encoder / 2 RGB LEDs, 64 MB SDRAM, 48 kHz / block 16 supported, USB CDC telemetry viable alongside boot-time DFU.