Last active
October 17, 2018 23:38
-
-
Save projectgus/cbf15d2e9eab881e653a47ca2d2e0ea9 to your computer and use it in GitHub Desktop.
Hacky way to get a BoM text file from a Kicad Netlist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Quick and rough KiCAD Netlist to text & CSV BoM file converter | |
# | |
# Usage: net2bom.py <project.net> | |
# | |
# Outputs: | |
# - project_bom.txt | |
# - project_bom.csv | |
# - project_bom_grouped.csv (has all of same value/footprint grouped on the same line) | |
# | |
# Note: Consider using KiCad's built-in tools (which are better than when I wrote this script) or | |
# use https://github.com/openscopeproject/InteractiveHtmlBom for 1000x more features | |
# | |
import csv,sys,os,collections,itertools, re | |
components = [] | |
component = None | |
item = 1 | |
FIELDS = { | |
"item" : "item", | |
"Reference" : "ref", | |
"ValeurCmp" : "value", | |
"IdModule" : "footprint" | |
} | |
netfile = sys.argv[1] | |
bomfile = os.path.splitext(os.path.basename(netfile))[0] + "_bom.txt" | |
csvfile = os.path.splitext(os.path.basename(netfile))[0] + "_bom.csv" | |
grouped_bomfile = os.path.splitext(os.path.basename(netfile))[0] + "_bom_grouped.csv" | |
# worst ever, just parsing line by line instead of parsing s-expressions... | |
with open(netfile) as f: | |
for line in f: | |
if "comp (ref" in line: | |
if component is not None: | |
components.append(component) | |
ref = re.search(r"ref (.+)\)",line).group(1) | |
component = { "ref" : ref, "item" : int(item) } | |
item += 1 | |
elif component is not None: | |
if "value" in line and not "value" in component: | |
component["value"] = re.search(r'value "?(.+?)"?\)',line).group(1) | |
elif "footprint" in line and not "footprint" in component and not "footprints" in line: | |
component["footprint"] = re.search(r'footprint "?(.+?)"?\)',line).group(1) | |
if component is not None: | |
components.append(component) | |
components = [ c for c in components if c["value"] != "SJ" ] | |
# remove library prefixes from footprint names, clean up stray commas | |
for component in components: | |
component["footprint"] = component.get("footprint", "").split(":")[-1] | |
component["value"] = component.get("value", "").replace(",", " ") | |
# convert purely numeric footprints to give an indicator of type | |
for component in components: | |
try: | |
if not int(component["footprint"]): | |
continue | |
if component["ref"].startswith("C"): | |
component["footprint"] = "CAP" + component["footprint"] | |
elif component["ref"].startswith("R"): | |
component["footprint"] = "RES" + component["footprint"] | |
elif component["ref"].startswith("L"): | |
component["footprint"] = "IND" + component["footprint"] | |
except ValueError: | |
continue | |
#%(item)8s | |
FORMAT="%(ref)8s%(value)20s%(footprint)20s\n" | |
CSV_FORMAT="%(ref)s,%(value)s,%(footprint)s\n" | |
# write plaintext BOM & CSV BOM | |
with open(bomfile, "w") as f: | |
with open(csvfile, "w") as csv: | |
header = dict((x,x) for x in [ "item","ref","value","footprint", "notes" ]) | |
f.write(FORMAT % header) | |
csv.write(",".join(header) + "\n") | |
for component in components: | |
component["item"] = item | |
item += 1 | |
f.write(FORMAT % component) | |
csv.write(CSV_FORMAT % component) | |
# create grouped BO | |
grouping_key = lambda i: (i["footprint"],i["value"]) | |
grouped = itertools.groupby(sorted(components, key=grouping_key), key=grouping_key) | |
result = [] | |
for k,g in grouped: | |
g = list(g) | |
result.append((" ".join(i["ref"] for i in g), str(len(g)), k[1], k[0])) | |
with open(grouped_bomfile, "w") as f: | |
f.write("Refs,Quantity,Value,Footprint\n") | |
for line in sorted(result): | |
f.write(",".join(line) + "\n") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment