metronome/pico
Me Here df6e70473c PM_K-1 firmware: smooth AA fonts, full set list, LED-off-on-stop, touch edge-detect
Four firmware improvements (web simulator already had these):
  - Smooth lettering: replace the upscaled 8x8 bitmap font (and the 7-seg BPM) with
    baked anti-aliased proportional fonts (DejaVuSans-Bold 22 + 78), rendered by
    blending fg over bg through a 16-entry alpha LUT. Generator: pico/gen_font.py.
  - All grooves: PROGRAMS now carries the full web set list (23 grooves), not 5.
  - Fix: the RGB LED stayed lit when stopped — the stop path now zeroes the colour
    and the fade only runs while playing (led_off()).
  - Fix: the on-screen play button played one beep then stopped — a finger held past
    the old lockout re-fired the toggle. Touch is now edge-detected like the hardware
    buttons (fires once on finger-down, ignores held).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:11:30 -05:00
..
__pycache__ PM_K-1 firmware: smooth AA fonts, full set list, LED-off-on-stop, touch edge-detect 2026-05-28 17:11:30 -05:00
gen_font.py PM_K-1 firmware: smooth AA fonts, full set list, LED-off-on-stop, touch edge-detect 2026-05-28 17:11:30 -05:00
main.py PM_K-1 firmware: smooth AA fonts, full set list, LED-off-on-stop, touch edge-detect 2026-05-28 17:11:30 -05:00
README.md PM_K-1 docs: make the two-step flash explicit (main.py is not drag-and-drop) 2026-05-28 16:42:18 -05:00

PM_K1 "Kit" — VARASYS PolyMeter firmware for the Raspberry Pi Pico

MicroPython firmware that turns a Raspberry Pi Pico on the 52Pi EP0172 "Pico Breadboard Kit Plus" into a touchscreen polymeter metronome. It runs the same program strings as https://metronome.varasys.io — design a groove in the web editor, copy its program string, paste it into PROGRAMS in main.py, and it plays here.

Everything is in one file: main.py (ST7796 display driver, GT911 touch, WS2812 RGB, buzzer, joystick, the polymeter engine — no external libraries).

The board (EP0172) — fixed pinout

Component Pico pins
3.5″ ST7796 320×480 display SPI0 — SCK GP2, MOSI GP3, CS GP5, DC GP6, RST GP7
GT911 capacitive touch I2C0 — SDA GP8, SCL GP9 (addr 0x5D)
WS2812 RGB LED GP12
Buzzer GP13
Button A / Button B GP15 / GP14
PSP joystick X = ADC0/GP26, Y = ADC1/GP27

The components are wired on the board — you don't breadboard anything; just seat the Pico.

Flash it — TWO separate steps

⚠️ main.py is NOT a drag-and-drop file. The RPI-RP2 drive that appears in BOOTSEL mode is the bootloader, and it only accepts a .uf2 firmware file — anything else (like main.py) is silently discarded on the next reboot. You first flash MicroPython with a .uf2 (drag-and-drop), and then copy main.py over the USB serial link with Thonny or mpremote. Two different steps.

Step 1 — install MicroPython (drag-and-drop a .uf2, one time)

  1. Download the MicroPython firmware .uf2 for your board:
  2. Hold BOOTSEL, plug into USB → the RPI-RP2 drive appears.
  3. Drag the .uf2 file onto that drive. It copies, the Pico reboots on its own, and the drive disappears — that's correct and means MicroPython is installed. (Don't use BOOTSEL again unless you're reinstalling the firmware.)

Step 2 — copy main.py (over USB serial, NOT to a drive)

After step 1 the Pico runs MicroPython and no longer shows up as a USB drive — so you can't drag files to it. Use a tool that talks to it over USB serial:

  • Thonny (easiest): install Thonny, plug the Pico in normally, then bottom-right click the interpreter selector → MicroPython (Raspberry Pi Pico) (you should see a >>> prompt in the Shell). Open main.py, then File ▸ Save as… ▸ Raspberry Pi Pico and save it as exactly main.py.
  • mpremote (command line): pip install mpremote then mpremote cp main.py :main.py

Reset (replug) and it boots straight into the metronome.

Controls

  • Touch: onscreen << / >|| / >> (prev · play/stop · next) and / TAP / +.
  • Joystick: up/down = tempo (push far for ±5), left/right = previous/next groove.
  • Button A (GP15): play / stop. Button B (GP14): tap tempo.
  • RGB LED flashes each beat (amber = accent, cyan = normal, violet = ghost); the buzzer clicks with matching pitch.

Add your own grooves

Edit the PROGRAMS list near the top of main.py — each entry is ("Name", "program string"). Get program strings from the web editor's program box (e.g. v1;t120;kick:4;snare:4=.X.X;hat:4/2). Supported: tempo t<bpm>, lanes sound:grouping[/sub][=pattern][~][!], pattern chars X accent · x normal · g ghost · . - _ rest, grouped meters like 3+3+2, polymeter ~. (Perlane dB gain @n is parsed but ignored — the buzzer is mono.)

If something looks off — calibration

All the knobs are flags in the CONFIG block at the top of main.py:

  • Colours look negative / washed out: toggle INVERT_COLORS.
  • Red and blue swapped: set SWAP_RB = True.
  • Taps land in the wrong place: set TOUCH_DEBUG = True, reset, watch the raw coordinates over the USB serial (Thonny shell) as you tap the corners, then set TOUCH_SWAP_XY / TOUCH_INVERT_X / TOUCH_INVERT_Y to match.
  • Joystick reversed: toggle JOY_INVERT_X / JOY_INVERT_Y; widen JOY_DEADZONE if it drifts.
  • Screen stays black: the backlight is hardwired on, so this usually means the SPI init didn't take — drop SPI_BAUD to 24_000_000 and retry.
  • Garbled / wrong size image: your panel lot may be a 240×320 ILI9341 instead of the 320×480 ST7796. This firmware targets the ST7796 you have (you said 320×480); if a unit ever ships ILI9341, set WIDTH,HEIGHT = 240,320 and use an ILI9341 init sequence.

Notes

  • Audio is a single passive buzzer, so coincident lane hits play one click at the highest priority (accent > normal > ghost); the RGB + screen still show the combined activity.
  • The scheduler is nonblocking and timed off time.ticks_us(), so tempo stays steady while the screen and inputs update.

Hardware reference: 52Pi EP0172 wiki · vendor code. VARASYS — Simplifying Complexity.