Skip to main content

Claude Code IoT Bridge — connect Claude to hardware via USB

Project description

nff — Claude Code IoT Bridge

nff connects Claude Code to embedded hardware over USB — or to simulated hardware via Wokwi — with no real board required. It exposes your board as a set of MCP tools so Claude can autonomously write firmware, compile it, upload it, read serial output, and debug, all from a single conversation.

you: "Make the LED blink every 200 ms and print the state to serial"
Claude: [writes sketch] → [compiles] → [uploads to ESP32] → [reads serial] → done

you: "Simulate a servo controlled by a button, show me the circuit"
Claude: [writes sketch] → [updates diagram.json] → [nff flash --sim] → [nff wokwi run --gui] → VS Code opens with live animated circuit

Supported boards (v1): Arduino Uno · Mega · Nano · Leonardo · ESP32 (CP210x / CH340) · ESP8266 (FTDI)


Quickstart

1. Install

pip install nff

2. Install external tools

# arduino-cli (required for all workflows)
# → https://arduino.github.io/arduino-cli/latest/installation/

# wokwi-cli (required for simulation only)
# → https://github.com/wokwi/wokwi-cli/releases
# Get a free CI token at https://wokwi.com/dashboard/ci

# Board cores (install the ones you need)
arduino-cli core install esp32:esp32
arduino-cli core install arduino:avr
arduino-cli core install esp8266:esp8266

3. Real hardware — plug in your board and run init

nff init

Detects your board by USB vendor/product ID and registers the nff MCP server.

  ✓ Found: ESP32 (CP210x) on COM10
  ✓ Config written to ~/.nff/config.json
  ✓ MCP config written to ~/.claude/claude_desktop_config.json

4. Simulation only — no board needed

nff wokwi init --board esp32:esp32:esp32 --token YOUR_TOKEN

Creates wokwi.toml and diagram.json in the current directory. Edit diagram.json to add components, then compile and simulate:

nff flash --sim sketches/my_sketch --board esp32:esp32:esp32
nff wokwi run --gui        # visual simulation in VS Code
nff wokwi run              # headless, serial output only

5. Verify everything works

nff doctor

CLI Reference

Real hardware

Command Description
nff init Detect board, write config, register MCP server
nff flash <file> Compile and upload a sketch or sketch directory
nff monitor Interactive serial monitor (Ctrl+C to exit)
nff doctor Check all dependencies and configuration
nff mcp Start the MCP server (called automatically by Claude Code)
nff flash sketches/blink
nff flash sketches/blink --board esp32:esp32:esp32 --port COM3
nff flash sketches/blink --manual-reset    # for boards with broken auto-reset
nff monitor --port COM10 --baud 115200

Wokwi simulation

Command Description
nff wokwi init Scaffold wokwi.toml + diagram.json in current directory
nff flash --sim <file> Compile sketch and run headless Wokwi simulation
nff wokwi run Run simulation, stream serial output to terminal
nff wokwi run --gui Open diagram.json in VS Code and auto-start visual simulation
nff wokwi run --serial-log FILE Save serial output to file
nff wokwi run --timeout MS Set simulation timeout (default 5000 ms)
nff wokwi init --board esp32:esp32:esp32
nff flash --sim sketches/servo_button --board esp32:esp32:esp32
nff wokwi run --gui
nff wokwi run --timeout 10000 --serial-log out.txt

Visual Simulation with VS Code

Install the Wokwi VS Code extension to get an animated circuit view alongside a serial monitor panel — no browser required.

nff wokwi run --gui

This opens diagram.json as a new tab in your existing VS Code window and automatically triggers Wokwi: Start Simulator after 3 seconds. Click components (buttons, potentiometers, etc.) to interact with the running simulation.

Workflow:

Edit sketch  →  nff flash --sim  →  nff wokwi run --gui  →  click in VS Code
     ↑_____________________________|

diagram.json — Circuit Schematic

The circuit lives in diagram.json next to wokwi.toml. nff wokwi init generates a minimal single-MCU stub; add components and wiring by hand or ask Claude.

Always include the serial monitor wires:

["esp:TX0", "$serialMonitor:RX", "", []],
["esp:RX0", "$serialMonitor:TX", "", []]

Common components:

{ "type": "wokwi-led",        "id": "led1", "attrs": { "color": "red" } }
{ "type": "wokwi-pushbutton", "id": "btn1", "attrs": { "color": "blue" } }
{ "type": "wokwi-servo",      "id": "srv1", "attrs": { "minAngle": "-90", "maxAngle": "90" } }
{ "type": "wokwi-resistor",   "id": "r1",   "attrs": { "value": "220" } }

ESP32 DevKit V1 pins: esp:D<gpio> · esp:GND.1 · esp:GND.2 · esp:3V3 · esp:VIN · esp:TX0 · esp:RX0

Pushbutton wiring: one side to GPIO (btn1:1.l), other side to GND (btn1:2.l). Use INPUT_PULLUP in the sketch.


ESP32 Servo — No Library Required

Use the built-in LEDC peripheral instead of ESP32Servo. Wokwi maps its full servo range to 500 µs – 2500 µs pulses.

With 50 Hz / 16-bit resolution (period = 20 000 µs):

Angle Pulse Duty
−90° (min) 500 µs 1638
0° (center) 1500 µs 4915
+90° (max) 2500 µs 8192
ledcAttach(SERVO_PIN, 50, 16);     // ESP32 Arduino core 3.x API
ledcWrite(SERVO_PIN, 4915);        // move to center

Set "minAngle": "-90", "maxAngle": "90" in diagram.json for correct visual mapping.


MCP Tools (what Claude can call)

Once registered, Claude Code has access to these tools:

Tool What it does
list_devices() List all connected USB boards
flash(code, board?, port?) Write, compile, and upload a sketch
serial_read(duration_ms?, port?, baud?) Capture serial output for N ms
serial_write(data, port?, baud?) Send a string to the device
reset_device(port?) Toggle DTR to hardware-reset the board
get_device_info(port?) Return port, board name, FQBN, baud rate
wokwi_flash(code, board?, timeout_ms?) Compile and simulate a sketch via Wokwi
wokwi_serial_read(code, board?, duration_ms?) Compile, simulate, return serial output
wokwi_get_diagram(board) Return a minimal diagram.json stub to extend

All tools fall back to the default device in ~/.nff/config.json when port and board are omitted.


Config File

~/.nff/config.json, written by nff init and nff wokwi init, editable by hand:

{
  "version": "1",
  "default_device": {
    "port": "COM10",
    "board": "ESP32 (CP210x)",
    "fqbn": "esp32:esp32:esp32",
    "baud": 115200
  },
  "wokwi": {
    "api_token": "YOUR_TOKEN",
    "default_timeout_ms": 5000,
    "diagram_path": null
  }
}

The Wokwi token can also be set via the WOKWI_CLI_TOKEN environment variable (takes precedence over config).


Supported Boards

Board Vendor ID Product ID FQBN
Arduino Uno 2341 0043 arduino:avr:uno
Arduino Mega 2560 2341 0010 arduino:avr:mega
Arduino Leonardo 2341 0036 arduino:avr:leonardo
Arduino Nano 2341 0058 arduino:avr:nano
ESP32 (CP210x) 10c4 ea60 esp32:esp32:esp32
ESP32 (CH340) 1a86 7523 esp32:esp32:esp32
ESP8266 (FTDI) 0403 6001 esp8266:esp8266:generic

Board not listed? Open a PR — adding one is two lines of code.


Claude Skill

This project ships a /nff Claude Code skill at .claude/commands/nff.md.

Type /nff in any Claude Code session inside this project to load the full pipeline reference — simulation workflow, hardware workflow, component wiring, servo calibration, and debugging checklist — directly into context.


Repository Structure

nff/
├── nff/
│   ├── cli.py              # Click entry point — routes subcommands
│   ├── mcp_server.py       # MCP server — registers all tools for Claude
│   ├── config.py           # Read/write ~/.nff/config.json
│   ├── commands/
│   │   ├── init.py         # nff init
│   │   ├── flash.py        # nff flash [--sim]
│   │   ├── monitor.py      # nff monitor
│   │   ├── doctor.py       # nff doctor
│   │   └── wokwi.py        # nff wokwi init / run [--gui]
│   └── tools/
│       ├── boards.py       # USB vendor ID detection
│       ├── serial.py       # pyserial read/write/stream
│       ├── toolchain.py    # arduino-cli subprocess wrappers
│       └── wokwi.py        # WokwiRunner, generate_diagram, write_wokwi_toml
├── sketches/
│   ├── blink_esp32/        # LED blink example
│   └── servo_button/       # Servo + button example (LEDC, no library)
├── diagram.json            # Wokwi circuit schematic
├── wokwi.toml              # Wokwi project config (points to compiled ELF)
├── .claude/
│   └── commands/
│       └── nff.md          # /nff Claude Code skill
├── tests/
├── pyproject.toml
└── CONTRIBUTING.md

Linux: Serial Port Permissions

sudo usermod -aG dialout $USER
# then log out and back in

nff doctor detects this and prints the fix if your port is inaccessible.


License

MIT — see LICENSE.
Copyright (c) 2026 Gauthier Lechevalier

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nff-0.2.1.tar.gz (52.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nff-0.2.1-py3-none-any.whl (48.2 kB view details)

Uploaded Python 3

File details

Details for the file nff-0.2.1.tar.gz.

File metadata

  • Download URL: nff-0.2.1.tar.gz
  • Upload date:
  • Size: 52.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for nff-0.2.1.tar.gz
Algorithm Hash digest
SHA256 59648bb2f3d47ae2d9a14cf96978984a09b8727fafba20e2e43e8b4a7a522b87
MD5 544bca6a29a68bb9dccb59503f8ff298
BLAKE2b-256 5833c6f95e285877aad11b73c0dd40ae6bba0ee7ef1d950cb4ed9b75e13bd514

See more details on using hashes here.

File details

Details for the file nff-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: nff-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 48.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for nff-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3ca7fcdbfd5c72891c4739231ab9f62dfcd646eb237b11bc5ba4eee797b9112f
MD5 80b19d756227357e2265c7e2ed073525
BLAKE2b-256 d52fa72ec1d496ff6874327212470243d805c08e1bd67da88544fbbfcfa8895b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page