Skip to content

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).

  1. Activate the crosspad-hil venv

    This puts the crosspad-hil command from your project’s venv on the PATH for this terminal. Used the one-line installer? Skip this step: crosspad-hil already works.

    Terminal window
    . ~/CrossPad/.venv/bin/activate

    You should see: (.venv) at the start of your prompt.

    If not: crosspad-hil install fails

  2. Check the setup and list boards

    doctor checks Python, the libraries crosspad-hil needs, the serial ports and (on Linux) your serial group. devices lists each CrossPad with its ports.

    Terminal window
    crosspad-hil doctor
    crosspad-hil devices

    You should see: [ok] rows from doctor, one of them naming your board, then one row per board from devices:

    [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 / UAC2
    dev_xxxx default /dev/ttyACM1 /dev/ttyACM0 …

    On Windows the ports show as COMx. Note your board’s dev_xxxx id.

    If not: a [!!] import: row: crosspad-hil install fails. [!!] serial-group: permission denied on the serial port. [!!] devices or no CrossPad found: smoke test stops with NO_DEVICE or PORT_BUSY.

  3. 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 status
    crosspad-hil bench claim --holder <name>
    export CROSSPAD_BENCH_HOLDER=<name>
    crosspad-hil cdc USB_GUARD

    The variable makes later crosspad-hil commands in this terminal run as you; without it the board answers BENCH_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, then USB_GUARD: 0.

    USB_GUARD: 0 means the board never stops at the USB serial mode? prompt, so tests run without a tap on the screen. On USB_GUARD: 1, run crosspad-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: claim exits 1 with … 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; run crosspad-hil usb-mode set default and press Allow on the board once.

  4. Run the smoke test as JSON

    --json prints the whole report as one JSON object, for scripts and CI.

    Terminal window
    crosspad-hil run smoke --json

    You should see: one JSON line containing "passed": true and "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.

  5. Run another scenario and find its report

    release_gate runs a broad set of checks in one go; stability runs the board for hours; latency and perf are measurements. Each lists its options with --help.

    Terminal window
    crosspad-hil run release_gate --help
    crosspad-hil run release_gate --quick
    crosspad-hil run stability --help
    crosspad-hil run latency --help
    ls hil_logs/

    --quick runs a short version of the checks; it stops at the USB serial prompt if the guard is on. stability runs until you stop it unless you pass --duration-hours.

    You should see: --help prints usage: crosspad-hil run <scenario> and one option per setting, with its default. The --quick run starts with PASS: release_gate: PASS [--quick: CI-length dry run of the gate, not a release verdict], then a table with one row per step. ls lists one <scenario>_<date>_<time> folder per run under hil_logs/ in the current directory, each holding a report.json.

    If not: exit code 2 with sounddevice or bleak in the output: audio or BLE scenarios say sounddevice is unavailable or bleak is not installed. Exit code 1: the lines under the table name the failed step; read its report as in smoke test prints MISSING MARKER, FATAL or BOOT LOOP.

  6. Optional: release the shared board

    Leave the board as you found it: the firmware bench status showed 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 status

    You should see: <name> released dev_xxxx, then a status whose Now line reads - free and whose On 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_BUSY with you (<other name>) may not run bench.release: release with the holder name you claimed with. See the board is claimed by another holder.

Terminal window
crosspad-hil run smoke; echo $?

The output contains PASS: HIL SMOKE: PASS and the last line is 0. The report is under hil_logs/smoke_<date>_<time>/report.json.

Register crosspad-mcp with Claude Code with node, as below:

Terminal window
npm i --prefix ~/.local/crosspad-mcp crosspad-mcp-server@latest
HIL_PY=~/CrossPad/.venv/bin/python
claude mcp add -s user crosspad --env CROSSPAD_HIL_PYTHON=$HIL_PY -- node ~/.local/crosspad-mcp/node_modules/crosspad-mcp-server/dist/index.js

Then run HIL from Claude:

crosspad_toolsets action=enable toolset=hil
crosspad_hil_run action=run scenario=smoke

The 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.

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)).

Terminal window
crosspad-hil bench status
crosspad-hil bench claim --holder <name>
export 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.