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.6.tar.gz (58.8 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.6-py3-none-any.whl (55.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nff-0.2.6.tar.gz
  • Upload date:
  • Size: 58.8 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.6.tar.gz
Algorithm Hash digest
SHA256 817be9f2be8e18c22d45d980de800adc02bb49c3761c08f61d715355642bc8c1
MD5 7984b2baf49e52e76d7ed95fd9cface9
BLAKE2b-256 94b09aea67eb91671aa0d885765a13cabdaebd8643e995d96217af0dfc5a2bb7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nff-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 55.6 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.6-py3-none-any.whl
Algorithm Hash digest
SHA256 e5817ecf5ab60204a9fce8f35b689cf1bb8c7d642e7df74e0028c4bf629cd035
MD5 7db7b8758411d3a9e220479ffe4cd0f0
BLAKE2b-256 28517f04b6c5270859b7a2405f0e304ef629e41708b5e868c265a273813ca4ef

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