Skip to content
Development
Skill

/testbench-debug

Remote GDB debugging of ESP32 devices via the testbench. Covers USB JTAG (C3/S3), dual-USB (S3 two-port), and ESP-Prog (FT2232H) approaches. Use when setting up JTAG debugging, connecting GDB, configuring OpenOCD, or troubleshooting debug connections. Triggers on "GDB", "JTAG",

From plugin
embedded-ai-harness
27918 skills

Claude Code

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

SKILL.md

testbench-debug.SKILL.md
name: testbench-debug
description: Remote GDB debugging of ESP32 devices via the testbench. Covers USB JTAG (C3/S3), dual-USB (S3 two-port), and ESP-Prog (FT2232H) approaches. Use when setting up JTAG debugging, connecting GDB, configuring OpenOCD, or troubleshooting debug connections. Triggers on "GDB", "JTAG", "debug", "OpenOCD", "breakpoint", "ESP-Prog", "step through", "debug session".

Testbench GDB Debugging

Remote GDB debugging of ESP32 devices through the testbench Pi. OpenOCD runs on the Pi, GDB connects from the container over TCP.

**Architecture:**

[Container]              [Pi (testbench)]           [ESP32]
  GDB ──── TCP :3333 ────── OpenOCD ──── USB JTAG ──────── CPU
                             (or)
                           OpenOCD ──── ESP-Prog ── JTAG pins

---

Three Approaches

| Approach | Chips | Extra Hardware | Serial During Debug | |----------|-------|:-:|:-:| | **USB JTAG** (FR-024) | C3, S3 (native USB only) | None | Yes | | **Dual-USB** (FR-025) | S3 (two USB connectors) | None | Yes + app USB | | **ESP-Prog** (FR-026) | All ESP32 variants | ESP-Prog (~$15) + cable | Yes |

---

Auto-Debug (Zero Config)

OpenOCD starts **automatically** when a device is plugged in or at boot. No API call needed.

**How it works:** 1. Device hotplugged → serial proxy starts → auto-detect tries OpenOCD configs 2. Chip identified via JTAG TAP ID → OpenOCD starts on the assigned GDB port 3. If USB JTAG fails (e.g. classic ESP32), falls back to ESP-Prog probe if available 4. GDB port reported in `/api/devices` response

**Check debug status:**

curl $TESTBENCH_URL/api/devices
# Look for: "debugging": true, "debug_chip": "esp32s3", "debug_gdb_port": 3335

**Manual override (optional -- only needed to force-stop or force-start):**

# Force stop (won't auto-restart until next hotplug)
curl -X POST $TESTBENCH_URL/api/debug/stop -d '{}'

# Force start with specific chip
curl -X POST $TESTBENCH_URL/api/debug/start \
  -d '{"chip": "esp32c3"}'

---

JTAG Reset (Preferred When Available)

When a debug session is active, the testbench automatically uses **JTAG reset** instead of DTR/RTS serial reset. This is transparent — the same `/api/serial/reset` API is used.

**Why JTAG reset is better:**

  • No USB re-enumeration (device node stays stable)
  • No flapping risk
  • No 2-second boot delay
  • Works even when serial port is unresponsive
  • Can halt the CPU to stop boot loops

**Via OpenOCD telnet (manual):**

# Soft reset (chip reboots normally)
echo "reset run" | nc "$BENCH" 4446

# Halt CPU (stops execution immediately)
echo "halt" | nc "$BENCH" 4446

# Reset and halt (for debugging from first instruction)
echo "reset halt" | nc "$BENCH" 4446

**Via testbench API (automatic):**

# Uses JTAG reset when debug session is active, DTR/RTS otherwise
curl -X POST $TESTBENCH_URL/api/serial/reset \
  -H "Content-Type: application/json" -d '{"slot": "SLOT1"}'

**Availability:** | Scenario | JTAG reset? | |----------|:-:| | C3/S3/C6/H2 with native USB | Yes (auto-started) | | Classic ESP32 + ESP-Prog | Yes (when probe wired) | | Classic ESP32 without ESP-Prog | No — DTR/RTS fallback |

---

Prerequisites

OpenOCD is pre-installed on the Pi:

/usr/local/bin/openocd-esp32           # binary
/usr/local/share/openocd-esp32/scripts/ # config files

If missing, run `install.sh` or manually:

# On Pi (aarch64)
wget https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20260304/openocd-esp32-linux-arm64-0.12.0-esp32-20260304.tar.gz
tar xzf openocd-esp32-linux-arm64-*.tar.gz
sudo cp openocd-esp32/bin/openocd /usr/local/bin/openocd-esp32
sudo mkdir -p /usr/local/share/openocd-esp32
sudo cp -r openocd-esp32/share/openocd/scripts /usr/local/share/openocd-esp32/scripts

---

1. USB JTAG (ESP32-C3 / ESP32-S3 with Native USB)

Identify the device

The device must show as `303a:1001` (Espressif USB JTAG/serial debug unit). Boards with CH340/CP2102 UART bridges do NOT support USB JTAG.

# Check from Pi
ssh pi@<bench-hostname> "lsusb -d 303a:1001"
# → Bus 001 Device 066: ID 303a:1001 Espressif USB JTAG/serial debug unit

USB interface layout (no kernel unbind needed):

  • Interface 0: CDC-ACM serial → `/dev/ttyACM*` (RFC2217 proxy)
  • Interface 1: CDC Data
  • Interface 2: Vendor Specific (**unclaimed** — OpenOCD uses via libusb)

Chip auto-detection

USB PID `303a:1001` is the same for C3 and S3. The JTAG TAP ID identifies the chip:

| TAP ID | Chip | Architecture | Config | |--------|------|-------------|--------| | `0x00005c25` | ESP32-C3 | RISC-V single-core | `esp32c3-builtin.cfg` | | `0x00010c25` | ESP32-H2 | RISC-V single-core | `esp32h2-builtin.cfg` | | `0x0000dc25` | ESP32-C6 | RISC-V single-core | `esp32c6-builtin.cfg` | | `0x120034e5` | ESP32-S3 | Xtensa dual-core | `esp32s3-builtin.cfg` |

Start OpenOCD manually (for testing)

# ESP32-C3
ssh pi@<bench-hostname> "openocd-esp32 -s /usr/local/share/openocd-esp32/scripts \
  -f board/esp32c3-builtin.cfg \
  -c 'gdb port 3333' -c 'telnet port 4444' -c 'bindto 0.0.0.0'"

# ESP32-S3
ssh pi@<bench-hostname> "openocd-esp32 -s /usr/local/share/openocd-esp32/scripts \
  -f board/esp32s3-builtin.cfg \
  -c 'gdb port 3333' -c 'telnet port 4444' -c 'bindto 0.0.0.0'"

Connect GDB from container

# C3 (RISC-V)
riscv32-esp-elf-gdb build/project.elf \
  -ex "target extended-remote $BENCH:3333" \
  -ex "monitor reset halt"

# S3 (Xtensa)
xtensa-esp32s3-elf-gdb build/project.elf \
  -ex "target extended-remote $BENCH:3333" \
  -ex "monitor reset halt"

Connect via OpenOCD telnet (quick test)

import socket, time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((BENCH, 4444))
time.sleep(0.5)
banner = s.recv(4096)  # telnet negotiation + "Open On-Chip Debugger"
s.sendall(b'halt\n')
time.sleep(1)
print(s.recv(4096).decode('latin-1'))  # "Target halted, PC=0x..."
s.sendall(b'resume\n')
time.sle
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.

skills10d309k
@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,…

ecc11d293k
@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-debug.

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