Commit graph

290 commits

Author SHA1 Message Date
Me Here
f564b75b1e COORDINATION: solo now; all prior-agent work verified + committed 2026-06-02 13:47:20 -05:00
Me Here
5dcef691c1 Add untracked notation deliverables (build/compile depend on these)
- src/notation.js — web notation engine inlined into pm_e-2.html (@BUILD:include)
- rust/pm-ui/src/notation/ — the notation module pm-ui/lib.rs imports
- rust/glyphgen/ + rust/assets/bravura/ (Bravura.otf + OFL.txt) — host atlas generator + font src
- rust/Cargo.toml (workspace) + rust/.gitignore
- assets/bravura.woff2.b64 (web font subset, @BUILD:bravura@) + info-pm_e-2.html

Without these a clean checkout couldn't build pm_e-2.html or compile pm-ui. (Left hardware/eda
make_svg* + kicad/_svgtest.json untracked — unrelated scratch.)
2026-06-02 13:46:45 -05:00
Me Here
cb54b4d689 Preserve notation + grammar feature work (verified complete + green)
The parallel agent's full session, committed now that it's solo:
- Grammar: flam/drag/roll ornaments (f/F d/D z/Z, per-lane orns channel) across
  src/engine.js, pico-cp/pico-explorer/pico-scroll app.py, pico/main.py, rust/track-format,
  + golden vectors / conformance (tests/, rust/track-format/tests).
- Live-sync deep-sync: SysEx 0x44 SLSYNC + 0x45 LOGSYNC (docs/livesync-protocol.md, src/livesync.js).
- PM_E-2 notation: web engine (pm_e-2.html, build/deploy/index/embed wiring) + Rust device port
  (pm-ui draw_notation rewrite + LaneView.groups, pm-kit ViewMode, uisim notesim).

Verified: node tests/run.mjs 47 pass / 1 known; ./rust/run.sh green; pm-kit firmware + uisim compile.
2026-06-02 13:45:26 -05:00
Me Here
49a4308c4b pm_e-2: logo-in-device top-left + header/footer fill (match editor.html) 2026-06-02 13:45:26 -05:00
Me Here
e9adbc5f02 COORDINATION: note pm_e-2 MIDI privacy fix 2026-06-02 08:31:43 -05:00
Me Here
46397627e4 editors: never auto-prompt for Web MIDI on load (privacy)
editor.html + pm_e-2.html called _ensureMidi() (requestMIDIAccess{sysex:true}, which always
prompts) on page load. Gate it behind a permission query — only auto-reconnect if MIDI is
already granted (querying does not prompt); otherwise wait for the user to click the connect
badge / Device-audio button. (editor-beta.html already had no on-load MIDI call.)
2026-06-02 08:31:43 -05:00
Me Here
390c974a5f editor: move VARASYS logo into the device top-left (out of the site header)
Add the brand lockup to the device .appheader (before the PM_E-1 title) and hide it in
.site-head, leaving the header with just nav (pushed right). Device logo sized to 30px.
Applies to editor.html + editor-beta.html.
2026-06-02 08:25:59 -05:00
Me Here
8049ab8d61 COORDINATION: GO for Agent-A Phase 4; pm-kit released + reverted to clean tile build; editor fill note 2026-06-02 07:54:43 -05:00
Me Here
336d1b43bb editor: fill the screen + align header to editor width
Editor #app and .device were capped (1400/1000px) and the shared header at 980px, so on
wide screens the logo sat inset from the editor's left edge and content floated narrow in a
wide page. Drop the caps (fill to the 24px body padding) and widen .site-head/.site-foot to
match — logo now lines up with the editor's left edge and the editor uses the full width.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 07:52:23 -05:00
Me Here
1eca3ee0fe pm-kit: small-tile incremental updates (sub-rect blit) to cut tearing
Validated against GeeekPi's lv_port_disp.c (vendor LVGL driver for this kit): it flushes
per-dirty-rectangle windowed writes at 62.5MHz, no TE — exactly this approach. Fix blit_rect
(1024-byte buffer; the 512-byte one was the black-screen bug), switch incremental updates from
full-width 144-row bands to changed 40x40 tiles. Full repaints still use the proven full blit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:52:07 -05:00
Me Here
eef535f9ef DESIGN: route LCD TE pin on production face for tear-free updates
ST7796S datasheet §10.8: panel rescans GRAM at 60Hz; tear-free writes require feeding
the TE output back to the MCU and writing during vblank. EP-0172 prototype wires no TE,
so large updates tear (small writes hide it — why MP/CP look clean). Add TE (e.g. GP4)
+ optional MISO to the face interconnect for the production board.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:04:42 -05:00
Me Here
bd3629ba4a pm-kit: working display — ported ST7796 driver + double-buffer + dirty row-bands
Replace mipidsi with a direct port of pico/main.py's ST7796 driver (per-command CS,
MADCTL 0x48, INVON, no offset, big-endian RGB565) — fixes the geometry/colour mangling.
Render the UI into a 300KB RAM framebuffer (embedded-graphics), then push only the
full-width row bands that changed vs the last frame (FNV row-diff) — flicker-free and
~144/480 rows per playhead step. Full-width windows only: the panel's MX bit mirror-maps
sub-width windows wrongly (that was the black screen). SPI 62.5 MHz per pico-cp to shrink
the tearing window (no TE pin is routed on this board, so hardware vsync isn't available).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:13:32 -05:00
Me Here
33dc5ff5cb PM_K-1 Phase 1: bench prototype firmware + doc (Pico 2 + WM8960, transformer-isolated XLR)
Per the approved audio re-architecture (prototype-first): prove the click -> codec ->
transformer-isolated XLR chain on bought boards before any custom PCB, keeping the RP2350
firmware.

- pico-wm8960/code.py: CircuitPython bring-up for Pico 2 + SparkFun WM8960 breakout.
  Synthesizes the click (familiar piezo pitches) -> I2S -> WM8960 -> HP/speaker; line-in
  monitor hook; stereo/pan ready for polymeter spatialization. Uses the proven adafruit_wm8960
  driver (no hand-rolled register driver).
- hardware/PROTOTYPE.md: shopping list, wiring, and bench milestones M1-M5 (M4 = the no-buzz
  ground-loop test = acceptance gate).

Key findings baked in:
- Buzz was a ground loop; cure = transformer galvanic isolation, NOT +/-15 V (which was only
  studio headroom and is dropped).
- WM8960 needs MCLK (CircuitPython I2SOut doesn't emit it); the SparkFun breakout's onboard
  24 MHz oscillator supplies it -> resolves Risk R1 with zero extra parts.

Track-format conformance (node tests/run.mjs) stays green; DSL untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:15:26 -05:00
Me Here
af79fe6f7f rust: probe-flash.md — flash+defmt via Pi Debug Probe in a Silverblue toolbox
Verified workflow (not first-principles): host udev rules with uaccess ACL (maps by UID
into the toolbox; group access shows nobody:nobody), prebuilt probe-rs in the toolbox,
probe-rs run --chip RP235x pm-kit.elf for flash+RTT defmt streaming.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:49:02 -05:00
Me Here
8f4264f4d2 pm-kit: defmt+probe-rs diagnostics + flip-link toolchain
Adopt proper embedded tooling for the blank-screen debug (user has a Pi Debug Probe):
- flip-link linker (baked into pm-rust:2): stack overflow faults cleanly instead of
  silently corrupting .bss/.data (the SPI buffer -> black screen class of bug).
- defmt + defmt-rtt + panic-probe: firmware logs boot/heap-free/display/parse/loop
  heartbeat over RTT; panics print message+location. .cargo runner = probe-rs run.
- Restore the full live metronome (from 08b0940) as the instrumented target.
- deploy + serve pm-kit.elf (probe-rs decodes defmt strings from the ELF).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:30:35 -05:00
Me Here
7faca6d0d7 pm-kit: isolation step 2 — heap init only (16KB), no parse, draw_ui
If blue → the 96KB heap memory was colliding (stack/buffer). If still black → the
allocator's presence itself. Narrowing the heap/display interaction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:16:46 -05:00
Me Here
9e2e833485 pm-kit: minimal isolation (heap + parse + draw_ui, no inputs/audio)
Diagnosing the blank screen: strips everything but the heap init, one allocator
exercise (parse), and the confirmed-working draw_ui. Blue+corners => heap/parse/
display fine, bug is the metronome loop. Black => heap/parse breaks the display.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:08:35 -05:00
Me Here
aeda999526 pm-kit: diagnostic — draw confirmed-working pattern to isolate blank screen
Temporary: draw_ui (blue + corners) instead of draw_metronome in the loop, keeping
heap+audio+inputs. Blue+corners shown => display/heap fine, draw_metronome is the
bug. Still black => heap/display path. Revert after diagnosis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:02:03 -05:00
Me Here
08b0940d73 pm-kit: redraw periodically (~7fps) so the metronome reliably appears
Blank-screen fix attempt: the single boot-time draw (right after the init DISPON)
was likely lost; the peripheral test worked because it redrew every frame. Redraw
on change AND every ~140ms. (Audio timing checked between redraws; partial/playhead
redraw is the next step to tighten it.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 07:54:23 -05:00
Me Here
95b960e071 docs: Stage 3 milestone 3 (live metronome) + notation views
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:20:43 -05:00
Me Here
8067820d19 site: Rust section now describes the live metronome (was bring-up image)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:19:38 -05:00
Me Here
0c788b1153 pm-kit: live metronome — real tracks, clock, audio clicks, controls, view toggle
The firmware is now an actual metronome (not a static screen):
- embedded-alloc heap → parses tracks with track-format on-device.
- 4 built-in grooves; clock-driven from the Timer; audio clicks on the master
  lane's hits via the GP13 PWM (accent louder/longer), short edge-triggered pulses.
- Controls: A = play/stop, B = grid/notation view; joystick (rotated 90° CCW)
  up/down = tempo +/-, left/right = prev/next groove.
- Renders draw_metronome or draw_notation; a cheap draw_progress strip animates the
  bar position every frame (full redraw only on change → no flicker).
- Robust: all input reads use unwrap_or (no panics in the loop) — addresses the
  self-test crash (likely an ADC unwrap on WouldBlock) and the continuous-buzzer.

Compile + simulator verified (grid renders all 4 grooves incl. triplets/polymeter).
NEEDS ON-DEVICE CHECK: audio timing, joystick directions, and that the crash is gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:18:28 -05:00
Me Here
ec29fb7284 pm-ui: notation refinement — shared stems + ledger lines
Gather notes per time-column across lanes; draw one shared stem per voice (hands
up / feet down) spanning the chord, so snare+hat on a beat share an up-stem. Ledger
lines for notes above/below the staff (hi-hat on its ledger line, crash higher).
Stems always clear the highest/lowest notehead; beams grouped per beat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:12:21 -05:00
Me Here
93617e1a91 pm-ui: drum notation view (first pass)
draw_notation() renders a bar as standard drum notation: 5-line staff + time
signature, voices mapped to staff positions and notehead types (oval drums,
cross hi-hat/cymbals), hands stem-up / feet stem-down, beamed eighths/sixteenths
grouped per beat, accents tinted. Developed entirely in the simulator
(uisim --bin notesim → PNG). Firmware build unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:59:57 -05:00
Me Here
04350f9d09 pm-kit: peripheral self-test — buttons + joystick (ADC) + speaker (PWM)
Honest answer to 'do the inputs/speaker work?': they had NO Rust code. Add the
drivers and a live self-test: buttons GP15/GP14 (pull-up), joystick GP26/GP27 via
ADC, speaker GP13 via PWM (~2 kHz click on button press). draw_peripheral_test
(pm-ui) shows button states, joystick dot + X/Y values, and beep activity; layout
verified in the simulator (uisim --bin periphsim) before flashing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:56:13 -05:00
Me Here
0ea442d68d pm-kit: draw the real metronome screen on the panel (static sample data)
Switch the firmware from the bring-up diagnostic to pm_ui::draw_metronome with
static borrowed lane data (no allocator yet). Shows the actual metronome UI on the
device; live track + moving playhead come when pm-core is linked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:48:50 -05:00
Me Here
676d9879fa pm-ui: first real metronome screen (header/BPM/transport + polymeter lane grid)
draw_metronome() renders the screen for any parsed track: track name + big BPM,
play/stop transport, and the polymeter lane grid — per-lane beat cells coloured by
level (accent amber / normal cyan / ghost purple / rest dark), playhead highlight,
beat gridlines, poly (~) marker. Pure no_std view over borrowed data (LaneView/
Screen) so the firmware build stays allocator-free.

uisim now parses a real track (track-format) and renders draw_metronome to PNG —
iterate the UI on the host, no bench. Firmware still draws the bring-up diagnostic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:38:48 -05:00
Me Here
d7b393b7c2 docs: Stage 3 — display milestone confirmed on hardware (ST7796 + CS-low fix)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:33:30 -05:00
Me Here
b154ccf493 pm-kit: hold CS low — fix ST7796 1/4 screen (mipidsi toggles CS mid-command)
Reading mipidsi's interface/spi.rs: send_command writes the command byte and its
parameters as TWO separate SpiDevice transactions, so a normal SpiDevice de-asserts
CS between them. The ST7796 needs CS continuous across command+parameters, so
MADCTL/COLMOD/B6 args never loaded → default scan/orientation → 1/4 + rotated
(parameter-less commands and the pixel stream still worked, which is why it lit up).

CircuitPython's FourWire holds CS low for the whole command; replicate that: drive
the real CS (GP5) low for the session and give ExclusiveDevice a no-op CS. DC alone
selects command vs data.

Diagnosed entirely on the host: panelsim (new) decodes mipidsi's actual command/
pixel stream into a PNG and rendered perfectly, proving the protocol was right and
the bug was in the physical SPI/CS layer — then the driver source confirmed it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:01:04 -05:00
Me Here
026c20523d pm-kit: full st7796 init as PRIMARY bring-up, then mipidsi for drawing
Host initdump proved mipidsi's MADCTL (0x48), COLMOD, and address window
(CASET 0..319 / RASET 0..479) already match CircuitPython exactly — so the 1/4
+ rotation wasn't an addressing bug. The missing piece was the ST7796 extension
init (B6/power/gamma) running as the PRIMARY bring-up right after reset (grafting
it onto mipidsi's already-DISPON'd panel blanked or under-configured it).

Now: manual hw reset + full CircuitPython st7796_init via the raw interface, THEN
Builder without reset_pin (re-asserts only the basics, extension setup persists).
initdump extended to also dump CASET/RASET draw windows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 22:50:51 -05:00
Me Here
b2ea27f506 pm-kit: minimal 0xB6 (no unlock) bracketed by DISP off/on for line count
The 0xF0 extension unlock (gates gamma/power) was the likely blanker; 0xB6 is a
basic command and needs no unlock. Strip to just DISPOFF -> 0xB6(480 lines) ->
DISPON, the one change vs mipidsi's working-but-1/4 baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 22:28:44 -05:00
Me Here
508fae56fb pm-kit: replay full CircuitPython st7796_init (extension setup before DISPON)
Host initdump (rust/uisim --bin initdump) showed mipidsi emits only SLPOUT,
MADCTL=0x48, INVON, COLMOD, NORON, DISPON — MADCTL already matches CircuitPython,
but the ST7796 extension setup (unlock, 0xB6 480-lines, power, gamma) is missing,
and sending it AFTER mipidsi's DISPON blanked the live panel. Replay the full
known-good st7796_init via Display::dcs() ending in its own DISPON. Adds the
initdump tool (capture init byte sequence on the host, no bench).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 22:06:31 -05:00
Me Here
0fa32a827f Rust UI: host simulator (PNG) + shared pm-ui crate; trim panel init
Answer to 'can you simulate it?': the UI now renders on the host.
- pm-ui: shared no_std embedded-graphics drawing (draw_ui), used by BOTH the
  firmware and the simulator — single source, no divergence.
- uisim: host crate that draws pm-ui onto a framebuffer and exports a PNG (pure
  Rust, no SDL). Confirmed the bring-up pattern renders correctly off-device, so
  the black screen is a panel/controller issue, not a draw bug.
- pm-kit: use pm_ui::draw_ui; trim the ST7796 extension init to just unlock + 0xB6
  (the gamma/VCOM sent after DISPON likely blanked it); LED solid during init then
  slow 1 Hz blink so hung-init / running / reset-loop are distinguishable.

Note: the simulator covers WHAT we draw (layout/colour/logic). It does NOT model
the ST7796 controller's hardware quirks (0xB6 line count, MADCTL scan, SPI init) —
those still need the bench, but that's a one-time bring-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:54:20 -05:00
Me Here
67182cd74c PM_G-1: fix I2C init on the pull-up-less Scroll Pack (busio+internal-pulls, bitbangio fallback)
The Pico Scroll Pack has no external I2C pull-up resistors (Pimoroni's C++ uses the
RP2040 internal pulls), so busio.I2C raised 'No pull up found on SDA or SCL' and the
firmware crashed before the splash. _make_i2c() now pre-enables the internal pull-ups
for busio and falls back to bitbangio (which uses them inherently). Pins GP4/GP5 were
correct.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:53:45 -05:00
Me Here
4afac44d06 index: add Rust firmware (alpha) section with pm-kit.uf2 download
A labelled developer/alpha section on the landing page with a brand-cyan download
button for the RP2350 Rust firmware + flash instructions, so it's grabbable from
the site (metronome.varasys.io/#rust) instead of the bare URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:36:04 -05:00
Me Here
0c8f370a5c pm-kit: send ST7796 extension init (0xB6 480 lines) mipidsi omits
mipidsi's ST7796 model uses the ST7789 init, which skips the ST7796-specific
extension-command unlock (0xF0) and Display Function Control (0xB6 = 480 driving
lines) — so the panel only scanned part of the screen (image confined to a corner
region + snow). After mipidsi's init, send the missing commands via Display::dcs()
using the known-good values from the CircuitPython st7796_init.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:32:08 -05:00
Me Here
17d2aa134d pm-kit: diagnostic display pattern + flip_horizontal (fix mirror)
Replace clear() with same-path full-screen fill, add a 4-edge red border, four
distinct corner markers (TL green / TR yellow / BL cyan / BR magenta) and a TL
label, to pin down rotation/mirror/size from one flash. Apply flip_horizontal to
match the panel's MADCTL MX bit (CircuitPython uses 0x48).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:16:34 -05:00
Me Here
35726b57ac PM_G-1: scrolling boot splash (model name) + transient BPM flash on tempo nudge
The splash doubles as a liveness/pixel-map check (if 'PM-G1 GRID' reads correctly,
the firmware booted and the LED mapping is right). The BPM flash makes X/Y tempo
nudges visible from any view (previously invisible in Grid/Pendulum).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:08:33 -05:00
Me Here
4275187008 pm-kit milestone 2: ST7796 display bring-up
Init the Kit's ST7796 320x480 over SPI0 (SCK=GP2, MOSI=GP3, CS=GP5, DC=GP6,
RST=GP7; BGR, colours inverted, 16 MHz) via mipidsi 0.9 + embedded-graphics, and
draw a panel + "PM-KIT / RUST OK" so SPI + the graphics stack are verifiable on
screen. GP25 LED keeps blinking as a heartbeat.

Compiles for thumbv8m; runtime (does it draw? colours/orientation right?) is the
on-device check. Next: tune orientation/colour if needed, then inputs + audio + pm-core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:56:07 -05:00
Me Here
8b4fca2a74 deploy: serve rust/pm-kit/pm-kit.uf2 (RP2350 Rust firmware download)
Copies the built Rust .uf2 to the web root when present, so it can be flashed from
the website (alongside the CircuitPython firmware downloads). Conditional — only
served if rust/pm-kit/build.sh has produced it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:49:49 -05:00
Me Here
0e224393f7 Rust port Stage 3 milestone 1: pm-kit boot-proof blink (RP2350)
First per-board binary. rust/pm-kit/ is a minimal rp235x-hal firmware that blinks
GP25 on the Pico 2 — proves the toolchain, RP2350 boot block (ImageDef), memory
layout, and flash before we add any drivers.

- src/main.rs + memory.x + build.rs + .cargo/config.toml: rp235x-hal blink, builds
  for thumbv8m.main-none-eabihf.
- build.sh + uf2.py: one command builds the ELF in the container, objcopies to a raw
  image, and packs pm-kit.uf2 (rp2350-arm-s family). Drag onto the Pico 2 in BOOTSEL.

Verified: builds clean; produces a valid 6-block UF2. Runtime (does it blink?) is the
on-device check. Next: drivers (display first) + link pm-core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:34:46 -05:00
Me Here
400d896518 Add PM_G-1 "Grid" form factor (Pimoroni Pico Scroll Pack) + Rust core/driver plan
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>
2026-05-31 20:30:15 -05:00
Me Here
c1601d9e46 Rust port Stage 2: scheduler timing + no_std (builds for RP2350)
- schedule.rs: ports the firmware's durs/timeline math (app.py tick/_prepare_next).
  render(track, bars) yields the deterministic click timeline; tests/schedule.rs
  asserts quarter-note spacing, subdivisions, swing 2/3:1/3, polymeter 5:4,
  accents/ghosts, mute, and multi-bar looping. All green on the host.
- The crate is now #![no_std] + alloc and builds for thumbv8m.main-none-eabihf,
  so the codec + scheduler are firmware-ready (verified:
  cargo build --lib --target thumbv8m.main-none-eabihf).

./rust/run.sh -> 9 tests pass (2 conformance + 7 schedule). docs/rust-port.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 19:34:02 -05:00
Me Here
adc92c7c02 PM_K-1 hardware: fix the 2 real import issues (crystal footprint + USB shield error)
- Crystal Y1: footprint was a 4-pad type (Crystal_SMD_3225-4Pin) on a 2-pin symbol ->
  pads 3/4 had no symbol pin. Switched to Crystal_SMD_2012-2Pin to match (set to the
  chosen crystal's real footprint at BOM time; ground the case pads if 4-pad).
- USB-C J5: the library symbol's shield pin "S1" mismatches older KiCad footprints that
  name the shell pads "SH" -> "pad S1 not found" ERROR. Dropped the shield net from the
  schematic; the USB shell->chassis is now a layout-time tie (direct or 1Mohm||cap),
  which is version-independent and standard practice. Error resolved; ERC 0.

The remaining ~46 import warnings are HARMLESS: "no net for pad N" on intentionally
unconnected pins (spare RP2350 GPIOs, unused relay NO contacts, unused ULN2003/74LVC14
channels, IC NC pins) -- expected on any board, nothing to fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 19:30:48 -05:00
Me Here
4fb3365f36 PM_K-1 hardware: tag parts into circuit-block "sheets" for KiCad select-by-block
board.py now wraps each section in a SKiDL Group (Power/MCU/Audio/RTC/MIDI/Indicator/
Speaker/Interconnect) via inline grp()/endgrp() helpers (no reindent). The netlist now
carries a per-part (sheetpath (names "/<block>/")), so in Pcbnew you can right-click a
footprint -> Select -> Items in Same Hierarchical Sheet and move a whole block at once
instead of dragging 167 loose parts. 167 parts across 8 sheets; ERC 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 19:21:18 -05:00
Me Here
9792729be0 PM_K-1 hardware: note locally-saved EVM/reference layouts in LAYOUT_REFERENCES.md
Downloaded the 3 priority reference layouts into hardware/datasheets/ (git-ignored):
- TPS65131EVM_SLVUAW7.pdf (switcher EVM 4-layer plots + BOM)
- TPS7A30-49EVM_SLVU405.pdf (+/-15V dual-LDO EVM, schematic+layout+BOM)
- RP2350-Minimal-KiCAD.zip (official RP2350 KiCad: nested RP2350A/QFN-60 + RP2350B/QFN-80)
Verified the switcher EVM contains the full Top/Inner1/Inner2/Bottom layer plots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:51:51 -05:00
Me Here
795252fbce PM_K-1 hardware: LAYOUT_REFERENCES.md -- manufacturer reference layouts per stage
Maps each stage to its authoritative layout reference (EVM / datasheet layout section /
app-note / reference design) with URLs + the key takeaway, to complement LAYOUT.md's
general rules. Standouts that match our exact circuit:
- TPS65131EVM-839 (SLVUAW7) -- switcher layout to copy
- TPS7A30-49EVM-567 -- a +/-15V dual-LDO EVM = our exact rails
- RP2350 Minimal-KiCAD.zip -- official MCU/USB/crystal/QSPI reference layout (+ footprints)
Plus PCM5102A community refs, THAT1646/1240 reference boards, TI op-amp layout app-notes
(SBOA092B/SLOA046), RV-8803 app manual solder-pad section, USB-C/USBLC6 diff-pair rules,
and TI class-D layout for the PAM8302.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:48:19 -05:00
Me Here
af8e7e236b PM_K-1 hardware: render simulations as PNG plots (gnuplot) so they're viewable
Added gnuplot to the EDA container; sims now wrdata their curves and plots.gp renders 5
PNGs in hardware/eda/sim/plots/ (open in any browser/viewer):
- input_loading: instrument(1M) preserves the +16dB pickup resonance the line(25k) flattens
- stage1_cmrr: common-mode leakage, 0.1% vs 1% resistor mismatch
- stage2_recon: DAC reconstruction filter (flat to 20k, -3dB ~75k)
- stage4_driver: balanced differential response (flat across audio)
- stage1_phantom: +48V transient clamped at the op-amp input, decays to ~0
(.data intermediates git-ignored.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:39:46 -05:00
Me Here
be524ce1ea Rust port Stage 1: track-format codec crate (passes the golden vectors)
A third implementation of the track DSL alongside engine.js and app.py, validated
against the same tests/fixtures/track-format.json:

- rust/track-format/: pure parse()/serialize() codec (std + alloc for now; no_std is
  a later refinement). Ports the app.py/engine.js semantics exactly — grouping,
  subdivisions, swing, ghost, polymeter, euclid, GM note-number aliases, unknown->beep,
  default groove (group-start accents), tempo clamp, empty->beep, and the playback-flow
  tokens (rep/end/relative-goto). Carries vol/cd too, so it's the most spec-complete
  of the three.
- tests/conformance.rs: the Rust adapter — reads the shared fixtures, asserts each
  case's normalized form (number-tolerant deep-equal) + serialize idempotency.
- rust/Containerfile + run.sh: Rust toolchain in a container (mirrors hardware/eda/),
  with the thumbv8m.main-none-eabihf target for the eventual RP2350 firmware. Never
  on the host.

Verified: ./rust/run.sh -> cargo test -> conformance + idempotent both pass.
docs/rust-port.md Stage 1 marked done.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:36:59 -05:00
Me Here
6aeca94222 PE-1 editor display: match the device (always-elapsed, ramp, device link)
Bring the web editor's display in line with the PM_K-1 device screen:
- Elapsed stopwatch is always visible and counts while playing (was gated behind
  the Timers switch); the switch now governs only the countdown.
- Tempo-ramp indicator in the display (↗/↘ amount/everyBars), shown whenever a
  ramp is active — mirrors the device's ramp arrow.
- Header "device" badge that lights green with the port name while a PM_K-1 /
  PM_X-1 is connected over USB-MIDI, updated live on connect/disconnect. The
  editor requests MIDI on load (Chrome remembers the grant) so it reflects the
  link automatically; the badge is also click-to-connect.

editor-beta.html (separate live-sync variant) left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:29:53 -05:00