bblayout
Lay electronic schematics out on a solderless breadboard instead of a PCB.
bblayout takes a netlist (components plus the nets their pins connect to) and
produces a real breadboard build: where every part goes, which jumper wires to
add, and step-by-step instructions. It also checks any layout for shorts,
open nets and physical conflicts. It was built so AI agents can go from "design a
circuit" to "here's exactly how to build it", but it works just as well from
Python or the command line.
- Pure Python, no dependencies, Python 3.9+
- Board templates: mini (170), half (400), full (830), full with split rails, double (1660), plus
"auto" - Footprints: DIP ICs (0.3" and 0.6"), inline parts (TO-92, headers, trimmers), flexible two-lead parts
- Auto-placement and routing that use the power rails and avoid jumper wires where possible
- An independent verifier for layouts edited by hand or by an AI
- Output as ASCII maps, SVG drawings, build steps and JSON
- PCB-like export for simulation: SPICE with breadboard parasitics, KiCad
.kicad_pcb, and per-net trace reports - Ready-made LLM tool definitions (Anthropic and OpenAI formats)
Install
pip install -e .
Quick start
from bblayout import Schematic, ic, resistor, capacitor, led, auto_layout
sch = Schematic("555 blinker", [
ic("U1", "NE555", ["GND", "TRIG", "OUT", "VCC", "CTRL", "TRIG", "DIS", "VCC"],
labels=["GND", "TRIG", "OUT", "RESET", "CTRL", "THRES", "DIS", "VCC"]),
resistor("R1", "VCC", "DIS", "10k"),
resistor("R2", "DIS", "TRIG", "68k"),
capacitor("C1", "TRIG", "GND", "10u", polarized=True),
capacitor("C2", "CTRL", "GND", "10n"),
resistor("R3", "OUT", "LED_A", "470"),
led("D1", "LED_A", "GND", "red"),
])
layout = auto_layout(sch, "half") # or "mini", "full", "full_split", "double", "auto"
print(layout.ascii())
print(layout.instructions())
assert layout.verify().ok
open("blinker.svg", "w").write(layout.svg())
1 5 9
T+ . * B . . * . VCC
T- . . . . E * . GND
a . . * C . E . . .
b . . . B . . . . .
c . . . . * . . . .
d . . . . C . . . .
e . . A A A A . . .
~~~~~~~~~~~~~~~~~~~~~~~~~
f . . A A A A . . .
g . . D D . . . . .
h F = = = F . . . .
i G = G * . . . . .
j . . * . . * . . .
B- . * . . . * . GND
B+ . . . . * * . VCC
A = U1 NE555 (DIP-8)
B = R1 10k (two-lead)
...
1. Insert U1 NE555 (DIP-8) straddling the centre channel, pin 1 at f3, notch facing left.
pin 1 (GND) -> f3 [GND]
...
5. Insert R1 10k (resistor):
pin 1 -> T+4 [VCC]
pin 2 -> b4 [DIS]
...
10. Wire (red, ~0.3"): a3 -> T+3 [VCC]
Breadboard templates
| Template | Points | Columns | Power rails |
|---|---|---|---|
mini |
170 | 17 | none |
half |
400 | 30 | 4 × 25 holes |
full |
830 | 63 | 4 × 50 holes |
full_split |
830 | 63 | 4 × 50 holes, split in the middle |
double |
1660 | 126 | 4 × 100 holes |
auto |
smallest of mini → half → full → double that fits |
Aliases such as "bb830", "400" and "half-size" work too. To define a custom
board, use Breadboard(columns=..., rails=..., rail_groups=..., split_rails=...)
or Breadboard.from_dict({...}).
Hole names
- Terminal holes: row
a–jplus column, e.g.e12. Rowsa–eandf–jsit on either side of the centre channel, and the five holes of one column on one side are connected (a strip). - Power-rail holes:
T+,T-(top) andB+,B-(bottom) plus the column they line up with, e.g.T+5. Rail holes come in groups of five, like on real boards.
Schematic JSON (the format for AI agents)
{
"name": "NPN switch",
"components": [
{"ref": "R1", "kind": "resistor", "value": "1k", "pins": ["IN", "BASE"]},
{"ref": "Q1", "kind": "npn", "part": "2N3904", "pinout": "EBC",
"pins": {"E": "GND", "B": "BASE", "C": "LED_K"}},
{"ref": "D1", "kind": "led", "value": "red", "pins": {"A": "LED_A", "K": "LED_K"}},
{"ref": "R2", "kind": "resistor", "value": "330", "pins": ["VCC", "LED_A"]},
{"ref": "U1", "kind": "ic", "part": "LM358",
"pins": ["OUT", "IN-", "IN+", "GND", null, null, null, "VCC"]}
],
"rail_nets": {"T+": "VCC", "T-": "GND"}
}
pinsis either a list in pin order or a{pin: net}object. Usenullfor an unconnected pin.kindpicks a default footprint:- two-lead:
resistor,capacitor,electrolytic(+/-),led(A/K),diode(A/K),inductor,crystal,button, ... - inline:
transistor/npn/pnp(E,B,C; override withpinout),mosfet(G,D,S),regulator(IN,GND,OUT),potentiometer(1,W,3),header - DIP:
ic(pins numbered from 1)
- two-lead:
- Give an explicit
footprint({"type": "dip", "pins": 28, "width": 6}) for anything unusual. rail_netsis optional. If you leave it out, nets namedVCC/VDD/5V/3V3/... go on the+rails andGND/VSSon the-rails.
Built-in examples: bblayout.circuits.get(name) for led_resistor,
button_led, 555_blinker, transistor_switch, opamp_amplifier,
regulated_supply, led_chaser.
Using it from an LLM agent
bblayout.tools exposes four tools: breadboard_layout, breadboard_verify,
breadboard_export and breadboard_templates. They include a JSON schema for the schematic format.
from bblayout.tools import tool_definitions, handle_tool_call
tools = tool_definitions() # Anthropic format; tool_definitions("openai") for OpenAI
...
result = handle_tool_call(block.name, block.input) # dict: ok, verification, instructions, ascii, layout
examples/claude_agent.py is a complete tool-use loop with the Anthropic SDK.
An agent can also change the returned layout JSON (move a part, add a wire)
and pass it to breadboard_verify. The verifier rebuilds connectivity from the
board itself and reports short, open, hole_conflict, covered_hole,
footprint, off_board, unplaced and rail_mismatch issues.
Command line
bblayout templates # list boards and example circuits
bblayout example 555_blinker -o blinker.json # dump an example schematic
bblayout layout blinker.json -b half -o layout.json --svg layout.svg
bblayout layout 555_blinker -b auto # built-in circuits work directly
bblayout verify layout.json
bblayout export layout.json --spice out.cir --kicad out.kicad_pcb --report
(python -m bblayout ... works without installing.)
PCB-like export for simulation
A breadboard is a crude PCB. Each terminal strip and power rail is a phosphor-bronze
spring clip (a wide, thin track), each jumper is a round copper wire, and every
inserted lead adds contact resistance. bblayout turns a layout into that copper
model with real track widths, so the result can go into a simulator or a PCB tool.
layout = auto_layout(circuits.get("555_blinker"), "half")
model = layout.pcb() # copper model: traces with width, length, R, L
print(model.report()) # trace count, length and resistance per net
open("blinker.cir", "w").write(layout.spice(analysis=".tran 1m 2")) # with parasitics
open("ideal.cir", "w").write(layout.spice(parasitics=False)) # ideal, for comparison
open("blinker.kicad_pcb", "w").write(layout.kicad_pcb()) # KiCad board
bblayout export 555_blinker -b half --spice blinker.cir --kicad blinker.kicad_pcb \
--pcb-json copper.json --supply VCC=9 --analysis ".tran 1m 2" --report
net traces clips wires contacts length mm R sum mOhm L sum nH eq. 1oz width mm
GND 8 6 2 8 89.28 171.95 67.03 9.31
TRIG 5 4 1 6 37.18 124.37 25.86 9.31
VCC 9 6 3 9 111.06 193.57 85.06 9.31
...
What is modelled (all values can be changed with BreadboardPhysics):
| Element | Default geometry | Becomes |
|---|---|---|
| Terminal-strip / rail clip | 1.5 × 0.3 mm phosphor bronze (ρ = 1.1e-7 Ω·m) | B.Cu track; SPICE R + L per segment between used holes |
| Jumper wire | 22 AWG solid Cu (Ø 0.644 mm), span + 2 mm per end | F.Cu track; SPICE R + L |
| Lead / wire insertion | 20 mΩ contact | SPICE series R |
| Neighbouring strips | 2 pF per adjacent pair; rail pairs 0.5 pF per hole | SPICE C between nets |
| Bench supply | plugs into the end of its rail | SPICE V source + contact |
- SPICE (ngspice / LTspice / Xyce syntax): resistors, capacitors, inductors,
LEDs, diodes, BJTs, MOSFETs, pots (
.param POS_RV1) and switches (.param R_SW1) become native elements with simple generic models. ICs becomeXsubcircuit calls, and the deck lists the.includelines for you to point at vendor models. Supply voltages are guessed from net names (5V,3V3, andVCC= 5 V) unless you passsupplies={...}. Unconnected pins get a 1 TΩ resistor to ground so the DC operating point exists. - KiCad (
.kicad_pcb, KiCad 6+): parts are through-hole footprints at their holes, clips are B.Cu tracks, jumpers are F.Cu tracks, wire ends are vias, and the board outline is the breadboard.track_width="equivalent"swaps the physical widths for the 1 oz copper width with the same resistance. Jumpers are insulated wires above the board, so where one crosses a pad KiCad DRC will report a clearance violation; that is expected. - JSON (
model.to_dict()): every trace (layer, width, thickness, length, R, L, equivalent 1 oz width, IPC-2221 ampacity), every contact and every coupling capacitance, plus per-net totals.
AI agents get the same through the breadboard_export tool.
How the auto-layout works
- Supply nets are assigned to the power rails.
- DIP ICs are placed left to right across the centre channel, with free columns between them.
- The other parts are placed greedily, most-connected first. Each legal position gets a score: a lead in a strip that already carries its net costs nothing, a new strip costs a little, and a strip that needs a jumper back to its net costs more (more for longer jumpers). A lead can go straight into a rail. Parts may not arch over a chip, and a part lying flat along a row covers the holes under its body.
- Every used strip keeps a free hole for wiring. Strips on supply nets are wired to the nearest rail of that net, and rails are tied together only where needed. Other nets are joined with a short spanning tree of jumpers that fits the free holes in each strip.
Layouts are deterministic. auto_layout(..., fixed={"U1": "f20"}) pins chosen
parts in place, and LayoutOptions adjusts IC spacing, reserved holes and rail
behaviour.
Limitations: jumpers are straight point-to-point wires, so they can pass over chips. The placer is greedy, so the layout is good but not optimal. Each DIP has one orientation (pin 1 bottom-left).
Development
python -m unittest discover -s tests # or: pytest
License
Apache-2.0
Metadata
Release files for bblayout 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bblayout-0.0.1.tar.gz | 64.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bblayout-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 125.8 kB
Release files / bblayout-0.0.1.tar.gz
| Download URL | bblayout-0.0.1.tar.gz |
|---|---|
| Size | 64.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f08c2d7b5aea0df9a950bd6663ecec5a366c0af506291bedbdf1c9314c9bcc48
|
|
BLAKE2b-256 checksum How to use checksums |
898c4e94cb92a8ac9015cc80b061854e85ab8a4a747affc7baa94fbf3ff39369
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / bblayout-0.0.1-py3-none-any.whl
| Download URL | bblayout-0.0.1-py3-none-any.whl |
|---|---|
| Size | 61.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
89ce472f16e3a117f6caae0bc28565240374b19e6ed08f62f2768bbc43493778
|
|
BLAKE2b-256 checksum How to use checksums |
88bebc5af5a2e3f6eacef71106a20af5950e15bb1b78e0b9508486e5a472ad63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log