metronome/rust/pm-daisy
Me Here d80c35984e pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware)
Develop the full Daisy Pod spike so it can be flashed the moment the board
arrives. Architecture: one shared engine, two front-ends.

- pm-synth: make it `#![no_std]` (mirroring track-format), routing float math
  through `libm` so the SAME f32 code runs on the host and on the Daisy's
  Cortex-M7F (hardware FPU — no fixed-point port needed). Add `Player`, a
  self-running sequencer that owns the Synth + scheduled clicks and renders
  sample-by-sample, looping at the pattern boundary. Integer-only hot path
  (clicks pre-resolved to sample indices); exposes a `fired()` beat counter.
  Add SPIKE_PROGRAM/SPIKE_BARS as the shared source of truth.

- synthrender: render the SAME Player to pm-daisy-preview.wav — the host-side
  "simulator". Bit-identical preview of the hardware output (before its codec);
  far more useful than chip emulation (Renode can't model the audio codec).

- pm-daisy (new, workspace-excluded firmware): thin BSP binary for the Daisy
  Seed/Pod. embedded-alloc heap + board bring-up + SAI-DMA audio interrupt
  feeding Player::next_sample() into stereo frames, USER LED flashing per click.
  Audio loop follows the `daisy` crate's examples/audio.rs. Board revision
  (codec) is a Cargo feature; README documents matching it + both flash paths
  (probe-rs/RTT and USB DFU) + the QSPI-bootloader fallback.

Verified without hardware: host build + preview render (48 kHz, onsets on the
8th-note grid at 124 BPM); firmware cross-compiles + links for thumbv7em-none-
eabihf at ~87 KB (fits the 128 KB internal flash) across all three codec
revisions; track-format conformance + `node tests/run.mjs` (47 pass) still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 11:41:10 -05:00
..
.cargo pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
src pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
.gitignore pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
build.rs pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
build.sh pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
Cargo.toml pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
memory.x pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
README.md pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00

pm-daisy — PolyMeter click engine on the Daisy Pod

A time-boxed spike (see docs/daisy-spike.md): play the PolyMeter groove engine on real Cortex-M7 hardware to judge whether the Electrosmith Daisy Pod (STM32H750, onboard audio codec) is a credible home for the audio engine if PolyMeter ever grows into a real-time audio workstation.

It boots playing a hardcoded 124-BPM 909 pattern (pm_synth::SPIKE_PROGRAM) out the Pod's audio jack, flashing the Daisy Seed's USER LED on each click.

What runs here

  • Shared engine, verified on host. The audio is produced by pm_synth::Player — the exact same code the host synthrender renders to pm-daisy-preview.wav. Listen to that WAV to hear what the hardware should play before you flash anything.
  • Transport only is new. This crate is a thin board-support binary: heap + board bring-up + the SAI audio-DMA interrupt feeding Player::next_sample() into stereo frames. Structure follows the daisy crate's examples/audio.rs.

⚠️ Set the board revision (or you get silence)

The Daisy Seed comes in revisions with different audio codecs. Pick the one on your Seed (check the sticker / silkscreen) — it's a Cargo feature:

Your Seed Codec Build with
Daisy Seed (original) AK4556 ./build.sh seed
Daisy Seed 1.1 (default) WM8731 ./build.sh (or seed_1_1)
Daisy Seed 1.2 / Seed2 DFM PCM3060 ./build.sh seed_1_2

Build

./build.sh [revision]     # containerized (pm-rust:2); produces pm-daisy.bin + pm-daisy.elf

Flash — two options

A. Debug probe (recommended — gives you defmt logs over RTT): A probe wired to the Seed's SWD pins (e.g. the Raspberry Pi Debug Probe you already use for pm-grid/pm-kit — see rust/probe-flash.md).

probe-rs run --chip STM32H750VBTx pm-daisy.elf    # or `cargo run --release` from this dir

B. USB DFU (no probe needed): Hold the Daisy's BOOT button, tap RESET, release BOOT — the Seed enumerates as STM32 system DFU. Then:

dfu-util -a 0 -s 0x08000000:leave -D pm-daisy.bin -d ,0483:df11

Too big for 128 KB?

The STM32H750 has only 128 KB internal flash. If the linker reports a FLASH overflow, flash via the Daisy Bootloader to the 8 MB QSPI instead:

  1. Install the bootloader once at https://flash.daisy.audio/ (Bootloader tab, v6.x).
  2. In memory.x, set FLASH : ORIGIN = 0x90040000, LENGTH = 8M - 0x40000 (commented there).
  3. Rebuild, then enter the bootloader (tap BOOT within 2 s of reset; LED pulses) and:
    dfu-util -a 0 -s 0x90040000:leave -D pm-daisy.bin -d ,0483:df11
    

Beyond the spike (Pod extras, not yet wired)

The Pod adds 2 buttons, 2 knobs, an encoder, and 2 RGB LEDs on Seed GPIO/ADC pins. Obvious next steps once audio is confirmed: knob → tempo, button → start/stop, RGB LED → beat/downbeat color. Wiring those needs the Pod pin map from Electrosmith's pinout (verify before assigning pins). This spike deliberately uses only the always-present Seed USER LED to avoid guessing the Pod pinout.