Automation & LLM Tools
The Jumperless V5 is built to be driven by software — by your own scripts, by desktop apps, and by LLM agents (Cursor, Claude Desktop/Code, etc.). This page is the hub for every way to control the board from outside the menu.
The full hardware/API surface is documented in the MicroPython API Reference — this page is about transport and tooling, not the individual calls.
USB port structure
The Jumperless enumerates as four USB CDC serial interfaces. On macOS they are
named JLV5portN; on Linux they are sequential /dev/ttyACMx; on Windows they
are sequential COMx.
| Port | macOS suffix | Role |
|---|---|---|
| 1 | JLV5port1 |
Main terminal, menu, > one-liner Python, single-char commands |
| 2 | JLV5port3 |
Arduino UART passthrough |
| 3 | JLV5port5 |
MicroPython Raw REPL — primary transport for scripts and the Agent Skill |
| 4 | JLV5port7 |
USBSer3 machine backchannel — :help (YAML), :cmds, read-only status verbs |
Tip
The host helper scripts/jumperless.py detect (in the Agent Skill repo)
auto-finds the Raw REPL port and caches it, so you rarely need to name ports
by hand.
Note
I will refer to these ports by their index (1st, 2nd, 3rd, 4th). macOS names them by USB interface index, and each serial port uses two interfaces (control and data), so the suffixes step by two: 1, 3, 5, 7. Linux and Windows number them sequentially.
Communication methods
1. > Python one-liners (1st / main port)
Prefix a single line of Python with > on the main terminal. Best for quick
checks and single commands.
> connect(1, 5)
> v = adc_get(0)
> print(f"V = {v:.2f}")
2. MicroPython Raw REPL (3rd port)
The canonical transport for anything beyond a one-liner: full scripts, loops,
guided flows. This is what the Agent Skill's jumperless.py and the MCP server
both speak under the hood.
import time
for i in range(10):
print(i, adc_get(0))
time.sleep(0.5)
3. Arduino tags (2nd port, via UART)
From an Arduino connected to the Jumperless headers:
Serial.print("<p>connect(1, 5)</p>"); // run Python
Serial.print("<j>n</j>"); // menu command
4. Single-character commands (1st port)
Raw characters sent to port 1 trigger immediate menu actions — fast state
dumps, board clears, etc. (J = JSON state, x = clear, n = list nets, …).
5. USBSer3 backchannel (4th port) — read-only telemetry
A dedicated machine port that never blocks and never changes state. It speaks two command forms:
- Single characters dispatch instantly:
Afull status,VADC+current,GGPIO,Nnets,KYAML. :verbs read a newline-terminated line::gpio:s,:leds,:crossbar,:adc,:yaml,:every:1ms:100:gpio:s, …
Every command is gated by an access tag; only read-only queries run here, so an agent can poll the board freely without risk of mutating it.
Self-describing :help (YAML)
The backchannel describes itself. Send :help (or a bare :) and it emits a
YAML document — readable straight in a serial terminal, and parseable with
yaml.safe_load():
---YAML_HELP_START---
ser3_help: 1
protocol:
single_char: "Instant dispatch; no line collection. Multi-char modifiers use ':' verbs."
...
verbs:
- name: "gpio:s"
summary: "48-pin GPIO state snapshot"
args: "[float]"
output: "s{<48 0/1[/f]>}"
example: ":gpio:s"
min_us: 20
...
---YAML_HELP_END---
Targeted help is available too:
| Command | Returns |
|---|---|
:help |
Full YAML help document |
:help:<verb> |
One verb's details (e.g. :help:gpio:s) |
:help:<char> |
A single-char command's long help (e.g. :help:c) |
:help:json |
The same content in JSON |
:cmds / :cmds_all |
Machine command list (JSON) — :cmds_all includes blocked commands and their access tags |
A host harness for the backchannel ships with the firmware repo at
scripts/bench_backchannel.py (drives :bench, times verbs).
MCP server
Jumperless-mcp by LesbianVelociraptor
For MCP-native clients (Claude Desktop, Cursor MCP, …), the Jumperless exposes a Model Context Protocol server: a Rust MCP front-end plus a resident Python library on the device. It presents the board's capabilities as typed tools (connections, power, measurement, GPIO/PWM, OLED, overlays, state, …).
It lives as the mcp/ git submodule inside the canonical automation repo,
so one checkout gives you both the Agent Skill and the MCP server:
git clone --recurse-submodules https://github.com/Architeuthis-Flux/Large-Breadboard-Model.git
cd Large-Breadboard-Model/mcp
cargo install --path . # or use a release binary
Point your MCP client at the built binary:
{
"mcpServers": {
"jumperless": {
"command": "/path/to/Large-Breadboard-Model/mcp/target/release/jumperless-mcp"
}
}
}
Exclusive port: the MCP server holds the USB serial port while running. Don't run
jumperless.py(the Agent Skill transport) against the same board at the same time.
Agent Skill
If your agent supports the Agent Skills standard
(Cursor, Claude Code), install the Jumperless V5 skill. It teaches the agent the
device's capabilities and drives it through scripts/jumperless.py over the Raw
REPL, with progressive-disclosure reference docs loaded only as needed.
- On this site: Jumperless V5 Agent Skill
- Canonical source: Architeuthis-Flux/Large-Breadboard-Model
mkdir -p ~/.cursor/skills # or ~/.claude/skills
cd ~/.cursor/skills
git clone --recurse-submodules \
https://github.com/Architeuthis-Flux/Large-Breadboard-Model.git jumperless-v5
Node Map
Use this to translate between human‑readable names (like D13 or TOP_RAIL) and the internal node numbers.
All constants are available in the MicroPython REPL; you normally do not need the numbers directly, but the mapping is useful for mental models and debugging.
Breadboard rows (1–60)
Rows 1–60 represent the breadboard rows along the main area:
1–30— upper half31–60— lower half
Each row corresponds to a vertical strip of 5 holes on the physical board.
Example:
connect(1, 10) # connect top row 1 to top row 10
connect(35, GND) # connect lower half row 35 to ground
Node names and IDs
Every node name, alias and ID lives in Node Names and Constants in the MicroPython API Reference.
ISENSE_PLUS and ISENSE_MINUS are the two ends of a 2 Ω shunt resistor and are internally shorted through that shunt; always use them in series with the circuit being measured.
Caution
Avoid using DAC 0 and ROUTABLE_BUFFER_IN because they're used for probe switch sensing
Breadboard layout (high‑level)
The board follows a standard half‑size breadboard geometry:
-
Nano header at the top:
- Top row:
D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 GND RST1 D0 D1 - Second row:
D13 3V3 REF A0 A1 A2 A3 A4 A5 A6 A7 5V RST0 GND VIN
- Top row:
-
Top power rail:
TOP_RAILGND
-
Breadboard rows:
- Rows
1–30— upper half, between the top rails and the center gap - Rows
31–60— lower half, between the center gap and the bottom rails
- Rows
-
Bottom power rail:
BOTTOM_RAILGND
An ASCII diagram (from the docs) approximates the layout; the agent should treat rows and rails as in any typical solderless breadboard, with the addition that every hole has an individually controllable RGB LED.
DIP ICs straddling the center gap
A DIP chip (555, op-amp, logic, …) sits across the center gap, with each side in
a different breadboard half. The two halves are independent rows, so the two
sides of the chip map to numerically distant nodes. For a chip whose first pin
sits at upper-half row N, the upper-side pins run N, N+1, … and the
lower-side pins run from the row directly across the gap (N+30).
Worked example — an 8-pin DIP (e.g. a 555) with pin 1 at row 1:
| Pin | Node | Pin | Node |
|---|---|---|---|
| 1 | 1 |
8 | 31 |
| 2 | 2 |
7 | 32 |
| 3 | 3 |
6 | 33 |
| 4 | 4 |
5 | 34 |
Pins 1–4 are on the upper side (rows 1–4); pins 5–8 are across the gap on the lower side (rows 34→31, counting back toward pin 1). Adjust the base row to wherever the chip is actually placed; verify with the probe or a measurement when unsure.
How to use this map
- Prefer named constants like
D13,TOP_RAIL,GPIO_1,ADC0,ISENSE_PLUSwhen generating code. - Use the numeric IDs only when inspecting low‑level debugging output or when matching numeric IDs in
get_state()/get_net_info()results. - When the user describes physical positions (“top rail”, “bottom left rail”, “row 25”), translate them into these node names/IDs before constructing
connect(...)commands.
JSON State Format
The function get_state() returns a JSON string describing the entire board state: power rails, nets, GPIOs, and overlays. set_state(json_str) applies such a state. Two optional flags follow it, and they have to be positional — set_state(json_str, clear_first, from_wokwi), where clear_first defaults to true and from_wokwi to false. Keyword arguments raise a TypeError.
Note
The entire immediate state of the board is defined in this JSON and can be
shared to another Jumperless, saved as a backup, included in tutorials, etc.
Slots themselves are stored as YAML, which is what K and :yaml dump.
This file explains the structure so agents can safely inspect, modify, and re‑apply state.
Top-level structure
A typical get_state() JSON (pretty‑printed) looks like:
{
"power": {
"top_rail": 3.8,
"bottom_rail": 3.8,
"dac0": 3.25,
"dac1": 0.0
},
"nets": [
{
"index": 1,
"name": "GND",
"nodes": ["GND", "21"],
"special": "none"
},
{
"index": 2,
"name": "Top Rail",
"nodes": ["TOP_R", "23"],
"special": "RAIL",
"voltage": 3.8
}
// ...
],
"gpio": [
{
"pin": 1,
"net": null,
"function": "SIO",
"direction": "INPUT",
"pull": "down",
"reading": "unknown",
"floating_read": 1
},
{
"pin": "TX",
"net": 7,
"function": "UART",
"direction": "OUTPUT",
"pull": "down",
"reading": "unknown",
"floating_read": 0
}
],
"overlays": [
{
"name": "border",
"row": 1,
"col": 1,
"width": 30,
"height": 10,
"colors": ["003333", "003333", "..."]
}
]
}
There may be additional keys in future firmware versions; agents should not assume the set of keys is fixed and should preserve unknown keys when round‑tripping.
power section
"power": {
"top_rail": 3.8,
"bottom_rail": 3.8,
"dac0": 3.25,
"dac1": 0.0
}
Fields:
top_rail— voltage (float) configured forTOP_RAILbottom_rail— voltage configured forBOTTOM_RAILdac0,dac1— DAC channel setpoints
When using set_state, changing these values drives the corresponding channels and is saved into the current slot. Any value outside -8 V to 8 V is rejected.
nets section
Each item describes a net (set of nodes that are electrically connected inside the crossbar).
Example:
{
"index": 4,
"name": "DAC 0",
"nodes": ["DAC_0", "BUF_IN"],
"special": "DAC",
"voltage": 3.25
}
Fields:
index— numeric net indexname— human‑readable net name (may be auto‑generated or user‑defined)nodes— array of node names (strings), such as"GND","TOP_R","21","D13", etc.- These names correspond to the mapping in Node Names and Constants.
special— annotation for special nets; the values are"none","RAIL","DAC","ADC","GPIO","UART_TX","UART_RX"and"FAKE_GPIO_IN"voltage— present onADC,DAC,RAILandFAKE_GPIO_INnetslogic— present onGPIO,UART_TXandUART_RXnets; the logic level (0 or 1)
Important: nets describes logical groupings of nodes. It is not a direct list of bridges; bridges are derived from the crossbar configuration internally.
gpio section
Each entry describes one logical GPIO:
{
"pin": 1,
"net": null,
"function": "SIO",
"direction": "INPUT",
"pull": "down",
"reading": "unknown",
"floating_read": 1
}
Fields:
pin— either:- An integer
1–8forGPIO_1–GPIO_8 "TX"or"RX"for the UART pinsnet— net index this pin is connected to (ornullif unconnected)function— textual description of function:"SIO"— standard GPIO"UART"— UART TX/RX- Other firmware‑defined roles
direction—"INPUT"or"OUTPUT"pull—"up","down", or"none"reading— textual summary of the last known logic state ("high","low","unknown", etc.)floating_read— boolean (0/1) indicating whether floating detection is enabled
Agents can inspect this section to understand logical GPIO usage, but should generally change GPIO configuration via the MicroPython APIs (gpio_set_dir, gpio_set_pull, etc.) rather than editing this JSON directly.
overlays section
Overlays describe additional graphics drawn on the breadboard RGB LEDs. They are rendered on top of the firmware’s net coloring.
Example:
{
"name": "border",
"row": 1,
"col": 1,
"width": 30,
"height": 10,
"colors": [
"003333", "003333", "...",
"003333", "000000", "...",
"... more hex color strings ..."
]
}
Fields:
name— overlay name (string)row— top row (1–10)col— left column (1–30)width— width in columnsheight— height in rowscolors— flat array of hex color strings without0xprefix, e.g."003333","FF2020"- Length =
width * height - Order is row‑major: top row left→right, then next row down, etc.
"000000"means “transparent” — do not override underlying LED color
Brightness recommendation:
- Avoid full‑scale white
FFFFFF. - Prefer saturated colors with reduced magnitude (~25%): e.g.
2020FF,20FF20,FF2020,404040.
When editing overlays via JSON:
- Always maintain the correct
width * heightnumber of color entries. - Preserve unknown fields if present.
- Ensure that
row,col,width, andheightkeep the overlay within the canvas, which is 10 rows by 30 columns: row 1 is the top outer edge, rows 5 and 6 straddle the center gap, and row 10 is the bottom outer edge.
Often it is easier to use the MicroPython overlay APIs (overlay_set, overlay_clear, etc.) directly instead of editing overlays in the state JSON manually. The JSON is still useful for inspection and for bulk export/import of complex scenes.
Editing state safely
General guidelines:
- Fetch once, apply locally.
- Call
get_state()and parse the JSON in the host environment. -
Keep this as your “baseline” snapshot.
-
Work on a copy.
- Clone the baseline object before modifying it.
-
Preserve unknown fields.
-
Modify only what you care about.
- For power: update
power.top_rail,power.dac0, etc. - For overlays: add or replace specific entries in
overlays. -
Avoid rewriting
netsunless you fully understand the implications; it is often better to useconnect()/disconnect()APIs to modify connectivity. -
Re‑serialize and apply.
- Use a standard JSON encoder.
- Call
set_state(json_string, True)when applying a complete, authoritative state. -
If you are only adding overlays or adjusting power, send a JSON with just those sections.
set_stateaccepts any subset ofpower,nets,gpioandoverlaysand only applies the sections that are there.clear_firstmatters only when anetssection is present — that is when it clears every existing connection first. -
Rollback on problems.
- If
set_stateleads to unexpected behavior, re‑apply the saved baseline snapshot.
Example: adjusting top rail voltage via state
Instead of:
dac_set(TOP_RAIL, 5.0)
You can do:
import json
state = json.loads(get_state())
state["power"]["top_rail"] = 5.0
set_state(json.dumps(state))
This approach is useful when batching multiple changes (e.g. updating both rail voltages and overlays) in a single atomic operation.
Example: augmenting overlays
Suppose get_state() returns a state with existing overlays, and you want to add a new “place‑here” marker at row 3, column 10:
import json
state = json.loads(get_state())
# Create a 1×1 overlay with a bright but not full‑scale green
place_here = {
"name": "place_here_row3_col10",
"row": 3,
"col": 10,
"width": 1,
"height": 1,
"colors": ["20FF20"]
}
state.setdefault("overlays", [])
state["overlays"] = [
o for o in state["overlays"] if o.get("name") != place_here["name"]
] + [place_here]
set_state(json.dumps(state))
This pattern:
- Preserves existing overlays.
- Replaces any prior overlay with the same name.
- Leaves other state fields untouched.
Wokwi import (from_wokwi=True)
When from_wokwi=True, set_state() expects json_str to be a Wokwi diagram.json rather than a Jumperless state JSON. The firmware converts the Wokwi representation into bridges, nets, and colors.
Agents can:
- Ask the user to paste a Wokwi JSON.
- Pass it directly to
set_state(wokwi_json, True, True).
After that, a subsequent get_state() will return the standard Jumperless state JSON.
Robustness tips for agents
- Treat
get_state()as authoritative for the current internal configuration, but always cross‑check against the user’s description of external wiring and components. - Always preserve unknown keys when editing state; future firmware versions may add fields.
- Prefer using high‑level MicroPython APIs (
connect,disconnect,dac_set,overlay_set, etc.) for incremental changes, and use state JSON editing for bulk or offline transformations (import/export, heavy refactoring, or pre‑computed overlays).
MicroPython tool reference (subset)
The complete, authoritative list is the MicroPython API Reference. The most common calls for automation:
| Category | Key tools |
|---|---|
| Connections | connect(), disconnect(), nodes_clear(), is_connected(), fast_connect() |
| Slots | nodes_save(slot), switch_slot(slot), CURRENT_SLOT() |
| State | get_state(), set_state(json) |
| Voltage | dac_set(ch, v), dac_get(ch) |
| Measurement | adc_get(ch), ina_get_current(s), ina_get_voltage(s), ina_get_power(s) |
| GPIO / PWM | gpio_set(), gpio_get(), gpio_set_dir(), gpio_set_pull(), pwm(), pwm_stop() |
| WaveGen | wavegen_set_wave(), wavegen_set_freq(), wavegen_set_amplitude(), wavegen_start() |
| UI | oled_print(), oled_clear(), probe_read_blocking(), probe_button(), clickwheel_get_button() |
| LED overlays | overlay_set(), overlay_set_pixel(), overlay_clear(), overlay_clear_all() |
| Net inspection | get_net_info(), get_num_nets(), print_nets(), print_bridges() |
Node addressing (quick form)
- Breadboard rows:
1–60 - Rails / supplies:
TOP_RAIL,BOTTOM_RAIL,GND,DAC0,DAC1 - Arduino:
D0–D13,A0–A7,AREF,RESET - GPIO:
GPIO_1–GPIO_8,UART_TX,UART_RX - Measurement:
ADC0–ADC3,ISENSE_PLUS,ISENSE_MINUS
A mental model for LLM agents
The Jumperless prevents internal shorts, but cannot see external wires or components physically on the breadboard. An agent should maintain a model of what it believes is on the board and raise confidence through measurement and user confirmation.
A lightweight per-component record works well:
{
"id": "comp_001",
"type": "resistor",
"value": 1000,
"pins": [5, 10],
"confidence": 0.95,
"detection_method": "measured",
"notes": "Measured 987Ω between rows 5-10"
}
| Confidence | Source | Meaning |
|---|---|---|
| 1.0 | measured |
Electrically verified |
| 0.9 | user_confirmed |
User explicitly confirmed |
| 0.7 | user_stated |
User mentioned it casually |
| 0.5 | inferred |
Guessed from context |
| 0.3 | assumed |
Default assumption |
Workflows
Safe power-up. Before energizing an unknown board, ask what's on it, build a
low-confidence model, then verify key rows with an ADC before connecting power —
a hidden user wire between two rows can turn TOP_RAIL + GND into a dead
short.
Identify with the probe. Use probe_read_blocking() to let the user point
at a node, then route an ADC to it and measure.
Characterize as a black box. Drive a node with the WaveGen / DAC and sweep while reading an ADC.
Batch edits via state. Fetch get_state(), mutate the JSON in Python, and
re-apply with set_state() — one atomic round-trip instead of many calls.
Undo. The firmware keeps a history ring, so you don't need to burn a slot
before destructive ops like nodes_clear(). Call undo() and redo() from
MicroPython (^ and & in the terminal), or keep a get_state() snapshot to
re‑apply.
Safety guidelines
- Voltage range. ADC0–ADC3 are buffered for ±8V; the board is ±9V tolerant overall.
- Short protection. The firmware refuses dangerous internal connections
(e.g.
TOP_RAILdirectly toGND), but cannot see external wiring. - Confirm power. The board is USB-powered (no barrel jack); ask if unsure.
- Crossbar resistance. ~ 20–40Ω per default
connect()path (~ 80Ω with a single path). Measure voltage at the destination to compensate for drop. - Settling time. Allow a few ms after routing/DAC changes (50–100 ms after
nodes_clear()/set_state()) before measuring.
Implementation status
| Feature | Status |
|---|---|
connect(), disconnect(), is_connected() |
Implemented |
dac_set(), adc_get(), ina_*() |
Implemented |
gpio_*(), pwm() |
Implemented |
oled_print(), probe_*() |
Implemented |
| WaveGen tools | Implemented |
| Slot management | Implemented |
get_state() / set_state() snapshot |
Implemented |
adc_get_stats() (averaged sampling) |
Available via the MCP server |
undo() / redo() history |
Implemented |
USBSer3 :help self-describing backchannel |
Implemented (firmware) |