Files
2026-07-24 14:04:04 +02:00

6.3 KiB

name, description
name description
kicad-manufacturing-export Use when regenerating or exporting the BOM CSV, CPL/position CSV, or Gerbers+drill zip for a KiCad PCB project via kicad-cli (e.g. "regenerate the gerbers", "update the BOM and CPL", "re-export for JLCPCB"). Covers the exact kicad-cli invocations and column formats this project's ordering files expect, and the CPL rotation-correction trap (regenerating the position file the naive way silently wipes hand-verified assembly rotation fixes). KiCad 8/9/10, JLCPCB-style deliverables.

Regenerating BOM / CPL / Gerbers via kicad-cli

For the re-bba-rb board specifically, deliverables live in hardware/re-bba-rb/: re-bba-rb-bom.csv, re-bba-rb-cpl.csv, gerbers/rebbarb.zip. Adjust paths/output names for other boards, but the gotchas below (especially the CPL rotation trap) apply generally.

1. Gerbers + drill + zip

cd hardware/re-bba-rb
kicad-cli pcb export gerbers --board-plot-params -o gerbers/ re-bba-rb.kicad_pcb
kicad-cli pcb export drill --format excellon --excellon-separate-th \
  --excellon-units mm -o gerbers/ re-bba-rb.kicad_pcb

--board-plot-params reuses whatever layer/plot settings are already stored in the board file — don't hand-pick layers, this reproduces exactly what was there before. --excellon-separate-th splits plated/non-plated holes into *-PTH.drl / *-NPTH.drl, matching the existing file-naming convention.

Then re-zip (excluding the previous zip itself, so it's not nested inside its own replacement):

import zipfile, os
folder = 'gerbers'
files = sorted(f for f in os.listdir(folder) if f != 'rebbarb.zip')
with zipfile.ZipFile(os.path.join(folder, 'rebbarb.zip'), 'w', zipfile.ZIP_DEFLATED) as z:
    for f in files:
        z.write(os.path.join(folder, f), arcname=f)

kicad-cli's gerber export also produces a *-job.gbrjob metadata file alongside the layer files — include it in the zip, it's harmless and some fabs use it for auto-configuration.

2. BOM

kicad-cli sch export bom \
  --fields "ITEM_NUMBER,Reference,Value,Footprint,MPN,LCSC,QUANTITY,DNP,Manufacturer" \
  --labels "Item,Designator,Value,Footprint,MPN,LCSC,Qty,DNP,Manufacturer" \
  --group-by "Value,Footprint,MPN,LCSC,Manufacturer" \
  --sort-field "Reference" \
  --ref-range-delimiter "" \
  -o re-bba-rb-bom.csv \
  re-bba-rb.kicad_sch

--ref-range-delimiter "" disables range compression (C18-C20 becomes C18,C19,C20) — this project's existing BOM convention is flat comma lists, not ranges; drop this flag if the target format wants ranges instead. --group-by on those 5 fields means any two components sharing value + footprint + MPN + LCSC + manufacturer collapse into one row (with a combined Qty) — this is how e.g. adding a new part that happens to match an already-used part (same value/footprint/MPN) merges into an existing BOM line instead of creating a new one, which is usually what you want (no new line item added to the order).

Reference-only / non-purchasable footprints (gold-finger connectors, mouse-bite NPTH-hole footprints, etc.) are excluded automatically via in_bom no on the schematic symbol (or exclude_from_bom in the footprint's attr) — don't try to filter them out in the export command itself.

Verify after export: diff the new BOM against the old one and sanity-check that only expected rows changed — an unexpectedly shorter BOM (fewer component rows/refs than before) is the signature of the kicad-bom-edit skill's "malformed property silently drops a whole sheet" bug, not a real BOM change.

3. CPL / position file — the rotation trap

kicad-cli pcb export pos --format csv --units mm --side both --exclude-dnp \
  -o /tmp/pos_raw.csv re-bba-rb.kicad_pcb

kicad-cli's own CSV columns (Ref,Val,Package,PosX,PosY,Rot,Side) don't match JLCPCB's expected format (Designator,Mid X,Mid Y,Layer,Rotation, with Layer capitalized Top/Bottom, not lowercase top/bottom). Transform:

import csv
with open('/tmp/pos_raw.csv', newline='') as f:
    rows = list(csv.DictReader(f))
with open('re-bba-rb-cpl.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['Designator', 'Mid X', 'Mid Y', 'Layer', 'Rotation'])
    for r in rows:
        layer = 'Top' if r['Side'].strip().lower() == 'top' else 'Bottom'
        writer.writerow([r['Ref'], f"{float(r['PosX']):.6f}",
                          f"{float(r['PosY']):.6f}", layer,
                          f"{float(r['Rot']):.6f}"])

Then — before treating this as done — reapply every correction in hardware/re-bba-rb/CPL_ROTATIONS.md. KiCad's raw per-footprint rotation frequently does not match what the assembler expects (arbitrary per-footprint authoring reference vs. the assembler's actual placement convention), and this has already been hand-verified part-by-part against JLCPCB's placement-preview render for this board. Regenerating the position file "the normal way" silently overwrites those fixes back to (wrong) raw values — there is no automated check that catches this, so it's easy to quietly ship a regression. Read that file's sign-convention note before applying anything (short version: "N degrees clockwise" feedback means subtract N in the CPL's Y-up frame, not add it).

Do not reach for a generic community rotation-correction table (e.g. cpl_rotations_db.csv from JLCKicadTools) as a shortcut — one was tried on this board and found wrong for at least one package family (claimed LQFP-: 270°; this board's actual correct value is 0°, unmodified). That table is calibrated against a specific plugin's own raw-rotation baseline, which is not the same as kicad-cli's raw CSV baseline (different Y-axis handling) — the numbers don't transfer.

Quick full-refresh recipe

  1. Gerbers/drill/zip: regenerate + re-zip (section 1) — safe to always redo, no hidden state to lose.
  2. BOM: regenerate (section 2) — safe to always redo. Diff-check the row count.
  3. CPL: regenerate raw (section 3), then reapply CPL_ROTATIONS.md before calling it current.
  4. Run ERC (kicad-cli sch erc --severity-all) and DRC (kicad-cli pcb drc --severity-all) after any schematic/PCB change that prompted the refresh — not the export step itself, but worth confirming nothing regressed if this was bundled with other edits.