AMEN AUDIO
AMEN AUDIO / MANUALDownload Markdown ↓

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, https://daisy.nyc3.cdn.digitaloceanspaces.com/products/pod/ES_Daisy_Pod_Rev5.pdf) 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<LOGGER_INTERNAL> 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.