Commit graph

284 commits

Author SHA1 Message Date
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
Me Here
8cbd53a2ee PM_K-1 hardware: register pm_k1 fp-lib (fix import errors) + white bg on SVGs
- hardware/kicad/fp-lib-table: project footprint-library table registering pm_k1 ->
  ${KIPRJMOD}/pm_k1.pretty, so opening the pm_k1_core project auto-resolves the custom
  TQ2SA relay + RV-8803-C7 footprints (fixes the 4 "footprint not found" import errors
  for K1/K2/K3/U13).
- schematics/*.svg: inserted a white background rect (netlistsvg renders dark strokes on
  a transparent bg -> invisible in dark-mode viewers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:57:10 -05:00
Me Here
39fe087b2c PM_K-1 hardware: per-block schematic SVGs (netlistsvg) so the design is viewable
Added Node.js + netlistsvg to the EDA container; make_svg.py renders a SKiDL block to a
schematic SVG. Generated hardware/eda/schematics/*.svg for 12 blocks (audio stages 1-4 +
integrated, power tree, RP2350 core, RTC, MIDI, indicator, speaker) -- open in a browser.
Auto-routed (functional, not pretty); per-block so they're readable. interconnect omitted
(netlistsvg layout engine errors on the 24-pin USB-C + headers; its mapping is in DESIGN.md
s7). Intermediates (.json/_skin.svg) git-ignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:42:04 -05:00
Me Here
f201892c9c docs: staged Rust-port plan (codec crate first, gated by the golden vectors)
Grounds the native-Rust direction in what now exists: port inside-out, lowest risk
first, with tests/fixtures/track-format.json as the acceptance gate. Stage 1 (the
track-format crate as a third conformance adapter) is the concrete next PR -
host-testable in a container, no hardware. Toolchain goes in a container per the
develop-in-container rule, not the host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:13:32 -05:00
Me Here
8bba218f67 Editor controls for playback flow + close web-side divergences
- docs/playback-flow-test.md: on-device verification checklist for the runtime
  (stop / rep / next / relative-goto / boundary / manual-override cases).
- editor.html + editor-beta.html: graphical "At end" control (loop / next / stop /
  goto ±N) plus a rep-count input in the arrangement panel, wired through
  state.rep/state.end -> currentSetup/currentPatch. Authoring is no longer
  text-field-only.
- src/engine.js: patchToSetup now clamps tempo to [5,300] and defaults to a beep:4
  lane when no lanes are given, matching the firmware. The editors keep their
  "no lanes" hint by checking the raw input for a ':' token instead of parsed lanes.
- fixtures: tempo-clamp-high + empty-defaults-to-beep now pass on both engines.

Suite: 41 pass / 1 known (only the intentional vol/cd host boundary remains).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:11:43 -05:00
Me Here
42eefdf250 PM_K-1 hardware: custom RV-8803-C7 footprint (from datasheet) -> all 27 resolve
hardware/kicad/pm_k1.pretty/RV-8803-C7.kicad_mod, built from the RV-8803-C7 datasheet
(Mouser 2308195) Recommended Solder Pad drawing: SON-8, pad 0.4x0.8mm, pitch 0.9mm,
rows +/-0.65mm (0.5mm inner gap); top-view pin map per the package drawing (pin-1 index
bottom-left): bottom L->R 1-4, top L->R 8-5. Validated via `kicad-cli fp upgrade`.

With this + the TQ2SA relay, ALL 27 board.net footprints now resolve (19 stock KiCad +
2 in pm_k1.pretty). board.net imports 100% clean into Pcbnew once the pm_k1 project
footprint library is registered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:00:48 -05:00
Me Here
fb9afe2b27 PM_K-1 hardware: custom TQ2SA relay footprint (verified) + project fp lib
hardware/kicad/pm_k1.pretty/Relay_DPDT_Panasonic_TQ2-SA.kicad_mod -- built from the
Panasonic TQ-SMD datasheet "Recommendable mounting pad, SA type" (2.54mm pitch, 1.0x2.94mm
pads, rows +/-3.31mm, pin map coil 1/10 / pole1 COM3 NC4 NO2 / pole2 COM8 NC7 NO9).
Validated via `kicad-cli fp upgrade` (parses to canonical KiCad 9 format). board.py now
references pm_k1:Relay_DPDT_Panasonic_TQ2-SA and pm_k1:RV-8803-C7.

RV-8803-C7 footprint NOT built: every host with its land pattern blocks/times-out my
fetcher (Micro Crystal, EM Micro, Mouser) and SnapEDA needs login. Won't guess a 0.8mm-pitch
land pattern (geometry + pin-pad mapping). Drop a verified RV-8803-C7.kicad_mod into
pm_k1.pretty (SnapEDA / datasheet) and it resolves.

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