Skip to content

HITL: Implement logic analyzer HITL - #92

Open
fughilli wants to merge 11 commits into
mainfrom
agent/fug-105-upgrade-sbc-deploy
Open

HITL: Implement logic analyzer HITL #92
fughilli wants to merge 11 commits into
mainfrom
agent/fug-105-upgrade-sbc-deploy

Conversation

@fughilli

Copy link
Copy Markdown
Owner

No description provided.

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://fughilli.github.io/splanc/pr-preview/pr-92/

Built to branch gh-pages at 2026-08-16 07:03 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Claude Agent and others added 4 commits August 15, 2026 22:21
Move the sbc-deploy pin from cb70fcc to 18a5346 in lockstep across all four
spots (MODULE.bazel git_override + pi/hitl/flake.{nix,lock} + pi/provisioning/
nix/flake.lock). The new revs (upstream PRs #4 + #5) make the zero-conf macOS
aarch64 builder work for EXTERNAL bazel_dep consumers — the builder flake now
rides in each sbc_application target's runfiles, so `bazel run …image_sd` on
macOS auto-manages the sized VM from the pin (no --framework-subdir, no
vendoring, no --cross). Verified on hardware: the Pi 3 image builds via the
auto-started VM and deploys.

Locks regenerated with `nix flake update sbc-deploy` (only the sbc-deploy node
moved; transitive inputs unchanged). MODULE.bazel.lock needs no change
(git_override isn't content-pinned). Also gitignore .sbc-build/ (consumer-side
builder gc-roots/logs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fied

New rig variant (//pi/hitl:hitl_la, board raspberry-pi-3) with an FX2/fx2lafw
"Saleae clone" logic analyzer tapping the ESP32-C6 DUT's WS2812 DIN, closing the
"real LED wire correctness needs a logic analyzer on a bench" gap.

The FX2 is a rig-level SHARED instrument the daemon owns (never passed into a
container): internal/analyzer brokers captures on the single device (mutex-
serialized), maps each DUT to its channel subset + protocol, runs a triggered
sigrok-cli capture, and decodes it (rgb_led_ws281x / rgb_led_spi — both built
into libsigrokdecode >=0.5.3). New POST /capture endpoint; the container-side
`hitl-capture` and the daemon are wired via $HITL_CAPTURE_SERVER. So the runner's
per-DUT raw-USB isolation is untouched.

sbc-deploy injects the board via $SBC_BOARD at eval, so nix/hitl-app.nix keys its
sigrok closure + capture flags + FX2 udev rules on raspberry-pi-3 — one appModule
yields a lean Pi 5 image and a capture-enabled Pi 3 image, no flake fork.

Hardware-driven specifics:
- analyzerChannelMap defaults to D6 (PIN20/GPIO20 -> FX2 CH6 on hitl-rig-la-1).
- hitl_la hostname = the flake's nixosConfigurations attr "hitl-rig" (deploy_live's
  --hostname is the attr selector); per-rig identity via $SBC_HOSTNAME_OVERRIDE.
- broker: when the tapped line is idle the trigger never fires and sigrok writes no
  .sr; detect that and return "no data captured …" instead of a cryptic decode error.

Verified: go build/vet/test (incl. a synthetic-.sr golden that runs the real
sigrok decode); the Pi 3 image builds + deploys; and on hardware, a driven
counting pattern decodes correctly on D6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… notes

harness/led_pattern.py: pure helpers — build a set_counting_pattern message and
the expected pixels; diff_structure() compares lit-channel STRUCTURE (the firmware
scales the wire via WS2812 color-correction/gamma+brightness, so full-scale 255
arrives ~160 — assert the pattern, not exact bytes). Pinned by the CI unit test
tests/test_led_pattern.py.

harness/hitl_led_capture.py (manual+hitl py_test): drive a known counting pattern
on the DUT over the player WSS, capture the WS2812 wire via the daemon's shared
analyzer, assert the decoded pattern. --device-ws mode drives an already-reachable
ws URL + captures via the daemon directly (no reserve/flash/provision) — the path
used to confirm the rig on hardware, tunneling to the DUT through the rig host.

WORKLOG: the LA rig is hardware-verified (a driven pattern decodes on D6). Two
open rig-networking issues documented as follow-ups: the Pi 3 can't host the
provisioning AP (brcmfmac AP mode won't start — needs a USB Wi-Fi dongle for the
AP), and the reservation container can't reach a DUT on an external LAN. Until
those are fixed, use --device-ws.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tests couldn't tell rig types apart: every rig shares tag:splanc-hitl and
pool.Pick chooses by idle/shortest-queue, so an analyzer test could land on an
ESP-toolbox rig and fail at capture. Model the analyzer as a first-class,
self-describing CAPABILITY (tags are too coarse).

- api.Status gains Analyzer{present, driver, protocols, channels}; the daemon
  advertises it via queue.WithAnalyzer(broker.Describe()) when the broker is
  enabled (analyzer.Broker.Describe derives distinct protocols/channels from the
  channel map). Additive/omitempty — non-analyzer rigs are unchanged.
- pool.Pick takes an optional pool.Require{Analyzer}: it narrows the pool to
  rigs advertising the capability before ranking, or errors clearly if none.
- `hitl reserve/run --require analyzer` threads it through resolve→Pick.
- hitl_client.Reservation(require=...) passes --require; hitl_led_capture sets
  require="analyzer" AND asserts the chosen rig's /status has one (so an explicit
  --server / --device-ws pin can't silently target a non-analyzer rig either).

Unit tests: pool require-filter (picks the analyzer rig over an idle non-analyzer
one; errors when none) + Broker.Describe. go build/vet/test green; CLI validates
--require. Advertisement lights up once the daemon is redeployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Pi 3's onboard brcmfmac can't reliably host an AP, so the analyzer rig uses a
dedicated USB WiFi dongle for its AP (ap0) and leaves wlan0 free for STA. The dongle
is an RTL8851BU (WiFi 6 + BT combo, 0bda:1a2b CD-ROM → 0bda:b851 after usb_modeswitch).

Driver: nix/rtl8851bu.nix from morrownr/rtw89 (@ e2be1a0) — the mainline mac80211
rtw89 family backported to kernels 6.6+, so it supports hostapd AP cleanly. The rig's
in-tree rtw89 predates 8851BU-USB (landed ~6.14; rig is 6.12.87), hence out-of-tree;
it builds on 6.12 with zero source patches, and ships the WiFi firmware
rtw8851b_fw-1.bin (nixpkgs' linux-firmware predates it).

hitl-app.nix (gated on the analyzer board via SBC_BOARD): apIface = ap0; module
rtw89_8851bu_git in boot.kernelModules (led-class-multicolor first, since rtw89 is
built with CONFIG_RTW89_LEDS_MC); driver in boot.extraModulePackages + hardware.firmware
(post-boot module → rides the deploy_live layer, no SD reimage); a .link renames the
radio (matchConfig.Driver = rtw89_8851bu_git) to ap0; usb_modeswitch udev rule flips
the CD-ROM dongle into WiFi mode; the AP profile / firewall forwarding target ap0.

Verified on hitl-rig-2 across a clean reboot: modules autoload, firmware loads,
wlan1 -> ap0 rename fires, and NetworkManager hitl-ap activates on ap0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude Agent and others added 2 commits August 16, 2026 04:21
…ions

hitl-capture (run over SSH by the harness) needs $HITL_CAPTURE_SERVER, but the
daemon's `podman -e` vars reach only sshd's process, not the sessions it spawns
(SetEnv was a static PATH/DBUS list, UsePAM no). The container's env-driven tools
(hitl-capture, and HITL_DUT/HITL_ADAPTER_SERIAL as documented) were thus invisible
to `ssh host cmd`. This surfaced now because the earlier LA capture was verified via
--device-ws (daemon-side capture), never the container hitl-capture path.

Fix: the entrypoint writes the HITL_* env to ~/.ssh/environment and sshd_config gets
PermitUserEnvironment yes, so both interactive shells and `ssh host cmd` inherit them.
Also add gnugrep to the entrypoint's runtimeInputs — it uses `printenv | grep` to
select the vars, and only openssh+coreutils were on its PATH (grep was missing, so
the env file came out empty).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… reload)

Two fixes found bringing the full container capture path up on real hardware:

- Capture window: the broker software-triggers within a fixed sample window
  (fx2lafw has no hardware trigger), and the default was ~8.3ms. But the C6's
  counting probe repaints the WS2812 frame only at its render cadence (10 Hz /
  every 100ms), so an 8ms window misses the burst ~92% of the time and sigrok
  reports "trigger never fired / no data". Bump the default to ~208ms (2× the
  100ms cadence) so a burst's rising edge is reliably in the window.

- Image reload: `podman load` won't move hitl-test:latest to the freshly-built
  image while the tag is still held by an old image that a stale reservation
  container references — so container.nix changes silently never reached the rig
  (had to podman-load by hand). hitl-image-load now clears stale hitl containers
  and untags the old image before loading, and orders Before=hitl-manager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude Agent and others added 2 commits August 16, 2026 06:03
A shared-mailbox bridge so the claude-container agent can run host/macOS-only
commands (bazel run …deploy_live / image_sd and other host bazel/nix that need the
auto-managed aarch64 builder VM + SSH to the rig) without a human relaying each one.

- tools/hostdeploy.py: host-side watcher, left running in the repo root. Watches
  .hostdeploy/request.json, runs allowlisted commands (bazel/bazelisk/nix/git),
  streams output to <id>.log, writes <id>.status, heartbeats .hostdeploy/alive.
- tools/hostrun.sh: container-side submitter. Forwards SBC_* env, streams the log,
  exits with the real rc. Preflights the heartbeat and tells you to start the watcher.
- .hostdeploy/ (runtime mailbox) gitignored.
- .claude-container-overlay/skills/host-deploy: agent skill documenting the mechanism.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The lock recorded a stale @@//MODULE.bazel hash; bring it in line with the
current MODULE.bazel content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…BLE radio)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant