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). AnalogControlknob smoothing runs atAudioCallbackRate()— 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 viaProcessAnalogControls(). Fine for spec §8.- RGB LEDs are software-PWM'd:
Led::Init(..., samplerate)defaults to 1 kHz and dims viaUpdate()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 ifUpdateLeds()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/SimpleLedcompiles clean, ELF/hex/bin produced; FLASH usage well under 128 KB).make program-dfu— flashes the app over USB DFU to0x08000000(core/Makefile:343-344).make program-boot— flashes the Daisy bootloader binary (core/Makefile:346-347).- ⚠ There is no
program-apptarget in this libDaisy version (the build prompt assumed one). The equivalent flow isAPP_TYPE=BOOT_QSPIin the app Makefile, thenmake program-dfutargets 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
- §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+.)
make program-appdoesn't exist in libDaisy v5.4.0-22 — useAPP_TYPE=BOOT_QSPI+make program-dfuif a bootloader app is ever needed.- 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.
- 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.