metronome/pico-cp
Me Here 7ccc75e399 Phase 3: USB-MIDI audio — play the device through the computer's speakers
Firmware (pico-cp/code.py): on every click, send a USB-MIDI note-on per firing lane —
GM drum note by voice (SOUND_GM), velocity by level (accent/normal/ghost) — via the
default-enabled usb_midi.ports[1]. Polyphonic, so the computer plays the full groove.
New CONFIG: MIDI_ENABLED (default on), MUTE_BUZZER (silence the buzzer when using
computer audio).

Editor (editor.html): a '🎹 Device audio' toggle uses the Web MIDI API
(requestMIDIAccess) to voice incoming notes through the existing synth — Note-On ->
GM_NUM[note] / velocity-to-gain -> playInstrument(). The device is the clock; the
browser is the sound module, locked in sync. Chrome/Edge.

Verified: firmware emits the right notes (kick+hat on beat 1 of four-on-the-floor,
snare's rest skipped); editor loads clean with the toggle + handlers present. Docs
(info-kit, both READMEs) updated. The on-device buzzer/screen still work standalone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:40:08 -05:00
..
__pycache__ Phase 3: USB-MIDI audio — play the device through the computer's speakers 2026-05-28 22:40:08 -05:00
code.py Phase 3: USB-MIDI audio — play the device through the computer's speakers 2026-05-28 22:40:08 -05:00
font_l.bin PM_K-1 CircuitPython: fix MemoryError + red/blue swap (from on-board test) 2026-05-28 21:30:03 -05:00
font_m.bin PM_K-1 CircuitPython: fix MemoryError + red/blue swap (from on-board test) 2026-05-28 21:30:03 -05:00
font_s.bin PM_K-1 CircuitPython: circle pad grid, small labels, dimmer LED, faster SPI 2026-05-28 22:07:00 -05:00
programs.json Phase 1: CircuitPython firmware (USB-drive edition) for the Kit 2026-05-28 21:10:34 -05:00
README.md Phase 3: USB-MIDI audio — play the device through the computer's speakers 2026-05-28 22:40:08 -05:00

PM_K1 "Kit" — CircuitPython edition (USB drive + editor)

The CircuitPython firmware for the 52Pi EP0172 Pico kit. Unlike the MicroPython version (../pico/main.py), this makes the Pico mount as a USB drive (CIRCUITPY) that carries the firmware and your tracks — so you can edit on the web and reprogram it without Thonny. It runs the same programstring language as https://metronome.varasys.io.

Status: experimental, phase 1. This drives the screen/touch/joystick/buzzer and reads your grooves from programs.json. The editor's oneclick "Save to device" and USBMIDI audiotocomputer are landing in later phases. The simpler MicroPython firmware (../pico/main.py) remains the rocksolid fallback — and the Pico can't be bricked (BOOTSEL → drag a MicroPython .uf2 back).

Install

  1. Flash CircuitPython: hold BOOTSEL, plug in, and drop the CircuitPython .uf2 for your board onto the RPIRP2 drive (https://circuitpython.org/board/raspberry_pi_pico/ — or the Pico 2 / W build). It reboots and a CIRCUITPY drive appears.
  2. Copy everything from the bundle onto CIRCUITPY (draganddrop — it's a normal drive now):
    • code.py (this firmware — runs on boot)
    • programs.json (your grooves)
    • font_s.bin, font_m.bin, font_l.bin (the antialiased fonts — kept as files to save RAM)
    • editor.html (an offline copy of the web editor, so the drive carries its own programmer)
  3. It starts immediately. Editing programs.json (or resaving it from the editor) makes CircuitPython autoreload with the new tracks.

Play through the computer's speakers (USB-MIDI)

The board also shows up as a USB-MIDI device and sends a note on every click (a GM drum note per lane, velocity by accent). Open the editor in Chrome/Edge, click 🎹 Device audio, grant MIDI access, then press play on the device — the editor voices the groove through its full synth, out your computer's speakers, locked to the device's clock. Set MUTE_BUZZER = True in code.py if you'd rather silence the on-board buzzer while doing this.

Controls (same as the MicroPython build)

  • Touch: onscreen ◀◀ / ▶ / ▶▶ (prev · play/stop · next) and / TAP / +.
  • Joystick: up/down = tempo, left/right = previous/next groove.
  • Button A (GP15) play/stop · Button B (GP14) tap tempo.
  • RGB LED flashes each beat; buzzer clicks (accent/normal/ghost).

programs.json

{ "title": "PolyMeter",
  "programs": [ { "name": "Four on the floor", "prog": "t120;kick:4;snare:4=.x.x;hatClosed:4/2" } ] }

Each prog is a program string from the web editor. Add/replace entries and save — the device reloads.

Easiest way to (re)program it: in the editor (the web app, or the editor.html on the drive), build a set list, then the setlist menu → 📟 Save to device → pick the CIRCUITPY drive. In Chrome/Edge it writes programs.json straight onto the drive (the Pico autoreloads); elsewhere it downloads the file to drag on. 📥 Load from device reads a programs.json back into a new set list.

Calibration (flip flags at the top of code.py)

  • Red/blue swapped: flip MADCTL between 0x48 (default) and 0x40.
  • Colours look negative: toggle INVERT_COLORS.
  • Taps land wrong: set TOUCH_DEBUG = True, watch the serial output, then set TOUCH_SWAP_XY / TOUCH_INVERT_X / TOUCH_INVERT_Y.
  • Joystick reversed: toggle JOY_INVERT_X / JOY_INVERT_Y.
  • Computer audio: MIDI_ENABLED (default on) sends the MIDI notes; MUTE_BUZZER silences the buzzer.
  • LED too bright / too dim: change LED_BRIGHTNESS (0..1, default 0.15).
  • Screen tearing: the SPI panel has no tearing-effect sync; SPI_BAUD (default 62.5 MHz) is pushed fast to minimise it — lower it only if the display is unstable.
  • Screen blank / garbled: the panel lot may differ; drop SPI_BAUD, and if it's a 240×320 ILI9341 instead of the 320×480 ST7796, the init/size need changing (this targets the 320×480 you have).
  • RGB LED is driven by the core neopixel_write module — no library to install. If it stays dark, your CircuitPython build is unusually missing that module (everything else still works).

If code.py ever errors, CircuitPython prints the traceback on the screen and over USB serial — copy that to me and I'll fix it.

The fonts are the same baked antialiased blobs as the MicroPython build (see ../pico/gen_font.py).