Skip to content
Development
Skill

/testbench-wifi

Use this skill whenever you need to control the testbench's WiFi radio for testing — starting a SoftAP for DUTs to connect to, joining a DUT's captive portal as a station, scanning for networks, relaying HTTP requests to devices on the WiFi network, or provisioning DUT WiFi

From plugin
embedded-ai-harness
27918 skills

Claude Code

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

SKILL.md

testbench-wifi.SKILL.md
name: testbench-wifi
description: Use this skill whenever you need to control the testbench's WiFi radio for testing — starting a SoftAP for DUTs to connect to, joining a DUT's captive portal as a station, scanning for networks, relaying HTTP requests to devices on the WiFi network, or provisioning DUT WiFi credentials. Essential for any test that involves WiFi connectivity, captive portal flows, or HTTP communication with devices on the isolated test network (192.168.4.x). Triggers on "wifi", "AP", "station", "scan", "provision", "captive portal", "enter-portal", "HTTP relay", "wifi test", "SoftAP".

ESP32 WiFi & Provisioning

Base URL: `$TESTBENCH_URL` — see Step 0

Step 0: Point at a bench

There are several benches and their addresses move, so nothing here writes one down. `$BENCH` is not usable either — a container cannot resolve mDNS. Discover the bench and export its URL:

export TESTBENCH_URL=$(sudo python3 .claude/skills/esp-idf-handling/discover-testbench.py \
                         --url --name <bench-hostname>)
curl -s "$TESTBENCH_URL/api/info"        # confirm before anything else

`--url` refuses to guess when more than one bench answers, so `--name` is required whenever a second bench is powered on. `TESTBENCH_URL` is the same variable `pytest --wt-url` falls back to.

Operating Modes

The testbench has two WiFi operating modes:

| Mode | wlan0 usage | WiFi endpoints | |------|-------------|----------------| | **wifi-testing** (default) | Test instrument (AP/STA/scan) | Active | | **serial-interface** | Joins WiFi for additional LAN | Disabled |

# Check current mode
curl $TESTBENCH_URL/api/wifi/mode

# Switch to wifi-testing mode
curl -X POST $TESTBENCH_URL/api/wifi/mode \
  -H 'Content-Type: application/json' \
  -d '{"mode": "wifi-testing"}'

# Switch to serial-interface mode (joins a WiFi network)
curl -X POST $TESTBENCH_URL/api/wifi/mode \
  -H 'Content-Type: application/json' \
  -d '{"mode": "serial-interface", "ssid": "MyNetwork", "pass": "password"}'

Endpoints

Request and response shapes: [FSD Appendix D.4](../../../docs/Harness-FSD.md#d4-wifi-instrument), plus `/api/enter-portal` in [D.2](../../../docs/Harness-FSD.md#d2-serial-management).

**AP and STA are mutually exclusive** — one radio. Starting the AP drops any STA association and vice versa, so a test that needs both must sequence them.

WiFi AP (Access Point)

# Start AP
curl -X POST $TESTBENCH_URL/api/wifi/ap_start \
  -H 'Content-Type: application/json' \
  -d '{"ssid": "TestAP", "pass": "testpass123", "channel": 6}'

# Check AP status and connected clients
curl $TESTBENCH_URL/api/wifi/ap_status

# Stop AP
curl -X POST $TESTBENCH_URL/api/wifi/ap_stop

AP and STA are mutually exclusive — starting one stops the other.

WiFi STA (Station)

# Join a network
curl -X POST $TESTBENCH_URL/api/wifi/sta_join \
  -H 'Content-Type: application/json' \
  -d '{"ssid": "MyNetwork", "pass": "password", "timeout": 15}'

# Disconnect
curl -X POST $TESTBENCH_URL/api/wifi/sta_leave

WiFi Scan

curl $TESTBENCH_URL/api/wifi/scan

WiFi On/Off Testing

To test a device's behavior when WiFi connectivity is lost and restored:

# 1. Ensure device is connected to testbench AP
curl -X POST $TESTBENCH_URL/api/wifi/ap_start \
  -H 'Content-Type: application/json' \
  -d '{"ssid": "TestAP", "pass": "testpass123"}'

# 2. Stop AP — device loses WiFi
curl -X POST $TESTBENCH_URL/api/wifi/ap_stop

# 3. Monitor device behavior (serial or UDP logs)
# ... wait for desired duration ...

# 4. Restart AP — device should reconnect
curl -X POST $TESTBENCH_URL/api/wifi/ap_start \
  -H 'Content-Type: application/json' \
  -d '{"ssid": "TestAP", "pass": "testpass123"}'

# 5. Wait for device to reconnect
curl "$TESTBENCH_URL/api/wifi/events?timeout=30"

HTTP Relay

**IMPORTANT:** Devices on the testbench AP (192.168.4.x) are NOT directly reachable from the development machine. Always use this relay to make HTTP requests to device endpoints (e.g. `/status`, `/ota`). The response body is base64-encoded — decode it to get the actual JSON.

# GET request to device
curl -X POST $TESTBENCH_URL/api/wifi/http \
  -H 'Content-Type: application/json' \
  -d '{"method": "GET", "url": "http://192.168.4.2/status", "timeout": 10}'

# POST with base64-encoded body
BODY=$(echo -n '{"key":"value"}' | base64)
curl -X POST $TESTBENCH_URL/api/wifi/http \
  -H 'Content-Type: application/json' \
  -d "{\"method\": \"POST\", \"url\": \"http://192.168.4.2/config\", \"headers\": {\"Content-Type\": \"application/json\"}, \"body\": \"$BODY\", \"timeout\": 10}"

# Decode the base64 response body
curl -s -X POST $TESTBENCH_URL/api/wifi/http \
  -H 'Content-Type: application/json' \
  -d '{"method": "GET", "url": "http://192.168.4.x:8080/endpoint", "timeout": 10}' \
  | python3 -c "import json,sys,base64; r=json.load(sys.stdin); print(base64.b64decode(r['body']).decode())"

WiFi Events

Long-poll for STA_CONNECT / STA_DISCONNECT events:

curl "$TESTBENCH_URL/api/wifi/events?timeout=30"

Enter-Portal (Captive Portal Provisioning)

Ensures a device is connected to the testbench AP. If the device has no WiFi credentials, the testbench provisions it via the device's captive portal.

curl -X POST $TESTBENCH_URL/api/enter-portal \
  -H 'Content-Type: application/json' \
  -d '{"portal_ssid": "<DUT-portal-SSID>", "ssid": "TestAP", "password": "testpass123"}'

| Field | Description | |-------|-------------| | `portal_ssid` | Device's captive portal SoftAP name | | `ssid` | Testbench's AP SSID (filled into the device's portal form) | | `password` | Testbench's AP password (filled into the device's portal form) |

**Procedure:** 1. Starts testbench AP (using `ssid`/`password`) if not already running 2. Waits for the device to connect (it may already have credentials) 3. If device doesn't connect, testbench joins the device's captive portal So

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

Which coding agents can use testbench-wifi?
testbench-wifi 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.