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
18018 skills
Install
$ npx -y skills add SensorsIot/Embedded-AI-Harness --skill testbench-wifi --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/testbench-wifi

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

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

Get the whole plugin
Stats
181
Stars
54
Forks
Maintained
Maintenance
Python
Language
MIT
License
1mo ago
Last commit
8mo ago
Created

Repo: SensorsIot/Embedded-AI-Harness

Other skills on embedded-ai-harness.