115 lines
5.4 KiB
Markdown
115 lines
5.4 KiB
Markdown
---
|
|
name: kicad-bom-edit
|
|
description: Use when adding or editing BOM/part fields (MPN, LCSC, Manufacturer, tolerance, voltage/current ratings, DNP) on KiCad .kicad_sch symbols by hand or script, or when a kicad-cli BOM export is missing parts. Covers safe property insertion, the "malformed property silently drops a whole sheet" pitfall, and the netlist-vs-BOM count verification that catches it. KiCad 7/8/9/10 S-expression schematics.
|
|
---
|
|
|
|
# Editing KiCad schematics for BOM
|
|
|
|
Adding manufacturer part numbers (MPN), LCSC codes, ratings, etc. to a KiCad
|
|
`.kicad_sch` so the BOM can be ordered/assembled (e.g. JLCPCB). **Prefer the
|
|
KiCad GUI** (Symbol Fields Table / per-symbol properties) — it can't corrupt the
|
|
file. Only hand/script-edit when the GUI isn't available, and then follow this
|
|
exactly.
|
|
|
|
## How symbols store fields
|
|
|
|
Each placed symbol is `(symbol (lib_id ...) (at ...) ... <properties> <pins> (instances ...))`.
|
|
A field is a **property**, a direct child of the symbol:
|
|
|
|
```
|
|
(property "MPN" "FNR4018S3R3MT"
|
|
(at 139.7 87.63 0)
|
|
(hide yes)
|
|
(show_name no)
|
|
(do_not_autoplace no)
|
|
(effects (font (size 1.27 1.27)))
|
|
)
|
|
```
|
|
|
|
Properties `Reference`, `Value`, `Footprint`, `Datasheet`, `Description` are
|
|
mandatory; `MPN`/`LCSC`/`Manufacturer`/`V`/`Type`/`Current`/etc. are custom BOM
|
|
fields. Field **names must be unique** within a symbol. `kicad-cli sch export
|
|
bom --fields '...'` selects which show up as columns.
|
|
|
|
## ⚠️ THE PITFALL: a malformed property silently drops the WHOLE sheet
|
|
|
|
New BOM fields must be **siblings** of `Value` (direct children of the symbol) —
|
|
NOT nested inside another property. If you accidentally place them *inside* the
|
|
`Value` property, `kicad-cli sch export bom` will **silently omit every component
|
|
on that sheet** — no warning, exit code 0 — while `kicad-cli sch export netlist`
|
|
still parses fine and shows nothing wrong. A whole page of parts (regulators,
|
|
connectors…) can vanish from the BOM you send to the fab.
|
|
|
|
**Root cause seen in practice:** naive regex insertion. Property indentation
|
|
**varies by file** (some use 2 tabs for `(property`, some 1). A regex like
|
|
`.*?\n\t\t\t\)` grabs the first 3-tab close — which is the inner `effects`/`font`
|
|
close, not the property's own close — so the insert lands *inside* `Value`.
|
|
|
|
## Safe insertion (script)
|
|
|
|
1. **Match the entire target property block by balanced parens**, keyed on its
|
|
unique content (e.g. `Value` = "2.2uH" at a known `(at ...)`), from the
|
|
`(property "Value"` line to *its own* closing paren at the property's indent
|
|
level. Don't rely on a fixed tab count guessed from another file — detect the
|
|
file's actual property indentation first.
|
|
2. Insert new `(property ...)` blocks **after** that close, at the **same indent
|
|
level** as `Value` (siblings), with children one level deeper.
|
|
3. Keep every block paren-balanced (each `(property` / `(effects` / `(font` has
|
|
its close).
|
|
|
|
Robust pattern (Python) — replace the whole Value block and append siblings:
|
|
|
|
```python
|
|
import re
|
|
src=open(f).read()
|
|
# whole L1 Value property: 2-tab property line, 3+tab children, 2-tab close
|
|
pat=re.compile(r'\t\t\(property "Value" "2\.2uH"\n(?:\t\t\t.*\n)*?\t\t\)\n')
|
|
m=pat.search(src)
|
|
def prop(n,v):
|
|
return (f'\t\t(property "{n}" "{v}"\n\t\t\t(at 139.7 87.63 0)\n\t\t\t(hide yes)\n'
|
|
f'\t\t\t(show_name no)\n\t\t\t(do_not_autoplace no)\n\t\t\t(effects\n\t\t\t\t(font\n'
|
|
f'\t\t\t\t\t(size 1.27 1.27)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n')
|
|
block=m.group(0).replace('"2.2uH"','"3.3uH"') # keep Value block intact
|
|
add=prop("MPN","FNR4018S3R3MT")+prop("LCSC","C167805")+prop("Manufacturer","cjiang")
|
|
src=src[:m.start()]+block+add+src[m.end():]
|
|
open(f,'w').write(src)
|
|
```
|
|
|
|
(Whitespace inside S-expressions is not significant to KiCad — matching indent
|
|
is only for legibility and to hit the right closing paren. KiCad rewrites
|
|
formatting on its next save.)
|
|
|
|
## ✅ ALWAYS verify: netlist component count must equal BOM row count
|
|
|
|
This is the check that catches the silent-drop bug. After any schematic edit:
|
|
|
|
```bash
|
|
kicad-cli sch export netlist --format kicadsexpr -o /tmp/n.net project.kicad_sch
|
|
kicad-cli sch export bom --fields 'Reference,Value,MPN,LCSC' -o /tmp/b.csv project.kicad_sch
|
|
# count components in each; they MUST match
|
|
```
|
|
|
|
Parse the netlist `(components (comp (ref ...)))` for the ref set, and the BOM
|
|
CSV first column for its ref set, and print the difference. If the BOM set is
|
|
smaller, some sheet was dropped — you have a malformed property. Confirm the
|
|
specific row (`grep '"L1"' /tmp/b.csv`) shows its new fields populated.
|
|
|
|
## Dual-project-instance sheets
|
|
|
|
A sub-sheet that was once its own standalone project (e.g. `Power.kicad_sch`
|
|
reused in `re-bba-rb`) has `(instances (project "power" ...) (project
|
|
"re-bba-rb" ...))` on each symbol. This is normal, but it makes such sheets
|
|
extra-sensitive to malformed properties in BOM export — always run the
|
|
count check after editing these.
|
|
|
|
## Part selection for JLCPCB / LCSC
|
|
|
|
- Fill `MPN`, `Manufacturer`, and the **`LCSC` C-number** (e.g. `C167805`) — the
|
|
LCSC code is what JLC assembly needs. Don't invent C-numbers; verify each on
|
|
lcsc.com / jlcpcb.com/parts (existence, stock, **Basic vs Extended** — Extended
|
|
= small one-time fee).
|
|
- For power inductors, record and check **Isat** (saturation ≥ peak current) and
|
|
**Irms**; a value/footprint alone isn't enough.
|
|
- Match the field-naming already used in the project (this repo uses `V`,
|
|
`Type`, `Current`, `MPN`, `LCSC`, `Manufacturer`).
|