APP_VERSION -> 0.0.6. Device firmware + editor change in lockstep — one-time manual
copy of 0.0.6 needed (the broken single-shot updater can't deliver it).
Update transport (fixes the failed/bricking updates):
- Editor now pushes app.py in 512-byte flow-controlled chunks: begin(0x21,len) ->
data(0x22)* -> commit(0x23), waiting for each ACK before the next. A single ~38KB
SysEx overran the device's USB-MIDI input buffer and arrived corrupt.
- Device writes chunks straight to /app.new, and on commit verifies length + no NUL +
App().run()/APP_VERSION present before the A/B install; rejects (NAK) otherwise and
keeps the working build. All errors caught -> never bricks.
Run/stop indicator moved off the screen onto the RGB LED (per feedback that recoloring
the whole background is wrong — it forces a full-screen SPI repaint and fringes the
anti-aliased text):
- Dim GREEN when stopped ("on"), dim RED while playing; the beat pulse flashes brighter
and now decays back to the running base instead of to black. Background is static black.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: a non-ASCII em-dash in an app.py comment. The A/B updater pushes app.py
as 7-bit SysEx (charCode & 0x7F), which turned the em-dash's bytes into a NUL byte ->
corrupt source -> the pushed build crashed on boot (black screen, onboard LED blinking
CircuitPython's error/safe-mode pattern). A dragged copy was fine (valid UTF-8); only
the over-MIDI path mangled it.
- Replace the em-dash with ASCII; app.py is now pure ASCII.
- build.sh now ASSERTS pico-cp/app.py is pure ASCII (fails the build otherwise) so this
class of bug can never ship again.
- Device 0x20 handler VALIDATES the pushed app.py before installing (reject if it
contains a NUL byte, or is missing App().run()/APP_VERSION) and now catches ALL
exceptions (not just OSError) -> a corrupt/truncated/oversized push NAKs and keeps the
working build instead of bricking. Longer pre-reload sleep so the ACK flushes.
APP_VERSION -> 0.0.5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App.py-only (ships over the one-click updater). APP_VERSION -> 0.0.3.
- Run/stop is now a background tint (gray running / near-black stopped) instead of
STOP text, reclaiming the space.
- Running time + bar counter show "of total" when the track has a b<n> length:
"1:23 of 2:00" and "bar N of 16" (bar cycles 1..N); total time derived from
bars x master-beats-per-bar x 60/bpm. Parser now reads the b<n> token.
- Practice log is filtered to the current track (drops the redundant track column).
- Pads: squares for the main pulse, circles for subdivisions (was square + hollow
outline); fewer vectorio shapes too.
- Track number set apart from the title (small + dim, right) so it no longer reads
as part of the title.
On-device editing (tap instrument -> lane table; tap beat -> cycle state; dirty-name
-> confirm save/revert) is deferred to Phase 2, where "save" has a correct destination
(an edited built-in saves as a user copy).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Split the CircuitPython firmware into a tiny stable loader (code.py) + the application (app.py,
carries APP_VERSION). The editor's ⋯ → "⬆ Update firmware" queries the device version (SysEx 0x02
-> 0x03 reply), fetches the latest app from the site (/pico-cp-app.py), shows device-vs-latest, and
pushes the new app.py over USB-MIDI (SysEx 0x20). The device installs it to a trial slot (old build
kept as app.bak), reboots, and the loader AUTO-ROLLS-BACK to app.bak if the new build fails to start;
a build that runs cleanly ~5s is confirmed (clears /trial). No BOOTSEL, no dragging; Chromium/Firefox.
app.py forced to pure ASCII so it pushes raw (no base64); SysEx buffer raised to 60KB.
build.sh/deploy.sh: bundle code.py+app.py and serve /pico-cp-app.py. Docs updated.
Verified in CPython: version reply, update install+reboot+ACK, rollback file dance; editor loads clean
with the updater wired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>