metronome/pico-explorer
Me Here da7c94e67f Implement per-track playback flow (rep / end / relative goto)
Adds the per-track end-action model designed in docs/track-format.md §3, end to
end across both engines, both firmwares, and the editors.

Grammar (parsed + serialized by engine.js and both app.py):
  rep=<n>     cycles before the end-action fires (default 1)
  end=stop    stop after rep cycles
  end=next    advance one track (sugar for end=+1)
  end=<±N>    relative goto after rep cycles (e.g. end=-2 = D.S.)
  (absent)    loop forever — the metronome default

Firmware runtime (pico-cp + pico-explorer): _on_new_bar now consults a per-track
_end_plan() and fires stop / gapless-advance / relative-goto at the right bar.
A cycle = b<bars>, else one master bar; fire bar = rep * cycle. Explicit end=
governs; with no end, the global Continue toggle stays a default (=end=next, still
needs b<bars>) so existing set-lists and the CONT UI are unchanged. _prepare_next
takes a target index; the seam machinery, _do_advance and live-sync all carry rep/end.

Editors (editor.html + editor-beta.html): state.rep/state.end thread through
applySetup / currentSetup / currentPatch so load -> edit -> save preserves the
flow; authoring is via the program-string field (no graphical control yet).

Tests: the 3 playback-flow vectors now pass on both engines (39 pass / 3 known).
Runtime decision logic (_end_plan / _goto_target) unit-tested for stop, rep,
relative goto clamp/wrap, and legacy-Continue precedence. Codec round-trip
verified idempotent. Both firmwares compile + mpy-cross clean.

Also: untrack stale __pycache__/*.pyc build artifacts and gitignore them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:37:06 -05:00
..
app.py Implement per-track playback flow (rep / end / relative goto) 2026-05-31 00:37:06 -05:00
boot.py PM_X-1 0.0.1: Pimoroni Explorer sibling firmware + Kit 0.0.23 device-id reply 2026-05-30 20:43:38 -05:00
code.py PM_X-1 0.0.1: Pimoroni Explorer sibling firmware + Kit 0.0.23 device-id reply 2026-05-30 20:43:38 -05:00
programs.json PM_X-1 0.0.1: Pimoroni Explorer sibling firmware + Kit 0.0.23 device-id reply 2026-05-30 20:43:38 -05:00
README.md PM_X-1 0.0.2: portrait flip + Kit-style layout (no more overlapping bits) 2026-05-30 21:37:12 -05:00

PM_X-1 "Explorer" — CircuitPython edition (Pimoroni Explorer · RP2350)

The CircuitPython firmware for the Pimoroni Explorer Kit (PIM744), set up as a self-contained appliance. Sibling to the PM_K-1 build in ../pico-cp/ (the 52Pi EP-0172 kit) — same engine, same program strings, same programs.json, same web editor.

This board is a 2.8″ ST7789V LCD + 6 user buttons + piezo speaker built around an RP2350B (Pico 2 class chip). No touchscreen, no joystick, no RGB LED. Editing is done 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.

Hold the device in portrait with the A/B/C buttons along the top and X/Y/Z along the bottom. The firmware drives the LCD as a 240 × 320 portrait at display.rotation = 270 — same UI shape as the PM_K-1 Kit, just shorter. If the screen comes up upside-down on your unit, change DISPLAY_ROTATION near the top of app.py to 90 (or 180 if rotated 180°) and re-flash.

Controls

Button Action
A play / stop
B tap tempo
C menu (Settings / Help / About / Practice log)
X prev track (hold to repeat)
Y tempo 1 (hold to repeat; after ~1.5 s the step grows to 5)
Z next track (hold to repeat)
X + Z tempo +1 (chord; same hold-repeat as Y)

In a menu: X / Z move the cursor up / down, Y decrements the focused value, A commits or cycles, B = back, C = close.

Install

  1. Flash CircuitPython for Pico 2 / RP2350. Hold BOOTSEL on the Explorer, plug it in over USB-C, drop the Pimoroni Explorer (RP2350) CircuitPython .uf2 onto the RP2350 drive. A CIRCUITPY drive appears.
  2. Copy the bundle onto CIRCUITPYboot.py, code.py, app.mpy, programs.json, font_s.bin / font_m.bin / font_l.bin, logo.bin / midi.bin / usb.bin, editor.html (offline editor). If an old app.py is on the drive, delete it.
  3. Power-cycle. It boots into appliance mode and runs.

Program it from the web

Open https://metronome.varasys.io in Chrome / Edge / Firefox. The set-list menu → 📟 Save to device pushes a programs.json over USB-MIDI; the device persists it and reloads. Click 🔗 Live sync to mirror edits in real time.

Pin reference

The display, buttons, and audio are wired into the board — no jumpers required. CircuitPython's official board definition for pimoroni_explorer2350 exposes board.DISPLAY pre-initialized, so the firmware just uses it.

Function GPIO
Button A GP16
Button B GP15
Button C GP14
Button X GP17
Button Y GP18
Button Z GP19
Piezo audio (PWM) GP12
Piezo amp enable GP13
I²C SDA (QwSTEMMA) GP20
I²C SCL (QwSTEMMA) GP21
Display board.DISPLAY (8080 parallel bus on GP26..GP39, initialized by board.c)

Calibration (flags at the top of app.py)

  • Speaker too loud / quiet: the piezo + amp gain is fixed in hardware. MUTE_SPEAKER silences the click; SPEAKER_AUTO_MUTE auto-mutes when a MIDI host is listening.
  • Buttons feel inverted: the polarity is hard-coded to active-low (pull-up). If a button fires on release instead of press, check the BTN_* pin map at the top of app.py.
  • Display orientation: the board's CircuitPython init mounts the panel landscape (320 × 240). If your screen looks rotated, that's a board.c-level thing — file a CircuitPython bug, don't patch the firmware.

If app.py ever errors, CircuitPython prints the traceback on the screen and over USB serial — send me that.