Skip to content
Development
Skill

/testbench-install

Use this skill whenever the testbench Pi itself needs to be built, installed, updated, or have code deployed to it — a fresh SD card, a first `install.sh` run, pushing a changed controller module to a running bench, or diagnosing why a change had no effect. This is about the

From plugin
embedded-ai-harness
27918 skills

Claude Code

$ npx -y skills add SensorsIot/Embedded-AI-Harness --skill testbench-install --agent claude-code

SKILL.md

testbench-install.SKILL.md
name: testbench-install
description: Use this skill whenever the testbench Pi itself needs to be built, installed, updated, or have code deployed to it — a fresh SD card, a first `install.sh` run, pushing a changed controller module to a running bench, or diagnosing why a change had no effect. This is about the machine, not the instruments: the other testbench-* skills drive a bench that already works. Triggers on "install the testbench", "set up the Pi", "deploy", "push this to the bench", "my change didn't take effect", "rebuild the SD card", "update the portal", "install.sh", "systemctl restart rfc2217-portal".

Setting up and deploying the testbench

The one fact that explains most confusion: **the service runs from `/usr/local/bin/`, not from the git checkout.** Editing files in the repo on the Pi — or on your laptop — changes nothing until they are copied across and the service is restarted. Every "my fix didn't work" report traces back to this.

Full operator procedure lives in [`docs/Harness-User-Manual.md`](../../../docs/Harness-User-Manual.md) §2. This skill is the working procedure plus the things that only bite when you actually do it.

Pick the operation

| Situation | Do this | |---|---| | New Pi, blank SD card | **Fresh install** below | | Bench works, want the latest code + no system changes | `sudo bash install.sh --update` | | Changed one module, want it live now | **Single-file deploy** below | | Changed a config default in `pi/config/` | Fresh-install path won't overwrite it — see *Config files are never overwritten* |

Fresh install

git clone https://github.com/SensorsIot/Embedded-AI-Harness.git
cd Embedded-AI-Harness/pi
sudo bash install.sh

`install.sh` is idempotent and does eight things: apt packages, standing down the services it manages dynamically (`hostapd` is masked; `dnsmasq` and `mosquitto` are disabled — the portal starts them itself, so leaving them enabled fights it), directories, the Python modules into `/usr/local/bin/`, helper scripts, config defaults, systemd + udev rules, then enable and start.

It fetches `openocd-esp32` from GitHub releases and installs it as `/usr/local/bin/openocd-esp32`, alongside — not replacing — Debian's `openocd`.

**It runs under `set -e`, and the script copy comes before systemd and udev.** So a single missing file aborts the install after the packages are in but before the service exists, and the output ends on a bare `cp: cannot stat` with no summary. Read the last line rather than assuming a long successful-looking log means it finished; `curl /api/info` is the only proof.

**The API contract is [FSD Appendix D](../../../docs/Harness-FSD.md#appendix-d-http-api--mcp-reference).** Every endpoint the bench serves is listed there with its request and response shape. Read it rather than the portal source: the source has carried a dead duplicate handler whose documented form the live one rejects, so guessing from code has already cost a debugging cycle.

**A fresh install exercises code that `--update` never reaches.** An installer bug can therefore sit undiscovered indefinitely on benches that were built before it was introduced — a file deleted from `pi/` but still named in `install.sh` breaks every new bench and no existing one.

Verify over HTTP — never by reading files over SSH:

curl -s $TESTBENCH_URL/api/info      # portal version, host info
curl -s $TESTBENCH_URL/api/devices   # every slot

If the bench has moved or you do not know its address:

sudo python3 .claude/skills/esp-idf-handling/discover-testbench.py --hosts

Then prove the subsystems actually loaded, because an import that fails on a newer Python takes only its own endpoint down and leaves the portal answering:

for ep in sdr/status siggen/status mqtt/status wifi/mode debug/probes gpio/status; do
  curl -s "$TESTBENCH_URL/api/$ep"; echo
done

Platform

The installer pins nothing and works across releases. Verified on:

| | Debian 12 bookworm | Debian 13 trixie | |---|---|---| | Python | 3.11 | 3.13 | | esptool | 4.x | 5.x |

**Python 3.13 removed `telnetlib`, `cgi`, `imp` and `distutils`.** No portal module uses them today; check before adding a dependency that might.

**esptool 5 renamed things and warns loudly.** `esptool.py` → `esptool`, `flash_id` → `flash-id`, and its output fields changed (`Chip is` → `Chip type:`, `Crystal is` → `Crystal frequency:`). The old spellings still work, so the portal keeps using them for compatibility with older benches, and anything parsing esptool output must accept both wordings.

Single-file deploy

The pattern for every module — only the destination name changes:

scp pi/portal.py pi@<bench-hostname>:/tmp/portal.py
ssh pi@<bench-hostname> 'sudo cp /tmp/portal.py /usr/local/bin/rfc2217-portal && sudo systemctl restart rfc2217-portal'

`portal.py` is the only one renamed on install (→ `rfc2217-portal`). The rest keep their filenames: `sdr_controller.py`, `wifi_controller.py`, `ble_controller.py`, `mqtt_controller.py`, `debug_controller.py`, `signal_generator.py`, `si5351.py`, `gpclk.py`, `morse.py`, `bcm_gpio.py`, `pe4302.py`, `sniffer.py`, `plain_rfc2217_server.py`.

Restarting `rfc2217-portal` is required even for a module the portal imports — Python has already loaded the old one.

Then prove it took, rather than assuming:

curl -s $TESTBENCH_URL/api/info

**SSH is for deploying code and nothing else.** Never drive the bench over SSH: every operation has an HTTP endpoint and `pytest/testbench_driver.py` wraps them all. Reaching for SSH to *do* something means the API is missing a capability — add the endpoint instead. This is a project rule, not a preference; see [`docs/Method/AI-Workflow.md`](../../../docs/Method/AI-Workflow.md#deploying-a-change-to-the-bench).

Things that only bite in practice

**Config files are never overwritten.** `install.sh` writes `/etc/rfc2217/signalgen.json` and `sdr.json` only when absent,

Read more
@sensorsiot
Ships withembedded-ai-harness

Spec to silicon, hands off. A horse is strong, fast, and willing — and useless for heavy loads until you harness it. The harness is not a part of the horse and not a part of the cart: it is the coupling that turns raw strength into pulled weight.

Stars on GitHub

Momentum

stars in total
279
Star history of SensorsIot/Embedded-AI-Harness on GitHub, a chart by star-history.com

Other skills on embedded-ai-harness.

build

build

Phase 3 of AI Closed-Loop Programming — the Build phase, and the driver of the whole loop: locate the project on the chain, name…

define

define

Phase 0 of AI Closed-Loop Programming — Definition: engineers the WHAT the loop converges on. Writes and evolves the FSD —…

grill-me

grill-me

Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about testbench-install.

Which coding agents can use testbench-install?
testbench-install installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.