Commit Graph

53 Commits

Author SHA1 Message Date
david 340ea3ec0a fix: reduce loop delay to 10ms so __loopHook processes RPCs
Validate and Test / validate-patches (push) Failing after 12s
Background thread stack (500 bytes) overflows after ~50 draw_frame
calls. __loopHook (called after loop() returns) is the fallback.
With delay(1000), it ran once per second — too slow.
Reducing to delay(10) keeps RPC processing responsive.
2026-06-24 05:24:20 -07:00
david f547201c98 fix: non-blocking response drain using setblocking(False)
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 05:23:19 -07:00
david 88ddf740cf fix: use REQUEST (type 0) + drain response after each send
Validate and Test / validate-patches (push) Failing after 13s
RouterBridge on MCU ignores NOTIFICATION (type 2) messages.
Must use REQUEST (type 0) for draw_frame, then drain the
response to prevent router buffer buildup.
2026-06-24 05:20:26 -07:00
david 86facfea38 clean: remove debug logging, final viz cleanup
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 05:14:33 -07:00
david 4f07a4007f fix: use NOTIFICATION (type 2) for draw_frame RPC
Validate and Test / validate-patches (push) Failing after 12s
Viz was sending REQUEST (type 0) but never reading responses.
Router response buffer filled up, blocking all subsequent RPCs.
NOTIFICATION (type 2) expects no response — no buffer buildup.
2026-06-24 05:09:53 -07:00
david 2af901bdca debug: add real-time logging to viz (note on/off, frame sent/drop)
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 05:03:32 -07:00
david 39e9b5c2fa fix: no reconnect storm on send failure, reduce refresh to 1 FPS
Validate and Test / validate-patches (push) Failing after 13s
- try_send: don't reconnect on failure, just skip and retry next cycle
- Background refresh: 5 FPS → 1 FPS to reduce router serial load
- Revert Bridge.run() - RouterBridge uses Zephyr RTOS thread
2026-06-24 04:59:09 -07:00
david 2968d57e1d fix: add Bridge.run() to loop() so MCU processes incoming RPCs
Validate and Test / validate-patches (push) Failing after 13s
Without Bridge.run() in loop(), the MCU never reads incoming
draw_frame requests from the router. First frame goes through
during setup handshake, but subsequent frames fill the router's
send buffer and the viz stalls.
2026-06-24 04:57:14 -07:00
david f43d046153 fix: restore executable permission on viz script
Validate and Test / validate-patches (push) Failing after 13s
2026-06-24 04:55:37 -07:00
david 3b40592055 fix: robust router send with auto-reconnect, 10ms timeout
Validate and Test / validate-patches (push) Failing after 13s
- try_send() helper: reconnect on send failure
- 10ms socket timeout instead of 300ms (stalls poll loop)
- Refresh path also uses try_send
- Remove committed library bloat from repo (add .gitignore)
2026-06-24 04:46:59 -07:00
david f70a8701c9 fix: make router socket non-blocking to prevent poll loop stalls
Validate and Test / validate-patches (push) Failing after 13s
2026-06-24 04:45:15 -07:00
david 31bd1d497c fix: use angle-bracket includes for Arduino libs
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 04:30:22 -07:00
david c3007867c4 fix: remove libraries from sketch.yaml, let CLI auto-detect
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 04:28:40 -07:00
david 94614bf774 fix: git stash before deploy, no pop (discard board changes)
Validate and Test / validate-patches (push) Failing after 12s
- just deploy: stash local board changes before git pull
- install.sh: configure git alias 'pull-clean' for manual use
- Board-side changes from test edits are discarded on deploy
2026-06-24 04:27:21 -07:00
david 014682e509 feat: auto-flash MCU sketch on post-merge if sketch/ changes
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 04:24:51 -07:00
david 2ac964907c refactor: rename sketch to sketch.ino, add flash-sketch recipe
Validate and Test / validate-patches (push) Failing after 12s
- Permanent rename: led-matrix-bridge.ino → sketch.ino (Arduino CLI convention)
- Add just flash-sketch recipe for compiling/flashing MCU via board's arduino-cli
- Run via post-merge hooks for auto-deploy
2026-06-24 04:24:32 -07:00
david 7af3874757 fix: event-driven viz with immediate rendering, zero decay, clean stats
Validate and Test / validate-patches (push) Failing after 12s
Rewrite led-matrix-viz.py to eliminate latency:
- Render and pack frame immediately on each MIDI event
- Send on next poll cycle (no POLLOUT dependency, no frame loop)
- Notes on/off purely from MIDI data — zero decay
- Background 5 FPS refresh for router drop recovery
- Clean stats: FPS, active_notes, midi_events, bytes_received, raw_packets
2026-06-24 04:09:41 -07:00
david 1d431c8902 debug: print note on/off to journal
Validate and Test / validate-patches (push) Failing after 13s
2026-06-24 04:08:24 -07:00
david 979e89e305 fix indentation and debug: add pipeline counters
Validate and Test / validate-patches (push) Failing after 13s
2026-06-24 04:07:17 -07:00
david 89da97c637 debug: log raw message content to journal
Validate and Test / validate-patches (push) Failing after 13s
2026-06-24 04:03:01 -07:00
david 515617ac94 debug: add bytes_received and raw_packets to viz stats
Validate and Test / validate-patches (push) Failing after 12s
2026-06-24 03:59:12 -07:00
david a94ac07a5c fix: pack frame at render time, send without POLLOUT dependency
Validate and Test / validate-patches (push) Failing after 13s
- pending_bytes stores already-packed msgpack, sendable immediately
- Try sendall() directly every iteration — no POLLOUT gate
- Refresh path resends last_sent_params (not raw frame tuple)
- Router socket uses settimeout instead of setblocking for simpler I/O
2026-06-24 03:56:50 -07:00
david 68d38de9a5 fix: rewrite viz as event-driven with zero-latency MIDI rendering
Validate and Test / validate-patches (push) Failing after 13s
- Remove frame loop latency: render and send immediately on MIDI event
- Remove decay: notes on/off based purely on MIDI note-on/note-off
- Background 5 FPS refresh: catch router-dropped frames
- active_notes uses set instead of dict with decay counters
- Simpler stats: FPS, active notes, midi events processed
2026-06-24 03:31:12 -07:00
david 5115518b5f fix: reduce viz decay to 3 frames, add max-age safety cap, add FPS stats
Validate and Test / validate-patches (push) Failing after 12s
- DECAY_FRAMES default 8→3 (100ms visual trail)
- MAX_NOTE_AGE safety cap: force-expire notes after 15 frames
- Track note age separately from decay to prevent stale notes
- Add FPS and active_notes to viz stats for diagnostics
- Fix router reconnect: unregister old fd, not new one
2026-06-24 03:27:10 -07:00
david 0382450f8b feat: add git hooks for pre-push validation and post-merge auto-deploy
Validate and Test / validate-patches (push) Failing after 12s
- .githooks/pre-push: validate Pd patches via python3 pd-validator
- .githooks/post-merge: auto-restart services when systemd/scripts change
- install.sh: configure core.hooksPath at install time
- justfile deploy: set hooksPath after git pull
2026-06-24 03:19:51 -07:00
david ce4022fa1c fix: use correct pd version flag and semver regex
Validate and Test / validate-patches (push) Failing after 1m41s
- pd expects -version (single dash), not --version
- Use strict \d+\.\d+\.\d+ regex to extract full semver
2026-06-24 03:17:52 -07:00
david 290862a74e feat: add skills, version pinning, CI/CD, and generated-file headers
Validate and Test / validate-patches (push) Failing after 2m7s
- Create skills: rego-pd-validator, usb-gadget-configfs, led-matrix-msgpack-rpc
- Update pd-audio-synthesis skill with Uno Q specifics
- Add Pd version check (min 0.54.0) to start-synth.sh
- Add DO NOT EDIT headers to generated pd patches
- Add Gitea Actions CI workflow for patch validation
- Add ConditionPathExists guards to legacy systemd services
- Add 'just ci' recipe for CI
2026-06-24 03:16:28 -07:00
david 1876d8cd42 synth-diagnostic: just run tests, no service management
- run-tests.sh: no systemctl start/stop, just runs test-unit.py
- synth-diagnostic.service: removed Before=, keeps After=prometheus-exporter
  (no deadlock risk since run-tests.sh doesn't start services)
- justfile deploy: stop pipeline -> restart prometheus -> start
  synth-diagnostic -> restart pipeline

Pipeline starts independently at boot (no After=synth-diagnostic).
Diagnostic provides visibility at boot without blocking startup.
2026-06-24 02:59:14 -07:00
david 9aead397fd fix: remove After=synth-diagnostic from pipeline services
Root cause: synth-diagnostic uses systemctl start to start pipeline
services, but those services had After=synth-diagnostic — causing
deadlock (they waited for synth-diagnostic to exit, which was waiting
for them).

Fix: pipeline services no longer list synth-diagnostic in After=.
synth-diagnostic has Before= them for boot ordering, and starts them
directly via systemctl start in run-tests.sh.

Also fixed prometheus-exporter: had After=synth-diagnostic backwards
— should start before synth-diagnostic (synth-diagnostic has
After=prometheus-exporter).
2026-06-24 02:53:16 -07:00
david 8e82286287 synth-diagnostic starts pipeline services, cleans up on failure
- run-tests.sh: starts pipeline services, runs test-unit.py,
  stops services on failure, leaves running on success
- synth-diagnostic is the single gate for pipeline startup
- deploy: stop pipeline → restart prometheus → start synth-diagnostic
  (synth-diagnostic starts pipeline on success)
2026-06-24 02:49:58 -07:00
david b445c272fb add boot-time synth-diagnostic.service
- synth-diagnostic.service: runs test-unit.py before pipeline services
- Type=oneshot, After=prometheus-exporter, Before=pipeline services
- Pipeline services: After=synth-diagnostic
- deploy: restart prometheus-exporter → start synth-diagnostic → restart pipeline
- install.sh: includes synth-diagnostic in SERVICES array
2026-06-24 02:48:40 -07:00
david 2e10839fc9 add diagnostic dashboard served from prometheus-exporter
- dashboard.html: standalone HTML, fetches :9090/metrics, auto-refresh
- Exporter serves dashboard at /dashboard and /dashboard.html
- Cards: Pipeline, Audio, Mixer, MIDI Bridge, LED Viz
- Live gauges, counters, service status dots, last-activity timestamps
2026-06-24 02:43:31 -07:00
david 74e0d3fa03 fix: use threading.Lock() instead of object() for _stats_lock 2026-06-24 02:42:06 -07:00
david f673da4b3b merge midi metrics into prometheus-exporter
midi-bridge: removed HTTP server on :9091, now writes /tmp/midi-stats.json
prometheus-exporter: reads /tmp/midi-stats.json, exposes all metrics on :9090/metrics
Single scrape endpoint for all pipeline components
2026-06-24 02:41:27 -07:00
david 7706517b88 fix: initialize stats_last_write inside main() to avoid UnboundLocalError 2026-06-24 02:39:37 -07:00
david ad55dbc5e0 add viz diagnostic metrics to prometheus-exporter
- Viz writes stats to /tmp/viz-stats.json (frames_sent, frames_dropped, queue_depth)
- Exporter reads viz-stats.json and exposes:
  - viz_frames_sent_total (counter)
  - viz_frames_dropped_total (counter)
  - viz_pending_queue (gauge)
  - viz_queue_full (gauge)
2026-06-24 02:38:51 -07:00
david 5c95aa6cd0 viz: proper POLLOUT queue to prevent frame drops
- FrameQueue (max 5) buffers frames when router is slow
- POLLOUT drains pending_frames queue
- No silent drops — full queue increments counter
- Router always registered POLLIN| POLLOUT
- Frame interval 30ms (33 FPS)
2026-06-24 02:37:46 -07:00
david 272a13f8d9 viz: increase FPS to 30, drain UDP aggressively
- Poll timeout capped at 10ms for responsiveness
- Drain all pending UDP data before parsing
- Auto-reconnect router on error
- FPS 15 -> 30
2026-06-24 02:33:17 -07:00
david caef6f04c8 midi-bridge: add prometheus metrics on :9091/metrics
- midi_messages_total: all MIDI messages
- midi_notes_on/off_total: note-on/off counts
- midi_last_status/note/velocity: last message tokens
- midi_last_time_unix: epoch of last message
- Removed MIDI counters from prometheus-exporter (bridge owns them)
2026-06-24 02:25:31 -07:00
david 163ad4145b add prometheus exporter + test suite
- prometheus-exporter.py: stdlib-only HTTP metrics on :9090/metrics
- Exposes: ALSA status, hw_ptr rate, service health, mixer controls
- MIDI counters: notes_on, notes_off, errors
- test-unit.py: 10-test suite covering full MIDI→Pd→Audio pipeline
- test-tone.pd, test-midi-log.pd: minimal debug patches
- systemd service for exporter
- install.sh/deploy updated to include exporter
2026-06-24 02:22:05 -07:00
david 3cc497f632 refactor: 3-module pd architecture + viz non-blocking fix
- midi-in.pd: netreceive -> route -> unpack -> pitch/velocity outlets
- audio-out.pd: inlet~ -> dac~ abstraction
- synth.pd: consumer patch wiring midi-in -> voice -> audio-out
- led-matrix-viz.py: router socket non-blocking, registered with poller
- pd.rego: skip outlet/abstraction false positives in validation
2026-06-24 02:08:57 -07:00
david 242c74236f systemd services: midi-bridge, pd-synth-onboard, led-matrix-viz + install script 2026-06-24 01:36:36 -07:00
david f919af3d8b midi-bridge: normalize all MIDI channels to channel 1 2026-06-24 00:50:39 -07:00
david ed8cb7ca5a working synth: two detuned oscs, env~, lop~ (no slider for now) 2026-06-24 00:45:37 -07:00
david ccc40e4314 rebuild synth.pd from working components 2026-06-23 22:17:02 -07:00
david e098f32a32 fix synth: envelope driven by velocity not pitch, louder gain 2026-06-23 21:58:10 -07:00
david 7e8848d23c justfile: git-based deploy, streamlined recipes 2026-06-23 21:29:16 -07:00
david d239d58c44 onboard audio: JMISC HPH wiring, UCM fix, update scripts for git deploy 2026-06-23 21:25:51 -07:00
david a135dc4452 controller.pd: add MIDI passthrough for MPK Mini → board
Added notein→pack→noteout and ctlin→ctlout chains that forward all
incoming MIDI from the MPK Mini to the board's MIDI port. This means
physical keys/knobs now work alongside the GUI click buttons.
2026-06-23 14:44:05 -07:00
david 334e934dd7 stabilize systemd services: fix races, masked flooding, dependencies
- pd-synth: add After=usb-gadget-audio.service midi-bridge.service (was Wants= only, race)
- usb-gadget-audio: add Restart=on-failure After=sysinit.target
- pd-synth-i2s: fix Wants=configure-usb-audio.service → usb-gadget-audio.service
- detect-usb-role.sh: hardcode gadget role (all 4 detection methods always fail, skip 2s sleep)
- arduino-router-serial: masked on board (flooded 2173× restarts looking for nonexistent /dev/ttyGS0)
2026-06-23 14:31:11 -07:00