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>
24 lines
1.3 KiB
Python
24 lines
1.3 KiB
Python
# code.py - PM_G-1 A/B firmware loader (stable; rarely changes).
|
|
#
|
|
# The real application is the PRECOMPILED app.mpy (CircuitPython compiles a big .py at boot, which
|
|
# fragments the heap and OOMs; a .mpy loads without compiling). app.bak holds the previous known-good
|
|
# build. The web editor pushes a new app.mpy to a "trial" slot over USB-MIDI; this loader runs it, and
|
|
# if it fails to boot it AUTOMATICALLY ROLLS BACK to app.bak. (Unbrickable: BOOTSEL -> drag a .uf2.)
|
|
# app.mpy clears the /trial marker once it has run healthily for ~5s.
|
|
import supervisor, os
|
|
supervisor.runtime.autoreload = False # updates reboot explicitly; never auto-restart on our own writes
|
|
|
|
def _trial():
|
|
try: os.stat("/trial"); return True
|
|
except OSError: return False
|
|
|
|
try:
|
|
import app # runs the application (app.mpy; ends with App().run())
|
|
except Exception:
|
|
if _trial(): # a freshly-pushed build crashed on startup -> roll back
|
|
try:
|
|
os.remove("/app.mpy"); os.rename("/app.bak", "/app.mpy"); os.remove("/trial")
|
|
except Exception: pass
|
|
supervisor.reload() # reboot into the restored known-good build
|
|
else:
|
|
raise # the active build failed unexpectedly (rare) -> on-screen traceback
|