Skip to main content

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–j plus column, e.g. e12. Rows a–e and f–j sit 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) and B+, 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"}
}
  • pins is either a list in pin order or a {pin: net} object. Use null for an unconnected pin.
  • kind picks 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 with pinout), mosfet (G,D,S), regulator (IN,GND,OUT), potentiometer (1,W,3), header
    • DIP: ic (pins numbered from 1)
  • Give an explicit footprint ({"type": "dip", "pins": 28, "width": 6}) for anything unusual.
  • rail_nets is optional. If you leave it out, nets named VCC/VDD/5V/3V3/... go on the + rails and GND/VSS on 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 become X subcircuit calls, and the deck lists the .include lines for you to point at vendor models. Supply voltages are guessed from net names (5V, 3V3, and VCC = 5 V) unless you pass supplies={...}. 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

  1. Supply nets are assigned to the power rails.
  2. DIP ICs are placed left to right across the centre channel, with free columns between them.
  3. 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.
  4. 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)

Source distribution for bblayout 0.0.1
File Size Uploaded
bblayout-0.0.1.tar.gz 64.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bblayout 0.0.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page