Test on hardware (HIL)
Goal: Run more crosspad-hil scenarios than the Start-here smoke test, and read their reports.
You need: Start here done, including the crosspad-hil venv from 1. Set up the toolchain and a passing smoke test in 4. Check it works, with a flashed CrossPad on USB.
Every scenario answers with its exit code: 0 pass, 1 a test failed on the board, 2 a setup problem (no board, port busy, missing dependency).
-
Activate the crosspad-hil venv
This puts the
crosspad-hilcommand from your project’s venv on the PATH for this terminal. Used the one-line installer? Skip this step:crosspad-hilalready works.Terminal window . ~/CrossPad/.venv/bin/activateTerminal window C:\CrossPad\firmware\.venv\Scripts\Activate.ps1You should see:
(.venv)at the start of your prompt.If not: crosspad-hil install fails
-
Check the setup and list boards
doctorchecks Python, the libraries crosspad-hil needs, the serial ports and (on Linux) your serial group.deviceslists each CrossPad with its ports.Terminal window crosspad-hil doctorcrosspad-hil devicesYou should see:
[ok]rows fromdoctor, one of them naming your board, then one row per board fromdevices:[ok] python 3.…[ok] import:serial present[ok] import:rtmidi present[ok] import:yaml present[ok] devices dev_xxxx:default...ID MODE CDC CONSOLE ESP MIDI / STM MIDI / UAC2dev_xxxx default /dev/ttyACM1 /dev/ttyACM0 …On Windows the ports show as
COMx. Note your board’sdev_xxxxid.If not: a
[!!] import:row: crosspad-hil install fails.[!!] serial-group: permission denied on the serial port.[!!] devicesorno CrossPad found: smoke test stops with NO_DEVICE or PORT_BUSY. -
Optional: reserve a shared board
When several people share one test board, reserve it before you use it, so nobody else flashes or resets it during your test. Pick a holder name, such as your name or your session’s name.
Terminal window crosspad-hil bench statuscrosspad-hil bench claim --holder <name>export CROSSPAD_BENCH_HOLDER=<name>crosspad-hil cdc USB_GUARDTerminal window crosspad-hil bench statuscrosspad-hil bench claim --holder <name>$env:CROSSPAD_BENCH_HOLDER = "<name>"crosspad-hil cdc USB_GUARDThe variable makes later
crosspad-hilcommands in this terminal run as you; without it the board answersBENCH_BUSY. The lease lasts 30 minutes, and every board command renews it, including a long scenario.You should see:
<name> holds dev_xxxx until HH:MM (in 30 min); every board op renews it, thenUSB_GUARD: 0.USB_GUARD: 0means the board never stops at the USB serial mode? prompt, so tests run without a tap on the screen. OnUSB_GUARD: 1, runcrosspad-hil cdc "USB_GUARD 0"once (the board saves it), and tick Auto-accept updates at the next update as in 3. Flash the board.If not:
claimexits1with… is claimed by …; you are #1 in the queue: someone else holds the board, so wait and claim again at least once an hour to keep your place. When the board frees up, the first in the queue has 10 minutes to claim it; see the board is claimed by another holder.NO_CDC_IN_AUDIO_MODE: the board is not in MIDI + Serial mode; runcrosspad-hil usb-mode set defaultand press Allow on the board once. -
Run the smoke test as JSON
--jsonprints the whole report as one JSON object, for scripts and CI.Terminal window crosspad-hil run smoke --jsonYou should see: one JSON line containing
"passed": trueand"exit_code": 0.If not:
"exit_code": 1: smoke test prints MISSING MARKER, FATAL or BOOT LOOP."exit_code": 2: smoke test says No boot banner on the console port or smoke test stops with NO_DEVICE or PORT_BUSY. -
Run another scenario and find its report
release_gateruns a broad set of checks in one go;stabilityruns the board for hours;latencyandperfare measurements. Each lists its options with--help.Terminal window crosspad-hil run release_gate --helpcrosspad-hil run release_gate --quickcrosspad-hil run stability --helpcrosspad-hil run latency --helpls hil_logs/--quickruns a short version of the checks; it stops at the USB serial prompt if the guard is on.stabilityruns until you stop it unless you pass--duration-hours.You should see:
--helpprintsusage: crosspad-hil run <scenario>and one option per setting, with its default. The--quickrun starts withPASS: release_gate: PASS [--quick: CI-length dry run of the gate, not a release verdict], then a table with one row per step.lslists one<scenario>_<date>_<time>folder per run underhil_logs/in the current directory, each holding areport.json.If not: exit code
2withsounddeviceorbleakin the output: audio or BLE scenarios say sounddevice is unavailable or bleak is not installed. Exit code1: the lines under the table name the failed step; read its report as in smoke test prints MISSING MARKER, FATAL or BOOT LOOP. -
Optional: release the shared board
Leave the board as you found it: the firmware
bench statusshowed before your claim (see Update firmware over USB (OTA)), the app, the kit and the USB mode. Then give the board back with a note of what is on it now.Terminal window crosspad-hil bench release "<what is on the board now>"crosspad-hil bench statusYou should see:
<name> released dev_xxxx, then a status whose Now line reads- freeand whoseOn the board:line shows your note.If not:
holds no lease on dev_xxxx and is not queued for it: nobody holds the board any more, because your lease expired (30 minutes without a board command) or was already released; claim it again and release it with your note.BENCH_BUSYwithyou (<other name>) may not run bench.release: release with the holder name you claimed with. See the board is claimed by another holder.
Check it worked
Section titled “Check it worked”crosspad-hil run smoke; echo $?crosspad-hil run smoke; $LASTEXITCODEThe output contains PASS: HIL SMOKE: PASS and the last line is 0. The report is under hil_logs/smoke_<date>_<time>/report.json.
From Claude Code (optional)
Section titled “From Claude Code (optional)”Register crosspad-mcp with Claude Code with node, as below:
npm i --prefix ~/.local/crosspad-mcp crosspad-mcp-server@latestHIL_PY=~/CrossPad/.venv/bin/pythonclaude mcp add -s user crosspad --env CROSSPAD_HIL_PYTHON=$HIL_PY -- node ~/.local/crosspad-mcp/node_modules/crosspad-mcp-server/dist/index.jsnpm i --prefix $HOME\.local\crosspad-mcp crosspad-mcp-server@latestclaude mcp add -s user crosspad --env CROSSPAD_HIL_PYTHON=C:\CrossPad\firmware\.venv\Scripts\python.exe -- node $HOME\.local\crosspad-mcp\node_modules\crosspad-mcp-server\dist\index.jsThen run HIL from Claude:
crosspad_toolsets action=enable toolset=hilcrosspad_hil_run action=run scenario=smokeThe run is a background task; Claude follows it with crosspad_task, and a pass reports passed: true. If Claude shows no crosspad tools, see Claude shows no crosspad tools; if the HIL tools cannot start, see HIL tools say crosspad-hil is missing.
With a reserved shared board, ask Claude to run the crosspad-hil bench and crosspad-hil run commands from this page in the terminal. The crosspad_bench_claim, crosspad_bench_release and crosspad_bench_status tools are available in an upcoming crosspad-mcp update.
The board is claimed by another holder
Section titled “The board is claimed by another holder”Cause: Someone else holds the board’s lease, so bench claim exits with code 1 and … is claimed by …; you are #N in the queue, and other commands stop with error: BENCH_BUSY (exit code 2).
Fix: Check who holds it, wait, and claim again; then set CROSSPAD_BENCH_HOLDER to the name you claimed with, because a command without a holder is refused even on your own lease (this caller (no holder given)).
crosspad-hil bench statuscrosspad-hil bench claim --holder <name>export CROSSPAD_BENCH_HOLDER=<name>crosspad-hil bench statuscrosspad-hil bench claim --holder <name>$env:CROSSPAD_BENCH_HOLDER = "<name>"If bench release says holds no lease on … and is not queued for it, nobody holds the board any more: your lease expired (30 minutes without a board command) or was already released. Claim it again and release it with your note. If it stops with BENCH_BUSY and you (<other name>) may not run bench.release, you are releasing under another name while your lease is live: release with the name you claimed with.