Device screen redesign (CircuitPython app.py), built proportional to WIDTH/HEIGHT so it scales to other panels (one adaptive firmware, per-panel config — not a fork): - gen_assets.py bakes logo.bin (VARASYS wordmark, no tagline), midi.bin (DIN-5), usb.bin (trident) as 4-bit-alpha bitmaps (same packing as the fonts). - Header: VARASYS logo (brand cyan) replaces the "PM_K-1 KIT" text; MIDI icon goes green when a host is listening, USB icon lights when supervisor.runtime.usb_connected. load_alpha/make_glyph are non-fatal — a missing .bin falls back to text, never a black screen (addresses the corrupt-file failure mode we just hit). - Pad grid: filled squares on main beats, hollow outline squares (outer+inner rect) on off-beats; playhead fills the lit pad. Vertical gridlines at the master lane's beats (full height) so beats line up across lanes. - Stopwatch (m:ss) + bar counter (master-lane cycles), refreshed ~4x/s only on change. The .bin assets ship in the drive bundle (the A/B updater only pushes app.py), so a one-time re-copy is needed to pick them up. APP_VERSION -> 0.0.2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.3 KiB
PM_K‑1 "Kit" — CircuitPython edition (USB drive · push programming · MIDI audio · practice log)
The CircuitPython firmware for the 52Pi EP‑0172 Pico kit, set up as a self‑contained appliance.
It runs the same program‑string language as https://metronome.varasys.io. The simpler
MicroPython firmware (../pico/main.py) stays as a rock‑solid fallback — and the Pico can't be
bricked (BOOTSEL → drag a MicroPython .uf2 back).
What it does: drives the 3.5″ touchscreen with a lanes/pads display + anti‑aliased text, plays the
buzzer + RGB beat light, logs your practice to /history.json, accepts new set lists pushed
from the web editor over USB‑MIDI, and plays through your computer's speakers over USB‑MIDI.
Two power‑on modes (set by boot.py)
- Appliance mode — default (just plug in / power up). The firmware owns the filesystem, so it saves your practice log and writes set lists the editor pushes over USB‑MIDI. The drive is then read‑only to the computer — which also protects the firmware from accidental deletion.
- Editor mode — hold BUTTON A while plugging in. The drive is writable by the computer, so you
can drag
programs.json/code.py/ fonts on from any OS or browser (the universal fallback). Reset afterwards to return to appliance mode.
Install
- Flash CircuitPython: hold BOOTSEL, plug in, drop the CircuitPython
.uf2ontoRPI‑RP2(https://circuitpython.org/board/raspberry_pi_pico/ — Pico 2 / W builds also fine). ACIRCUITPYdrive appears. - Copy the whole bundle onto
CIRCUITPY:boot.py,code.py(loader) +app.py(the application),programs.json,font_s.bin/font_m.bin/font_l.bin,logo.bin/midi.bin/usb.bin(the on-screen logo + MIDI/USB status icons),editor.html(offline editor), and the helper scripts. (code.pyis a tiny stable loader;app.pyis what firmware updates replace. The.binassets — like the fonts — ride in the bundle, since the one-click updater only pushesapp.py; if a.binis missing the firmware just falls back to text and never fails to boot.) - Power‑cycle (so
boot.pytakes effect). It boots into appliance mode and runs.
Program it from the web (push over USB‑MIDI)
In the editor (Chrome / Edge / Firefox), build a set list → set‑list ⋯ menu → 📟 Save to device.
The editor sends it to the Pico over USB‑MIDI (SysEx); the firmware writes /programs.json, reloads, and
acknowledges — the editor shows Saved ✓. 📥 Load from device reads it back.
Universal fallback (any browser / OS, even Safari): Save to device downloads programs.json when no
device answers — boot the Pico in editor mode (hold A) and drag the file onto the CIRCUITPY drive.
Firmware updates (one‑click, A/B with auto‑rollback)
code.py is a small stable loader; the application is app.py (it carries APP_VERSION). To update:
the editor's ⋯ menu → ⬆ Update firmware… queries the device's version, fetches the latest from the site,
shows device vs latest, and on confirm pushes the new app.py over USB‑MIDI. The device installs it to
a trial slot (keeping the old build as app.bak) and reboots; if the new build doesn't boot, the loader
automatically rolls back to app.bak. A build that runs cleanly for ~5 s is confirmed. No BOOTSEL, no
dragging. (Updating CircuitPython itself still uses BOOTSEL + a .uf2, but that's rare. And the Pico is
unbrickable as the ultimate backstop.)
Play through the computer's speakers
The Pico is a USB‑MIDI device and sends a note per click (GM drum note per lane, velocity by accent). In the editor click 🎹 Device audio, grant MIDI access, and press play on the device — the editor voices the groove through its full synth, out your speakers, locked to the device's clock. While a host is listening the screen shows a green MIDI badge and the buzzer auto‑mutes (the computer plays instead). The editor also syncs the device clock, so the practice log gets real wall‑clock timestamps.
Controls & the practice log
- Joystick: up/down = tempo, left/right = previous/next groove.
- Button A (GP15): play / stop. Button B (GP14): tap tempo.
- Touchscreen: the bottom of the screen shows the practice log (time · BPM · duration · track) — newest first. Plays under 5 s aren't logged. Tap a row to arm it (turns amber), tap again to delete.
- RGB LED flashes the beat (amber accent / cyan normal / violet ghost); the buzzer clicks to match.
- The log is saved to
/history.json(next toprograms.json) in appliance mode and survives power‑cycles.
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 editor (tempo, lanes, patterns, /2 subdivision, /2s swing,
(3,8) Euclid, ~ polymeter, @-3 dB). The push above is the easy way to update it.
Calibration (flags at the top of code.py)
- Red/blue swapped: flip
MADCTLbetween0x48(default) and0x40. - Colours look negative: toggle
INVERT_COLORS. - Taps land wrong: set
TOUCH_DEBUG = True, read the raw coords over USB serial, then setTOUCH_SWAP_XY/TOUCH_INVERT_X/TOUCH_INVERT_Y. - Joystick reversed: toggle
JOY_INVERT_X/JOY_INVERT_Y. - Computer audio:
MIDI_ENABLED(default on);MUTE_BUZZERforces the buzzer off even standalone. - LED too bright/dim:
LED_BRIGHTNESS(0..1, default 0.15). - Screen tearing: SPI panels have no tearing‑effect sync;
SPI_BAUD(default 62.5 MHz) is pushed fast to minimise it — lower only if unstable. - Blank / garbled: the panel lot may differ; drop
SPI_BAUD, and if it's a 240×320 ILI9341 rather than the 320×480 ST7796, the init/size need changing (this targets the 320×480 you have). - RGB LED uses the core
neopixel_write(no library to install).
If code.py ever errors, CircuitPython prints the traceback on the screen and over USB serial — send
me that. The fonts are the baked anti‑aliased blobs from ../pico/gen_font.py. protect-firmware.sh (hide
the firmware files) is mainly for editor mode — appliance mode already keeps the drive read‑only.