# PM_K‑1 "Kit" — CircuitPython edition (USB drive + editor) The **CircuitPython** firmware for the 52Pi EP‑0172 Pico kit. Unlike the MicroPython version (`../pico/main.py`), this makes the Pico mount as a **USB drive (`CIRCUITPY`)** that carries the firmware and your tracks — so you can edit on the web and reprogram it without Thonny. It runs the same program‑string language as . > **Status: experimental, phase 1.** This drives the screen/touch/joystick/buzzer and reads your > grooves from `programs.json`. The editor's one‑click "Save to device" and USB‑MIDI audio‑to‑computer > are landing in later phases. The simpler **MicroPython** firmware (`../pico/main.py`) remains the > rock‑solid fallback — and the Pico can't be bricked (BOOTSEL → drag a MicroPython `.uf2` back). ## Install 1. **Flash CircuitPython:** hold **BOOTSEL**, plug in, and drop the CircuitPython `.uf2` for your board onto the `RPI‑RP2` drive ( — or the Pico 2 / W build). It reboots and a **`CIRCUITPY`** drive appears. 2. **Copy everything from the bundle** onto `CIRCUITPY` (drag‑and‑drop — it's a normal drive now): - `code.py` (this firmware — runs on boot) - `programs.json` (your grooves) - `font_s.bin`, `font_m.bin`, `font_l.bin` (the anti‑aliased fonts — kept as files to save RAM) - `editor.html` (an offline copy of the web editor, so the drive carries its own programmer) 3. It starts immediately. Editing `programs.json` (or re‑saving it from the editor) makes CircuitPython **auto‑reload** with the new tracks. ## Play through the computer's speakers (USB-MIDI) The board also shows up as a **USB-MIDI** device and sends a note on every click (a GM drum note per lane, velocity by accent). Open the [editor](https://metronome.varasys.io/editor.html) in **Chrome/Edge**, click **🎹 Device audio**, grant MIDI access, then press play *on the device* — the editor voices the groove through its full synth, out your computer's speakers, locked to the device's clock. The button shows the connected device's name and pulses green on each note; set `MUTE_BUZZER = True` in `code.py` if you'd rather silence the on-board buzzer while doing this. If the editor says **no MIDI input is connected**, copy **`boot.py`** onto `CIRCUITPY` too and **power-cycle** the Pico (`boot.py` only runs on a full reset). It frees a USB endpoint (drops the unused HID interface) so the MIDI port is guaranteed to appear alongside the drive. ## Protect the firmware (so end users only see the editor + their tracks) To stop someone accidentally deleting the firmware, **hide it** — the files keep running and "Save to device" still works, but only `editor.html` + `programs.json` show in the file browser. On the host, with the drive mounted, run the included helper (needs `fatattr`): ``` ./protect-firmware.sh /media/$USER/CIRCUITPY # hides code.py, boot.py, font_*.bin, README, itself ``` (Reveal again with `fatattr -h `.) For a **hard lock** — nothing on the drive can be changed from the computer at all — put `storage.remount("/", readonly=True)` in `boot.py`; but then the editor's *Save to device* can't write either, so you'd reprogram by temporarily removing that line (or gating it behind a held button at power-on). Hiding is usually the right balance. ## Controls (same as the MicroPython build) - **Touch:** on‑screen `◀◀ / ▶ / ▶▶` (prev · play/stop · next) and `− / TAP / +`. - **Joystick:** up/down = tempo, left/right = previous/next groove. - **Button A (GP15)** play/stop · **Button B (GP14)** tap tempo. - **RGB LED** flashes each beat; **buzzer** clicks (accent/normal/ghost). ## 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 web editor. Add/replace entries and save — the device reloads. **Easiest way to (re)program it:** in the editor (the web app, or the `editor.html` on the drive), build a set list, then the set‑list **⋯** menu → **📟 Save to device** → pick the `CIRCUITPY` drive. In Chrome/Edge it writes `programs.json` straight onto the drive (the Pico auto‑reloads); elsewhere it downloads the file to drag on. **📥 Load from device** reads a `programs.json` back into a new set list. ## Calibration (flip 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`, watch the serial output, 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) sends the MIDI notes; `MUTE_BUZZER` silences the buzzer. - **LED too bright / too dim:** change `LED_BRIGHTNESS` (0..1, default 0.15). - **Screen tearing:** the SPI panel has no tearing-effect sync; `SPI_BAUD` (default 62.5 MHz) is pushed fast to minimise it — lower it only if the display is unstable. - **Screen blank / garbled:** the panel lot may differ; drop `SPI_BAUD`, and if it's a 240×320 ILI9341 instead of the 320×480 ST7796, the init/size need changing (this targets the 320×480 you have). - **RGB LED** is driven by the core `neopixel_write` module — no library to install. If it stays dark, your CircuitPython build is unusually missing that module (everything else still works). If `code.py` ever errors, CircuitPython prints the traceback **on the screen and over USB serial** — copy that to me and I'll fix it. The fonts are the same baked anti‑aliased blobs as the MicroPython build (see `../pico/gen_font.py`).