New form factor: a plain RP2040 Pico + Pico Scroll Pack (PIM545) -- a 17x7 single-colour LED matrix + 4 buttons. The 7x17 matrix maps onto the editor's lane x step pad grid. - pico-scroll/: CircuitPython firmware (DEVICE_ID "G"). Engine/scheduler/SysEx/ live-sync copied verbatim from pico-explorer (engine byte-identical, so it stays on the track-format conformance lineage); vendored bulk-framebuffer IS31FL3731 driver (pins/map verified from pimoroni-pico); three LED views (Grid/Pendulum/BPM); 4-button input. Audio over USB-MIDI (no onboard speaker); optional P_BUZZER. - grid.html + info-grid.html: widget page (canvas mirrors the 3 LED views) + spec page with a ~$29 BOM. - Registered in build.sh (precompile + ASCII assert + pm_g1_circuitpy.zip), deploy.sh, embed.js, embed.html, index.html gallery, and both editors' FW_PATHS (device id G). - docs/rust-port.md: core/driver architecture (pm-core no_std engine+protocol; per-board drivers behind embedded-hal/embedded-graphics traits). CLAUDE.md + livesync-protocol.md note the new edition + device id. Python firmware stays in parallel with Rust (no abandonment yet). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 KiB
PM_G-1 "Grid" — CircuitPython edition (Pimoroni Pico Scroll Pack · RP2040)
The CircuitPython firmware for the Pimoroni Pico Scroll Pack (PIM545)
on a plain Raspberry Pi Pico, set up as a self-contained appliance. Sibling to the PM_K-1 build
in ../pico-cp/ and PM_X-1 in ../pico-explorer/ — same engine, same program strings, same
programs.json, same web editor, same live-sync protocol.
This board is a 17 × 7 single-colour (white) LED matrix (IS31FL3731 over I²C) + 4 buttons (A/B/X/Y) — no touchscreen, no joystick, no RGB LED, and no onboard speaker. It's the smallest, most minimal form factor: the 7-row × 17-column grid is the editor's lane × step pad grid in miniature. Editing happens in the web editor with Live sync on; the device mirrors changes in real time and emits its own play/stop/bpm/sel deltas back.
Audio is over USB-MIDI — turn on the editor's 🎹 Device audio to hear the clicks through
your computer's speakers. The Scroll Pack has no buzzer; if you solder a piezo to a free GPIO, set
P_BUZZER near the top of app.py to that pin to get an on-device click.
Views (button B cycles)
| View | What the 17 × 7 matrix shows |
|---|---|
| Grid (default) | Each lane is a row (top 7), each step a column; brightness = accent (bright) / normal / ghost (dim). A bright playhead column tracks the beat. Bars with ≤ 17 steps are centred one-column-per-step; longer bars are scaled to fit (multiple steps may share a column — no steps are dropped). |
| Pendulum | A single column bounces left↔right across the bar like a metronome arm, with a full-height flash on each beat (accent = brightest). Glanceable from across a room. |
| BPM | The current tempo as three 3 × 5 digits. |
Controls
| Button | Tap | Hold |
|---|---|---|
| A | play / stop | ≥ 0.6 s: cycle view (Grid → Pendulum → BPM) |
| B | next track | ≥ 0.6 s: next set list |
| X | tempo − 1 | auto-repeat (after ~1.5 s the step grows to − 5) |
| Y | tempo + 1 | auto-repeat (after ~1.5 s the step grows to + 5) |
Tap tempo lives in the web editor; the built-in playlists (Styles / Practice / Song) are baked into
firmware and your own set lists arrive in /programs.json over USB-MIDI. The mapping is deliberately
simple (this is a UI prototype) — all four buttons and their pins are at the top of app.py, easy to
re-bind.
Pinout (verified against the Pimoroni pico_scroll library)
| Signal | Pico pin |
|---|---|
| Button A / B / X / Y | GP12 / GP13 / GP14 / GP15 |
| Matrix I²C SDA / SCL | GP4 / GP5 |
| IS31FL3731 address | 0x74 |
| (optional piezo) | any free GPIO → set P_BUZZER |
Flashing
- Hold BOOTSEL, plug the Pico in, and drag on CircuitPython for Raspberry Pi Pico (the same
10.2.x build the Kit uses). The drive remounts as
CIRCUITPY. - Download
/pm_g1_circuitpy.zipfrom https://metronome.varasys.io and unzip its contents ontoCIRCUITPY(code.py,boot.py,app.mpy,programs.json, fonts/icons,editor.html). - Reset. By default the firmware owns the drive (appliance mode); hold button A while plugging in to make the drive writable again (editor mode).
The application is the precompiled app.mpy (the ~25 KB app.py source is too big to compile
on-device without OOM). Firmware updates are one click from the editor (⋯ → Update firmware),
pushed over USB-MIDI as an A/B update with automatic rollback — the device reports id G so the
editor sends the Grid build. Unbrickable: BOOTSEL + drag a .uf2 always restores it.
Status
This is the CircuitPython prototype used to nail down the LED-grid UI on real hardware. The
production firmware target is the native-Rust engine (one pm-core + per-board drivers — see
docs/rust-port.md); the Python builds stay as the simple, no-toolchain option in parallel.