metronome/pico-cp/README.md
Me Here 7d743c18a1 PM_K-1: appliance model — push-programming over USB-MIDI, on-device practice log, swing fix
Firmware (pico-cp/): the Pico now owns its filesystem by default (boot.py), so it can save the
practice log and write editor-pushed set lists; the drive is read-only to the computer, which also
protects the firmware. Hold button A at power-on for editor mode (drive writable; universal drag).
  - Replaced the on-screen touch buttons with an on-device PRACTICE LOG (time · BPM · duration ·
    track), newest-first, persisted to /history.json next to programs.json. Plays < 5s aren't logged;
    tap a row twice to delete it. Real timestamps once the editor syncs the clock.
  - USB-MIDI SysEx receiver: clock-set (0x01 -> RTC) and program-push (0x10 -> write programs.json,
    reload, ACK/NAK). disable autoreload so our own writes never self-restart.
  - Fixed swing: the parser was discarding the 's' flag, so /2s never swung. Now the scheduler uses a
    per-step duration with long-short (2:1, SWING_RATIO 2/3) pairs on even subdivisions, matching the
    web engine. Verified: ride:4/2s -> 266/133ms vs straight 200/200.

Editor (editor.html): requestMIDIAccess({sysex:true}); Save to device now pushes programs.json as
SysEx to the device (+ clock sync), waits for ACK, shows "Saved ✓", and falls back to downloading the
file (drag onto the drive in editor mode) when no device answers. Heartbeat also keeps the clock synced.
Web MIDI works in Chromium AND Firefox; the drag fallback covers any browser/OS incl. Safari.

Docs (pico-cp/README, info-kit, README) updated for the two modes, push programming, and the log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 00:38:08 -05:00

83 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PM_K1 "Kit" — CircuitPython edition (USB drive · push programming · MIDI audio · practice log)
The **CircuitPython** firmware for the 52Pi EP0172 Pico kit, set up as a selfcontained appliance.
It runs the same programstring language as <https://metronome.varasys.io>. The simpler
**MicroPython** firmware (`../pico/main.py`) stays as a rocksolid 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 + antialiased text, plays the
buzzer + RGB beat light, **logs your practice to `/history.json`**, accepts new set lists **pushed
from the web editor over USBMIDI**, and plays through your **computer's speakers** over USBMIDI.
## Two poweron 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 USBMIDI. The drive is then
**readonly 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
1. **Flash CircuitPython:** hold **BOOTSEL**, plug in, drop the CircuitPython `.uf2` onto `RPIRP2`
(<https://circuitpython.org/board/raspberry_pi_pico/> — Pico 2 / W builds also fine). A `CIRCUITPY`
drive appears.
2. **Copy the whole bundle** onto `CIRCUITPY`: `boot.py`, `code.py`, `programs.json`,
`font_s.bin` / `font_m.bin` / `font_l.bin`, `editor.html` (offline editor), and the helper scripts.
3. **Powercycle** (so `boot.py` takes effect). It boots into appliance mode and runs.
## Program it from the web (push over USBMIDI)
In the editor (Chrome / Edge / **Firefox**), build a set list → setlist **⋯** menu → **📟 Save to device**.
The editor sends it to the Pico over USBMIDI (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.
## Play through the computer's speakers
The Pico is a USBMIDI 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 automutes** (the computer plays instead).
The editor also syncs the device clock, so the practice log gets real wallclock 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 to `programs.json`) in appliance mode and survives powercycles.
## programs.json
```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 `MADCTL` between `0x48` (default) and `0x40`.
- **Colours look negative:** toggle `INVERT_COLORS`.
- **Taps land wrong:** set `TOUCH_DEBUG = True`, read the raw coords over USB serial, 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); `MUTE_BUZZER` forces the buzzer off even standalone.
- **LED too bright/dim:** `LED_BRIGHTNESS` (0..1, default 0.15).
- **Screen tearing:** SPI panels have no tearingeffect 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 antialiased blobs from `../pico/gen_font.py`. `protect-firmware.sh` (hide
the firmware files) is mainly for editor mode — appliance mode already keeps the drive readonly.