/esp-pio-handling
PlatformIO lifecycle for ESP32 firmware: platformio.ini, environment selection, build, upload and serial monitor, on local USB or through the workbench. Covers what differs from ESP-IDF — the .pio/build layout, the boot_app0 image an Arduino-framework build needs, and RFC2217
$ npx -y skills add SensorsIot/Embedded-AI-Harness --skill esp-pio-handling --agent claude-codeHow 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
/esp-pio-handling
Context preview
The summary Claude sees to decide when to auto-load this skill.
PlatformIO lifecycle for ESP32 firmware: platformio.ini, environment selection, build, upload and serial monitor, on local USB or through the workbench. Covers what differs from ESP-IDF — the .pio/build layout, the boot_app0 image an Arduino-framework build needs, and RFC2217
SKILL.md
esp-pio-handling.SKILL.mdname: esp-pio-handling
description: >
PlatformIO lifecycle for ESP32 firmware: platformio.ini, environment
selection, build, upload and serial monitor, on local USB or through the
testbench. Covers what differs from ESP-IDF — the .pio/build layout, the
boot_app0 image an Arduino-framework build needs, and RFC2217 upload ports.
Triggers on "pio", "platformio", "pio run", "pio upload", "platformio.ini",
"lib_deps", "Arduino framework".
PlatformIO Handling
Build, upload and monitor a PlatformIO ESP32 project, locally or through the testbench.
**The testbench does not care which build system produced the image.** Slot discovery, `/api/flash`, GPIO download mode, crash-loop and flapping recovery are the same either way and live in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). This skill covers only what PlatformIO does differently. When something goes wrong with the *bench* rather than the build, read that skill.
Step 1: Detect environment
Same as [`esp-idf-handling`](../esp-idf-handling/SKILL.md): if `/api/info` answers, use the testbench; if nothing answers, flash over local USB. The discovery script is shared — `.claude/skills/esp-idf-handling/discover-testbench.py --hosts`.
Step 2: Build
pio run # default environment
pio run -e esp32dev # one environment
pio run -t clean
With several environments in `platformio.ini`, ask which to build rather than guessing — they often target different boards. After a build, read the size report, and check `lib_deps` when a header is missing rather than installing libraries globally.
Step 3a: Upload — local USB
pio device list
pio run -t upload
pio run -e esp32dev -t upload
pio run -t upload && pio device monitor
Step 3b: Upload — testbench
Prefer `POST /api/flash` over driving esptool through RFC2217, for the reasons in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). What differs here is where the images are and which ones exist.
PlatformIO writes them to `.pio/build/<env>/`, and there is **no `flash_args`**, so use the explicit `bin@<offset>` form:
cd .pio/build/<env>
curl -s -X POST $TESTBENCH_URL/api/flash \
-F slot=SLOT1 -F chip=esp32 \
-F 'bin@0x1000=@bootloader.bin' \
-F 'bin@0x8000=@partitions.bin' \
-F 'bin@0x10000=@firmware.bin' | jq .
**An Arduino-framework build also needs `boot_app0.bin` at `0xe000`.** It is not in `.pio/build/` — it ships inside the framework package, under `~/.platformio/packages/framework-arduinoespressif32*/tools/partitions/`. Without it the bootloader has no OTA-selection data and boots the wrong slot after an update. An ESP-IDF project has no equivalent, which is why this step is not simply a pointer.
`testbench-flash.py` beside this skill collects all four and posts them:
pio run
python3 .claude/skills/esp-pio-handling/testbench-flash.py \
--host <bench>:8080 --slot SLOT3 --chip esp32
Offsets: classic ESP32 → `0x1000`, C3/S3/C6/H2 → `0x0`. Fields and response: [FSD Appendix D.9](../../../docs/Harness-FSD.md#d9-flashing-usb--ota).
Fallback: RFC2217 upload (LAN clients only)
upload_port = rfc2217://<bench>:4001
monitor_port = rfc2217://<bench>:4001
pio run -t upload --upload-port 'rfc2217://<bench>:4001?ign_set_control'
`?ign_set_control` is required: PlatformIO's esptool drives DTR/RTS, and over RFC2217 each one is a network roundtrip.
Step 4: Monitor
pio device monitor # local
pio device monitor --port 'rfc2217://<bench>:4001?ign_set_control'
For pattern matching, UDP logs, or watching a device whose USB port is occupied, use [`testbench-logging`](../testbench-logging/SKILL.md).
Troubleshooting
Bench-side problems — `absent`, `flapping`, `download_mode`, crash loops — are in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). PlatformIO-specific:
| Issue | Fix | |-------|-----| | No device found | `pio device list`; check the cable | | Permission denied | `sudo usermod -a -G dialout $USER`, then re-login | | Upload timeout on local USB | Enter boot mode: hold **BOOT**, press **RESET**, release **RESET** then **BOOT** | | Port busy | Another terminal holds the same RFC2217 port | | `Wrong boot mode detected (0x13)` | Classic ESP32 behind a USB-serial bridge (CP2102/CH340/CH9102) — its external auto-reset cannot be driven through the proxy. Flash via `POST /api/flash` (Step 3b). Native-USB C3/S3/C6/H2 strap internally and are unaffected | | Wrong firmware runs after an OTA | `boot_app0.bin` was not flashed — see Step 3b |
Read more
name: esp-pio-handling description: > PlatformIO lifecycle for ESP32 firmware: platformio.ini, environment selection, build, upload and serial monitor, on local USB or through the testbench. Covers what differs from ESP-IDF — the .pio/build layout, the boot_app0 image an Arduino-framework build needs, and RFC2217 upload ports. Triggers on "pio", "platformio", "pio run", "pio upload", "platformio.ini", "lib_deps", "Arduino framework".
PlatformIO Handling
Build, upload and monitor a PlatformIO ESP32 project, locally or through the testbench.
**The testbench does not care which build system produced the image.** Slot discovery, `/api/flash`, GPIO download mode, crash-loop and flapping recovery are the same either way and live in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). This skill covers only what PlatformIO does differently. When something goes wrong with the *bench* rather than the build, read that skill.
Step 1: Detect environment
Same as [`esp-idf-handling`](../esp-idf-handling/SKILL.md): if `/api/info` answers, use the testbench; if nothing answers, flash over local USB. The discovery script is shared — `.claude/skills/esp-idf-handling/discover-testbench.py --hosts`.
Step 2: Build
pio run # default environment pio run -e esp32dev # one environment pio run -t clean
With several environments in `platformio.ini`, ask which to build rather than guessing — they often target different boards. After a build, read the size report, and check `lib_deps` when a header is missing rather than installing libraries globally.
Step 3a: Upload — local USB
pio device list pio run -t upload pio run -e esp32dev -t upload pio run -t upload && pio device monitor
Step 3b: Upload — testbench
Prefer `POST /api/flash` over driving esptool through RFC2217, for the reasons in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). What differs here is where the images are and which ones exist.
PlatformIO writes them to `.pio/build/<env>/`, and there is **no `flash_args`**, so use the explicit `bin@<offset>` form:
cd .pio/build/<env> curl -s -X POST $TESTBENCH_URL/api/flash \ -F slot=SLOT1 -F chip=esp32 \ -F 'bin@0x1000=@bootloader.bin' \ -F 'bin@0x8000=@partitions.bin' \ -F 'bin@0x10000=@firmware.bin' | jq .
**An Arduino-framework build also needs `boot_app0.bin` at `0xe000`.** It is not in `.pio/build/` — it ships inside the framework package, under `~/.platformio/packages/framework-arduinoespressif32*/tools/partitions/`. Without it the bootloader has no OTA-selection data and boots the wrong slot after an update. An ESP-IDF project has no equivalent, which is why this step is not simply a pointer.
`testbench-flash.py` beside this skill collects all four and posts them:
pio run python3 .claude/skills/esp-pio-handling/testbench-flash.py \ --host <bench>:8080 --slot SLOT3 --chip esp32
Offsets: classic ESP32 → `0x1000`, C3/S3/C6/H2 → `0x0`. Fields and response: [FSD Appendix D.9](../../../docs/Harness-FSD.md#d9-flashing-usb--ota).
Fallback: RFC2217 upload (LAN clients only)
upload_port = rfc2217://<bench>:4001 monitor_port = rfc2217://<bench>:4001
pio run -t upload --upload-port 'rfc2217://<bench>:4001?ign_set_control'
`?ign_set_control` is required: PlatformIO's esptool drives DTR/RTS, and over RFC2217 each one is a network roundtrip.
Step 4: Monitor
pio device monitor # local pio device monitor --port 'rfc2217://<bench>:4001?ign_set_control'
For pattern matching, UDP logs, or watching a device whose USB port is occupied, use [`testbench-logging`](../testbench-logging/SKILL.md).
Troubleshooting
Bench-side problems — `absent`, `flapping`, `download_mode`, crash loops — are in [`esp-idf-handling`](../esp-idf-handling/SKILL.md). PlatformIO-specific:
| Issue | Fix | |-------|-----| | No device found | `pio device list`; check the cable | | Permission denied | `sudo usermod -a -G dialout $USER`, then re-login | | Upload timeout on local USB | Enter boot mode: hold **BOOT**, press **RESET**, release **RESET** then **BOOT** | | Port busy | Another terminal holds the same RFC2217 port | | `Wrong boot mode detected (0x13)` | Classic ESP32 behind a USB-serial bridge (CP2102/CH340/CH9102) — its external auto-reset cannot be driven through the proxy. Flash via `POST /api/flash` (Step 3b). Native-USB C3/S3/C6/H2 strap internally and are unaffected | | Wrong firmware runs after an OTA | `boot_app0.bin` was not flashed — see Step 3b |
Other skills on embedded-ai-harness.
- /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 the next act, design and declare tests, dispatch code/flash/verify, correct until the tests run clean. Owns the test plan, test design, audit,
Open skill - /commission
Phase 2 of AI Closed-Loop Programming — Commissioning: prove the project's OWN never-seen-working parts (its board, its wiring, its peers/simulators), so that a failing test means the code and not the setup. The workbench itself is never commissioned by a project — its quality
Open skill - /define
Phase 0 of AI Closed-Loop Programming — Definition: engineers the WHAT the loop converges on. Writes and evolves the FSD — atomic, falsifiable, provenance-tagged requirements each carrying its verification contract — plus architecture, data model, interface definitions, state
Open skill - /esp-idf-handling
Complete ESP-IDF lifecycle: project setup, build, flash, monitor, and OTA. Automatically detects whether a workbench is available or the device is connected locally via USB. Covers sdkconfig, partition tables, esptool, RFC2217 remote flashing, GPIO download mode, OTA updates,
Open skill - /grill-me
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
Open skill - /harness
Phase 1 of AI Closed-Loop Programming — harness the AI for a project: the one-time setup that straps the AI to this particular load so the loop can run. Sequences Definition (via /define) when no FSD exists, then installs the three planes, the testing standard, the test plan
Open skill

