← Documentation index

PPBUILD_INSTRUCTIONS

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
KNOWN LIMITATIONS
WIRE COLOURS
EXIT STATUS
THE BOARD
POLARITY
PARTS THAT DO NOT PLUG IN
PIN IDENTITIES
ENVIRONMENT
SUPPLIES
RAIL WIRES
FAR LEADS WITHOUT A HOLE
SEVERAL BOARDS
RAIL BRIDGES
DIAGRAM FALLBACK
SEE ALSO
AUTHOR
COPYRIGHT

NAME

ppbuild_instructions - convert a breadboard layout into a step-by-step build guide

SYNOPSIS

ppbuild_instructions [-o FILE] [--kicad-cli CMD] [--config FILE] [--order {position|netlist}] [--format {plain|markdown|html|orgmode}] [--child-level] [--search-datasheets] [--debug] [--quiet] SCHEMATIC
ppbuild_instructions
{--help|--version}

DESCRIPTION

ppbuild_instructions reads a real KiCad schematic (SCHEMATIC, a .kicad_sch file), runs it through the same auto_layout.py/render.py pipeline pprender(1) itself uses, and produces a textual, ordered, step-by-step instruction list a real person can follow to physically assemble the circuit on a breadboard by hand — which component goes in which hole first, which jumper wire connects which two points next, and so on.

Unlike every other script in this project, ppbuild_instructions produces prose, not a picture: it is meant to be read while building, not looked at while designing. It is also a learning tool, not only a fabrication guide — each step names the component’s own type and value, not just its reference, and can optionally link to further reading (see --search-datasheets below).

ppbuild_instructions internally calls pprender(1)’s own render() function (to a discarded temporary file) before generating any steps: a passive’s own far leg is resolved by a real, stateful, order-dependent collision search inside that function, and cannot be reliably re-derived independently — reusing the exact value that function computed is what guarantees the instructions always match the diagram. A schematic that pprender(1) itself would refuse to render (a failed connectivity or board-bounds check) will fail the same way here.

A component that auto_layout.py’s own placement algorithm could not place at all is reported once (see --quiet below) and silently excluded from the instructions — an honest, pre-existing limitation of the underlying placement, not something this tool can work around.

OPTIONS

-o FILE, --output FILE

Write the instructions to FILE instead of standard output.

--kicad-cli CMD

The kicad-cli(1) command to use for netlist export. Default: the KICAD_CLI environment variable if set, else kicad-cli on PATH.

--config FILE

A real ppbreadboard_config(1) config file describing the target board. Without this, the same default lookup pprender(1) uses applies.

--max-components N

Override this project’s own real, default component-count limit ( breadboard_config.py’s own DEFAULT_MAX_COMPONENTS constant, currently 150) — see that constant’s own docstring for the full, real rationale. Raising it above the real, existing default prints a real, direct warning to standard error, but still proceeds.

--order STRATEGY

Step ordering: position (the default — anchors first by leftmost pin, then passives left to right by physical column; confirmed by direct, quantitative comparison to minimize how often a real builder’s hands have to jump across the board) or netlist (anchors and passives both in plain reference order — simpler, but crosses sector boundaries far more often on a dense circuit).

--format FORMAT

Output format: plain (the default), markdown, html, or orgmode.

--child-level

Use simpler language, aimed at a child around age 7 (shorter sentences, plainer words), instead of the real, default, adult-targeted phrasing.

--search-datasheets

Attempt a real web request for a more direct datasheet link per specific part (an IC or connector, not a generic passive), instead of the default, fully offline behaviour (a Wikipedia link for recognized generic passive types — resistor, capacitor, inductor, diode, etc. — and a reliable search-engine query link for everything else). Best-effort only: falls back to the offline query link on any failure (no network access, a blocked domain, a timeout), never aborts the run over one missing link. This is the first genuine network access anywhere in this project; its own real accuracy has not been verified against a broad set of parts.

--version

Show this project’s own current version (read from VERSION) and exit.

--debug

Also print each generated step’s own board index, to standard error.

--quiet

Suppress the "generated N steps" message and the unplaced-component note, both printed to standard error. Never suppresses the instructions themselves.

KNOWN LIMITATIONS

Component orientation (a diode’s own polarity band, an electrolytic capacitor’s own polarity, a connector’s own pin-1 marking) is not tracked anywhere in this project’s own placement data, and so is never mentioned in the generated instructions — a real, separate, not-yet-attempted follow-up.

Net-grouped step ordering (clustering passives by which functional section of the circuit they belong to, rather than by physical position or reference) was considered and deliberately deferred: the simplest heuristic collapses almost everything into one group on an MCU-centric circuit, and a genuinely useful version needs real, further design work (see FutureResearch.org).

WIRE COLOURS

Every wire step names the colour of its wire, and pprender(1) draws the wire in the same colour, because both take it from one plan. Ground is black. The supply that the board’s rail carries is red and a second supply is orange; a third and later supply takes the first colours of the signal order. Every other net has a colour of its own from blue, green, yellow, white, grey, purple and brown, chosen by the leftmost column the net touches so that neighbouring nets differ; a circuit with more signals than colours repeats them. A wire to the power (+) rail is red, a wire to the ground (-) rail is black, and a wire that distributes power between rails is red.

EXIT STATUS

0

Instructions generated successfully.

1

The underlying connectivity or board-bounds check failed (same checks pprender(1) runs), or the circuit’s own real component count exceeds whichever --max-components limit is in effect (see above), or the circuit cannot be drawn or written down on the board and the diagram layout cannot help either (no free column within reach for a far lead, no free hole for a jumper wire): a message says so and suggests a larger board.

2

Invalid command-line usage.

THE BOARD

A design that needs a bigger board than the default gets one computed for it, and the hole names in the instructions ("f3") mean different strips on different boards. When the board is not the default five rows per half, the build instructions therefore state it, once, right after the title:

Board: 7 rows per half (rows a-g above the trench, h-n below).
A single board only: a configuration with several boards carries one geometry per board and is not stated.

A design that needs more width than a classic board (63 columns) is laid out on 2 sectors, one below the other, and the line then says so, for example

Board: 5 rows per half (rows a-e above the trench, f-j below), in 2 sectors, one below the other (a hole in a sector
after the first is marked, for example a5 (sector 2)).

Hole names are relative to their sector ("a5" exists in every sector), so every hole after the first sector is followed by its sector, as in "hole a5 (sector 2)"; a design on one sector mentions no sector at all.

A board whose configuration says its power rails are not continuous ("continuous = false" in the [rail] section) is stated too: the header adds that the rails are split partway along their length, to be checked with a multimeter and bridged with a jumper wire wherever the circuit uses both halves. A board with continuous rails, the default, says nothing of it.

POLARITY

A part whose two pins the netlist names as polar (A and K for an LED or a diode, + and - for a cell or a buzzer), or a polarised capacitor (whose symbol names both pins ˜ and, by KiCad’s convention, has the positive one on pin 1), has the role of each lead named in its step, with what tells the leads apart on the part, for example

Insert the LED (D2): one lead into hole g18 (the cathode, the shorter lead on the flat side of the case), the other into
hole j20 (the anode, the longer lead).

An LED’s anode is the longer lead; a diode’s cathode is the end marked with a band; a polarised capacitor’s positive lead is the one marked +. A part with no known marking, a photodiode for instance, gets the bare role. A resistor or a ceramic capacitor has no polarity and its step is unchanged. Which way round an integrated circuit or a connector sits (its pin 1) is not described.

PARTS THAT DO NOT PLUG IN

The KiCad footprint of each multi-pin part is read and the part classified. A through-hole part whose pads all lie on the 0.1 inch (2.54 mm) grid plugs straight into a breadboard, and its step says nothing more. A surface-mount part ends its step with "It is surface-mount (FOOTPRINT): it needs a breakout board with 0.1 inch pins."; a through-hole part with a pad off the grid, such as a TO-92 whose legs are 1.27 mm apart, with "Its pins are not on the 0.1 inch grid (the closest are 1.27 mm apart): check that it fits the board, or use an adapter."; a part whose footprint is not in the installed library ($KICAD_FOOTPRINT_DIR) with "No footprint is on file for it: check that it can be plugged into a breadboard." A through-hole part on the grid for which no way of plugging it in keeps its pins in strips of their own, a strip being the five holes of a column of one half (a two-row socket or header, whose rows are one hole apart: a pin of each row lands in every column, whichever way it is turned), is placed "one pin per column" instead of "straddling the trench" and ends its step with "Its two rows of pins are only one hole apart, so two of its pins would share a column of holes and be shorted: it cannot be plugged into a breadboard as it is, so mount it on a breakout board or use a jumper lead for each pin."; its layout is one column per pin, which is what a breakout board or jumper leads give. When any part is in one of these groups, a line under the title names them. A passive part is classified too, but only when its footprint is KNOWN to be unpluggable (surface-mount, off the grid, a shape that shorts its own pins): its step ends with the same sentence. A passive whose footprint plugs in says nothing, and neither does one with no footprint at all, which is taken to be an ordinary insertable part.

PIN IDENTITIES

An anchor’s step lists every pin with its hole, from the left, right after the sentence naming the hole of its leftmost pin: "Its pins, from the left: 1 in a5, 2 in a6, 3 in f7, 4 in f8." After the sentence naming the rails it connects to, it names the pins on each rail: "Its pins on the ground (-) rail: 2." A pin identifier that could make the list ambiguous (one with a comma, a space or a period) keeps the older wording for that part. The holes are the diagram’s, one column per pin; a package seats its pins where its footprint puts them, so when a part that plugs in has its pins listed a line under the title says to read the list as where each net goes, not as how to seat the part. A pin that is not connected is not counted as connected to a rail.

The pin list of a PHYSICAL part (a header, a DIP) holds every pin that is on a rail as well as the pins on nets, each with its hole, since the part stands in consecutive columns whether or not a pin has a net; a part laid out as a diagram has no hole of its own for a rail pin, and its list holds the pins on nets only.

The parts that are not anchors name their pins too, where the reconstruction would otherwise get them wrong. A connector or a cell wired straight to the rails (the step "Connect the X (J2) directly to the north rail.") names the pins on each rail, by the same convention: "Its pins on the power (+) rail: 1. Its pins on the ground (-) rail: 2." A two-lead part names the pin of each lead after its hole, "one lead into hole b6 (pin S), the other into hole e5 (pin T)", when the default of the reconstruction (the near lead is pin 1, the far lead pin 2) would be wrong: its pins are not 1 and 2 (an audio jack’s S and T), or its symbol names its two pins differently (Pin_1 and Pin_2, + and -) and the near lead is pin 2 (a two-pin connector). A resistor, an LED (its lead roles say it), an LDR or a switch never carries one.

ENVIRONMENT

PACKPAY_PHYSICAL_ANCHORS

On unless set to 0. A part whose footprint is a dual-row through-hole DIP (two rows 7.62 mm apart) is placed where the footprint puts its pins: two pins in each column, one in each half, the notch facing left and pin 1 in the lower row at the left, the other pins numbered counter-clockwise. The step says so ("Its notch faces left: pin 1 is in the lower row, at the left.") and lists the pins accordingly. Set to 0 for the diagram layout of the earlier releases, one column per pin. Every other part is laid out as before, and so is a design that has no such DIP.

PACKPAY_ANCHOR_REFINE

On unless set to 0: the anchors (the chips and connectors that stand in a row) are placed in a connectivity order that a local search then refines, which keeps related anchors closer and the net jumpers shorter (measured on the example designs: 18% shorter in total, never longer on any design); a layout that would leave a part unplaced is made again in the earlier order. Set to 0 for the earlier order.

PACKPAY_MIN_ANCHOR_SPACING

The smallest number of columns between two anchors (default 2). On a board too narrow for the default spacing of 6, a tighter spacing is tried, down to this value, before another sector or board is added; a board that fits at the default is untouched. 6 or more switches this off.

PACKPAY_SHRINK_GUARD

On unless set to 0: a tighter spacing is accepted only if it is drawn without refusing it and with no hole collision (a lead on a lead or a wire). Label overlaps do not count.

PACKPAY_FAR_LEAD_SIDES

On unless set to 0. A far lead (the lead of a two-lead part that is not on the pin or column the part hangs from) lands, nearest first on either side, in a column of its half that already holds its own net, before the plain right-first search; only for a net that is not a rail. It removes the jumper wire that a partner pin on the left used to cost (on a DIP the pins of the upper row run right to left). Set to 0 for the earlier search.

PACKPAY_PHYSICAL_HEADERS

On unless set to 0. A single-row pin header is placed in ONE row of holes, all its pins in the upper half in consecutive columns, instead of across the trench with its pins in both halves; the step says "standing in one row" where it would say "straddling the trench" and "Its pin 1 is at the left." where a DIP says where its notch is. Set to 0 for the diagram layout of the earlier releases. It needs PACKPAY_PHYSICAL_ANCHORS (on unless 0). Whatever the setting, a physical part’s rails are on its own rail pins, inside the part.

SUPPLIES

The breadboard has one power (+) rail and one ground rail. When a circuit has several supplies (nets that are not GND and are named like a supply, or are power nets with many pins), the one with the most pins is the rail, and every other supply is wired like an ordinary net, with jumpers, since two supplies on one rail would be joined. Ground is the net named GND.

RAIL WIRES

The jumper that connects a column to a rail is a step of its own, right after the step of the part it belongs to: "Connect a jumper wire from hole j14 to the nearby ground (-) rail." The hole is the outermost row of the column’s half, where the wire lands. There is one step per hole and polarity, since several leads in one rail column share one jumper. A chip’s rail column, a passive’s near lead and its far lead each have one when they are on a rail. For a chip laid out as a diagram, the column is where its pins on that rail are joined, and the step says which: "U1’s ground (-) pins 21, 3 go in this column: hole f14." (a physical part’s rail pins have holes of their own in its pin list).

FAR LEADS WITHOUT A HOLE

When a passive’s far leg has no hole of its own (its step says it "could not be placed within this board’s own row limit"), the jumper wire that starts in the column where that lead is drawn says whose lead it is: "Connect a jumper wire from hole b57 to hole g25 (net USB_DN). Its first end is where R19’s far lead connects." Until 0.72.84 the text did not say, and the way back from the instructions to a schematic gave that hole to the nearest part on its left. On several boards, when that wire crosses boards and is written as two steps (one from each end), the step for the end that is the far lead says so too: "This end is where R24’s far lead connects." The text of a circuit with no such far leg is unchanged.

SEVERAL BOARDS

When a circuit needs more than one breadboard, the text starts with a line that states each board ("Boards: 2 breadboards (board 1: 63 columns, 5 rows per half, 1 sector; board 2: ...)") and every step that sits on one of them starts with [board N], because the hole names are the same on every board (the a5 of board 2 is not the a5 of board 1). A wire between two boards is two steps, one from each end, each naming its board and the other; the rails of the boards are joined by bridge steps that name both boards. Until 0.72.81 only the wires between boards named a board, so a part on board 2 read exactly like one on board 1. The text of a circuit on one board is unchanged.

RAIL BRIDGES

A rail net’s separate strips are joined by a jumper, which the drawing shows and which is a step of its own, after the parts and the wires between holes: "Connect a jumper wire from the north ground (-) rail to the south ground (-) rail." A board of N sectors has N+1 rails, the first north, the last south, and the one between two sectors middle, so a two-sector board bridges north to middle and middle to south, for ground and for the supply rail. On several boards the step names them: "Connect a jumper wire from the south ground (-) rail of board 1 to the north ground (-) rail of board 2." Until 0.72.75 no step said so, and a builder following the text would have had two separate grounds on four of the seven example designs.

DIAGRAM FALLBACK

A part that stands where its footprint puts its pins (a header, a DIP) occupies the strips of its pins, rail pins included, and a lead of another net is never put in one. A DENSE circuit can leave a far leg no short-free column; then the command says so on the error output, "the physical layout of this circuit cannot be made without shorting two nets", and runs again with PACKPAY_PHYSICAL_ANCHORS=0 set, which lays the circuit out as a diagram (one column per pin). The same happens if two nets are found sharing a strip after the drawing. A circuit with no such part is not affected, and nothing changes when the variable is already 0.

SEE ALSO

pprender(1), ppauto_layout(1), ppbreadboard_config(1), pptest_build_instructions(1), ResearchJournal.org and FutureResearch.org in packpay’s own documentation set (see Index.org).

AUTHOR

Péter Felecan <pfelecan@gmail.com>, with development assistance from Claude (Anthropic).

COPYRIGHT

Copyright © 2026 Péter Felecan. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.