CLI toolkit for managing OpenPnP pick-and-place jobs.
Bridges the gap between KiCad PCB design and OpenPnP machine operation — generating board files, assigning feeders, and visualizing the setup.
KiCad Schematic KiCad PCB
│ │
├─ sch export bom ├─ pcb export pos
│ (Value,Footprint,IPN) │ (Ref,Value,Footprint,X,Y,Rot,Side)
▼ ▼
pnp/bom.csv position CSV
│ │
│ ▼
│ ┌──────────────┐
│ │ generate │──▶ pnp/board.xml + pnp/board.pos
│ └──────────────┘
│ │
│ ▼
│ ┌──────────────┐
│ │ ensure-parts │──▶ parts.xml, packages.xml
│ └──────────────┘
│
│ pnp/feeders.csv ─▶ ┌──────────────┐
│ │ assign │──▶ machine.xml (feeder slots)
│ └──────────────┘
│
│ pnp/job.xml ─────▶ ┌──────────────┐
└────────────────────▶ │ map │──▶ pnp/feeder-map.html
└──────────────┘
Shared config (from config repo, resolved via repo-dir):
openpnp-package-map.csv Footprint → package mapping + tape metadata
machine.xml Feeder positions, bed dimensions
Reads a KiCad position CSV and generates an OpenPnP board XML with remapped package names. Fiducials are auto-detected. Hand-place parts (packages with no tape info in the package map) are automatically disabled.
# KiCad 10+ (writes to file)
kicad-cli pcb export pos --format csv --side both --units mm --smd-only --exclude-dnp board.kicad_pcb
openpnp-tools generate -o pnp/ -n myboard board.csv
# KiCad ≤9 (writes to stdout)
kicad-cli pcb export pos --format csv --side both --units mm --smd-only --exclude-dnp board.kicad_pcb \
| openpnp-tools generate -o pnp/ -n myboardOutputs:
pnp/myboard.board.xml— OpenPnP board with placementspnp/myboard.pos— remapped position file
Ensures all parts referenced by a board exist in parts.xml with correct
package-id and heights from the package map. Also sets tape-specification
and compatible-nozzle-tip-ids on packages in packages.xml.
openpnp-tools ensure-parts pnp/myboard.board.xmlAssigns parts to feeder slots in machine.xml based on a project's feeders.csv.
Also sets tape-type, part-pitch, and rotation from the package map.
openpnp-tools assign pnp/feeders.csv
openpnp-tools assign --reset-unused pnp/feeders.csv # reset unassigned feeders
openpnp-tools assign --dry-run pnp/feeders.csv # preview changesResets all strip feeder part-id to CALIBRATION-DUMMY and zeroes feed-count.
Leaves positions, calibration, tape-type, rotation, and pitch untouched.
openpnp-tools reset-feeders # reset ~/.openpnp2/machine.xml
openpnp-tools reset-feeders --machine path/to/machine.xml # reset a copyGenerates a self-contained HTML page showing feeder positions, job parts, missing feeders, and board outlines.
With --bom, adds an IPN column for cross-referencing against inventory
management (e.g. InvenTree).
openpnp-tools map -o pnp/feeder-map.html pnp/myboard.job.xml
# With IPN from KiCad BOM
kicad-cli sch export bom --fields "Value,Footprint,IPN" \
--labels "Value,Footprint,IPN" --group-by "Value,Footprint,IPN" \
--exclude-dnp -o pnp/bom.csv myboard.kicad_sch
openpnp-tools map --bom pnp/bom.csv -o pnp/feeder-map.html pnp/myboard.job.xmlSyncs the base config between a git repo and the live ~/.openpnp2 directory.
With repo-dir set in the settings file, all commands work from any directory.
openpnp-tools config backup # snapshot ~/.openpnp2
openpnp-tools config apply # backup + copy repo → ~/.openpnp2
openpnp-tools config pull # copy ~/.openpnp2 → repo + reset feeders
openpnp-tools config save # pull + commit + push in one step
openpnp-tools config status # process check + per-file drift
openpnp-tools config status -d # include unified diff for modified files- backup — copies all config XMLs to
~/.openpnp2/backups/<timestamp>/, using the same format as OpenPnP itself. - apply — backs up first, then copies managed files from the repo into
~/.openpnp2. OpenPnP must be closed. - pull — copies managed files from
~/.openpnp2into the repo, then runsreset-feederson the repo copy. OpenPnP must be closed. - save — pull + reset + git commit + push. Use
-mfor a custom message. - status — reports whether OpenPnP is running and shows per-file drift.
Use
-dto show the actual diff.
| Flag | Default | Description |
|---|---|---|
--machine |
~/.openpnp2/machine.xml |
Path to OpenPnP machine config |
--package-map |
<repo-dir>/openpnp-package-map.csv |
Path to package map CSV (falls back to ~/.openpnp2/) |
Optional config file to avoid repeating flags. Looked up in the --config-dir
directory (default: ~/.openpnp2).
repo-dir: /home/user/dev/openpnp-config| Key | Used by | Description |
|---|---|---|
repo-dir |
config apply/pull/save/status, --package-map |
Path to the config git repo |
With repo-dir set, the package map is read directly from the repo and all
config commands work from any directory. CLI flags always override.
Single source of truth for package metadata, shared across all projects.
Lives in the config repo (resolved via repo-dir).
kicad_footprint,openpnp_package,height,tape_type,part_pitch,tape_width,nozzle_tip,rotation_offset
C_0805_2012Metric,C_0805,0.9,WhitePaper,4,8,NT1,90
R_0805_2012Metric,R_0805,0.5,WhitePaper,4,8,NT1,90
SOT-23,SOT-23,1.1,BlackPlastic,4,8,NT1,0
SOIC-8_3.9x4.9mm_P1.27mm,SOIC-8,1.75,BlackPlastic,8,12,TIP16cbc9505c3e1916,0| Column | Description |
|---|---|
kicad_footprint |
KiCad footprint library name (exact match) |
openpnp_package |
Short OpenPnP package name |
height |
Component height in mm |
tape_type |
WhitePaper, ClearPlastic, or BlackPlastic |
part_pitch |
Distance between parts on tape in mm |
tape_width |
Tape width in mm (8, 12, 16) |
nozzle_tip |
OpenPnP nozzle tip ID |
rotation_offset |
Degrees for "Rotation in Tape" in OpenPnP |
Lines starting with # are comments. Rows with empty tape columns are hand-place
components — they are mapped but get no feeder metadata, and generate marks
their placements as disabled.
Per-project file mapping feeder slots to parts:
feeder,part
LV08-01,C_0805-100n
RV08-02,SOT-23-2N7002
RH12-01,SOT-223-NCP1117-3.3_SOT223| Column | Description |
|---|---|
feeder |
Feeder slot name (e.g. LV08-01, RH12-03) |
part |
Part ID: {Package}-{Value} matching the board XML |
make setup # apply base config + assign feeders
# open OpenPnP, run the job# close OpenPnP
make save # pull tuning + commit + pushmake pnp # regenerate board.xml + ensure parts
make setup # apply + assign (update feeders.csv first if new parts)PROJECT = myboard
pnp:
kicad-cli pcb export pos --format csv --side both --units mm \
--smd-only --exclude-dnp $(PROJECT).kicad_pcb
openpnp-tools generate -o pnp -n $(PROJECT) $(PROJECT).csv
openpnp-tools ensure-parts pnp/$(PROJECT).board.xml
setup:
openpnp-tools config apply
openpnp-tools assign --reset-unused pnp/feeders.csv
save:
openpnp-tools config save -m "tuning: update from $(PROJECT)"
feeder-map:
kicad-cli sch export bom --fields "Value,Footprint,IPN" \
--labels "Value,Footprint,IPN" --group-by "Value,Footprint,IPN" \
--exclude-dnp -o pnp/bom.csv $(PROJECT).kicad_sch
openpnp-tools map --bom pnp/bom.csv \
-o pnp/feeder-map.html pnp/$(PROJECT).job.xmlRequires Go 1.26+:
make build # build binary
make test # run tests
make lint # run linterGranit is a hardware project using openpnp-tools for its pick-and-place workflow. See its hardware/Makefile and pnp/ directory for a real-world setup. The interactive feeder map is also deployed to the site.
The interactive feeder map was inspired by psypnp by Pat Deegan — a collection of OpenPnP scripting utilities for feeder management and job setup.
GPL-3.0-or-later — aligned with OpenPnP.