Skip to content

Instantly share code, notes, and snippets.

@arturo182
Last active May 20, 2024 04:57
Show Gist options
  • Save arturo182/a8c4a4b96907cfccf616a1edb59d0389 to your computer and use it in GitHub Desktop.
Save arturo182/a8c4a4b96907cfccf616a1edb59d0389 to your computer and use it in GitHub Desktop.
A KiCad BOM script for generating JLCPCB PCBA-compatible files!
<!--XSL style sheet to convert EESCHEMA XML Partlist Format to grouped CSV BOM Format
Copyright (C) 2014, Wolf Walter.
Copyright (C) 2013, Stefan Helmert.
Copyright (C) 2018, Kicad developers.
Copyright (C) 2019, arturo182.
GPL v2.
Functionality:
Generation of JLCPCB PCBA compatible BOM
How to use this is explained in eeschema.pdf chapter 14. You enter a command line into the
netlist exporter using a new (custom) tab in the netlist export dialog.
The command line is
xsltproc -o "%O.csv" "FullPathToFile/bom2grouped_csv_jlcpcb.xsl" "%I"
-->
<!--
@package
Generates a JLCPCB PCBA service compatible BOM
Functionality:
* Generate a comma separated value BOM list (csv file type).
* Components are sorted by ref and grouped by same value+footprint
One value per line
Fields are
Comment,Designator,Footprint,LCSC
The command line is
xsltproc -o "%O.csv" "full_path/bom2grouped_csv_jlcpcb.xsl" "%I"
-->
<!DOCTYPE xsl:stylesheet [
<!ENTITY nl "&#xd;&#xa;"> <!--new line CR, LF, or LF, your choice -->
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:variable name="digits" select="'1234567890'" />
<!-- for matching grouping of footprint and value combination -->
<xsl:key name="partTypeByValueAndFootprint" match="comp" use="concat(footprint, '-', value)" />
<!-- for table head and empty table fields-->
<xsl:key name="headentr" match="field" use="@name"/>
<!-- main part -->
<xsl:template match="/export">
<xsl:text>Comment,Designator,Footprint,LCSC</xsl:text>
<!-- all table entries -->
<xsl:apply-templates select="components"/>
</xsl:template>
<xsl:template match="components">
<!-- for Muenchian grouping of footprint and value combination -->
<xsl:for-each select="comp[count(. | key('partTypeByValueAndFootprint', concat(footprint, '-', value))[1]) = 1]">
<xsl:sort select="@ref" />
<xsl:text>&nl;</xsl:text>
<xsl:text>"</xsl:text><xsl:value-of select="value"/><xsl:text>","</xsl:text>
<!-- list of all references -->
<xsl:for-each select="key('partTypeByValueAndFootprint', concat(footprint, '-', value))">
<!-- strip non-digits from reference and sort based on remaining number -->
<xsl:sort select="translate(@ref, translate(@ref, $digits, ''), '')" data-type="number" />
<xsl:value-of select="@ref"/>
<xsl:if test="position() != last()"><xsl:text>,</xsl:text></xsl:if>
</xsl:for-each>
<xsl:text>","</xsl:text>
<xsl:value-of select="footprint"/><xsl:text>","</xsl:text>
<xsl:value-of select="fields/field[@name='LCSC']"/><xsl:text>"</xsl:text>
</xsl:for-each>
</xsl:template>
<!-- table entries with dynamic table head -->
<xsl:template match="fields">
<!-- remember current fields section -->
<xsl:variable name="fieldvar" select="field"/>
<!-- for all existing head entries -->
<xsl:for-each select="/export/components/comp/fields/field[generate-id(.) = generate-id(key('headentr',@name)[1])]">
<xsl:variable name="allnames" select="@name"/>
<xsl:text>,"</xsl:text>
<!-- for all field entries in the remembered fields section -->
<xsl:for-each select="$fieldvar">
<!-- only if this field entry exists in this fields section -->
<xsl:if test="@name=$allnames">
<!-- content of the field -->
<xsl:value-of select="."/>
</xsl:if>
<!--
If it does not exist, use an empty cell in output for this row.
Every non-blank entry is assigned to its proper column.
-->
</xsl:for-each>
<xsl:text>"</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
@gregsimon
Copy link

The script has trouble with spaces on Windows. Move it to a (full path) without spaces worked for me.

@heidnerd
Copy link

heidnerd commented Oct 16, 2021

The script has trouble with spaces on Windows. Move it to a (full path) without spaces worked for me.

The problem is xsltproc can't support spaces and some of the other windows path name conventions. The script itself is okay.

We should be reporting the xsltproc problem with windows to their bug reporting site.

http://www.xmlsoft.org/bugs.html

@tormyvancool
Copy link

The script has trouble with spaces on Windows. Move it to a (full path) without spaces worked for me.

The problem is xsltproc can't support spaces and some of the other windows path name conventions. The script itself is okay.

We should be reporting the xsltproc problem with windows to their bug reporting site.

http://www.xmlsoft.org/bugs.html

Ok I sent an email posting the link here and asking for the support of spaces and special characters.
We are in 2021, and these not-compatibilities should be just an ugly memory of the past

@gcormier
Copy link

gcormier commented Dec 2, 2021

inspired me to make this one

https://gist.github.com/gcormier/61addd28226bfd4ed6f645f312c3f13a

it will export your BOM and also update your POS file if it already exists to the JLCPCB header format

@abelflix
Copy link

abelflix commented Sep 6, 2022

Hi, I get this issue trying to generate the BOM. At least, more than 60% of the components are in stock at JLCPCBs part library. I don't want the rest of the parts to be assembled by JLCPCB since I already have the components.

imagen

@1technick
Copy link

Hi, It fails with a file error in version 6.0.9. 6.0.8 was ok. (windows 10 64)
Kicad plans to support only python in future so this may not work at all soon.
In my opinion it is much easier to debug python code so that is what i will do ;-)

@GadgetNutt
Copy link

GadgetNutt commented Mar 14, 2023

Hi.` The xsltproc appears to have been removed from KiCAD 7. Here is a solution:

Keep the instructions currently, but one slight change. You will need to install lxml in Python with command "python install lxml". Create a new .py file with the same name and put it in the same folder as bom2grouped_csv_jlcpcb.xsl and create the file with:

import lxml.etree as ET
import csv
import sys

# Get the command line arguments
xml_path = sys.argv[1]
csv_path = sys.argv[2]

def generate_bom(xml_path, csv_path):
        # Load the EESCHEMA XML Partlist Format file
        tree = ET.parse(xml_path)
        root = tree.getroot()
        
        # Define the namespace for the XML file
        ns = {"xsl": "http://www.w3.org/1999/XSL/Transform"}
        
        # Load the XSL stylesheet to transform the XML file to CSV
        xslt_tree = ET.parse("<setpathto>/KiCad/6.0/plugins/bom2grouped_csv_jlcpcb.xsl")
        transform = ET.XSLT(xslt_tree)
        
        # Apply the transformation to the XML file
        csv_data = transform(root)
        
        # Convert the XSLTResultTree object to a string and split into lines
        csv_data_str = str(csv_data)
        csv_lines = csv_data_str.splitlines()
        
        # Write the transformed CSV data to file
        with open(csv_path, "w", newline="") as csvfile:
        writer = csv.writer(csvfile, delimiter=",", quotechar="'", quoting=csv.QUOTE_NONE)
        for line in csv_lines:
        writer.writerow(line.split(","))

# Example usage
generate_bom(xml_path, csv_path)

and set the commandline to:
python "\KiCad\6.0\plugins\bom2grouped_csv_jlcpcb.py" "%I" "%O.csv"

@amazed-2022
Copy link

amazed-2022 commented Mar 18, 2023

Hi.` The xsltproc appears to have been removed from KiCAD 7. Here is a solution:

Keep the instructions currently, but one slight change. Create a new .py file with the same name and put it in the same folder as bom2grouped_csv_jlcpcb.xsl and create the file.

and set the commandline to: python "\KiCad\6.0\plugins\bom2grouped_csv_jlcpcb.py" "%I" "%O.csv"

Hello, thanks for the update, unfortunately it is not working for me. :(
I get the following error:

    tree = ET.parse(xml_path)
    ^
IndentationError: expected an indented block

How could I make it work?

@tormyvancool
Copy link

Hi.` The xsltproc appears to have been removed from KiCAD 7. Here is a solution:
Keep the instructions currently, but one slight change. Create a new .py file with the same name and put it in the same folder as bom2grouped_csv_jlcpcb.xsl and create the file.
and set the commandline to: python "\KiCad\6.0\plugins\bom2grouped_csv_jlcpcb.py" "%I" "%O.csv"

Hello, thanks for the update, unfortunately it is not working for me. :( I get the following error:

    tree = ET.parse(xml_path)
    ^
IndentationError: expected an indented block

How could I make it work?

If you install this, it should work fine. The full package for JLCPCB is created without any issue and in a subdir called "Production"
image

@amazed-2022
Copy link

If you install this, it should work fine. The full package for JLCPCB is created without any issue and in a subdir called "Production"

Thank you very much for the hint!

@JosephLL753
Copy link

I am still lost with this.

I have installed the Fabrication Toolkit, and created the .py file with the name bom2grouped_csv_jlcpcb.py, but I am still receiving the error code:
tree = ET.parse(xml_path)
^
IndentationError: expected an indented block

@amazed-2022
Copy link

amazed-2022 commented Mar 29, 2023

I am still lost with this.

You don't need the bom2grouped_csv_jlcpcb stuff at all!

Just use the "Fabrication Toolkit" button in the PCB Editor.

kép

@GadgetNutt
Copy link

I am still lost with this.

I have installed the Fabrication Toolkit, and created the .py file with the name bom2grouped_csv_jlcpcb.py, but I am still receiving the error code: tree = ET.parse(xml_path) ^ IndentationError: expected an indented block

Did you install Python and the lxml library?

@tormyvancool
Copy link

I am still lost with this.

You don't need the bom2grouped_csv_jlcpcb stuff at all!

Just use the "Fabrication Toolkit" button in the PCB Editor.

kép

Indeed I also posted how to get it (see a couple of replies above your one) ... but it seems ppl is not reading instructions

@GadgetNutt
Copy link

I'm currently using the other method. I'll probably switch soon. In the meantime I responded because someone asked.

@ep201
Copy link

ep201 commented Apr 7, 2023

How do you add this script to kicad? instructions are not clear


Found out how to add it, there is a Bill of Materials button on the top toolbar in schematic editor in KiCad 6, next to the board view button.
image

Now, I'm getting this error, same as outlined in this forum post: https://forum.kicad.info/t/command-error-return-code-11-to-generate-bom/5447/12
can we get a fix for this?
image
It may be an issue with directories with spaces. The directory after C:/Users/ in the paths in this screenshot are formatted "[Firstname] [Lastname]" , with a space between first and last, and I encounter the same issue.


edit 2: fixed issue by replacing %O in the command line with a file name, e.g. "bom.csv" instead of "%O.csv"
edit 3: actually, no matter which project I run, now any bom file is only exported to the original folder, not the destination project folder, no matter how I name the file destination.

@tormyvancool
Copy link

tormyvancool commented Apr 8, 2023

How do you add this script to kicad? instructions are not clear

Drop thi s script. it's now overtaken by the one I mentioned above.
And you install it via the plugin manager

image

@Mk-arc
Copy link

Mk-arc commented Apr 13, 2023

The toolkit is great. But please not that it first deletes the complete content of the production folder without warning :( caused some data loss for me.

@ramonimbao
Copy link

For anyone who wants the .py file by @GadgetNutt to work, it's missing indentations.

Here's how the source file should look:

import lxml.etree as ET
import csv
import sys

# Get the command line arguments
xml_path = sys.argv[1]
csv_path = sys.argv[2]

def generate_bom(xml_path, csv_path):
	# Load the EESCHEMA XML Partlist Format file
	tree = ET.parse(xml_path)
	root = tree.getroot()

	# Define the namespace for the XML file
	ns = {"xsl": "http://www.w3.org/1999/XSL/Transform"}

	# Load the XSL stylesheet to transform the XML file to CSV
	xslt_tree = ET.parse("C:/Program Files/KiCad/bin/scripting/plugins/bom2grouped_csv_jlcpcb.xsl")
	transform = ET.XSLT(xslt_tree)

	# Apply the transformation to the XML file
	csv_data = transform(root)

	# Convert the XSLTResultTree object to a string and split into lines
	csv_data_str = str(csv_data)
	csv_lines = csv_data_str.splitlines()

	# Write the transformed CSV data to file
	with open(csv_path, "w", newline="") as csvfile:
		writer = csv.writer(csvfile, delimiter=",", quotechar="'", quoting=csv.QUOTE_NONE)
		for line in csv_lines:
			writer.writerow(line.split(","))

# Example usage
generate_bom(xml_path, csv_path)

The location of the bom2group_csv_jlcpcb.xsl might be different for you so be sure to change that as necessary.

I also had to open a KiCAD Command Prompt and run python install lxml before it worked since it was missing the module.

Tested working on a fresh install of KiCAD 7.0.2.

@tormyvancool
Copy link

The toolkit is great. But please not that it first deletes the complete content of the production folder without warning :( caused some data loss for me.

+1 for that. On top of this it will be nice to have the PROJECT_NAME into the file names ... if it puts Date and Hours to keep kind of versioning, it will be nice

@rtek1000
Copy link

rtek1000 commented Jul 4, 2023

Hi.` The xsltproc appears to have been removed from KiCAD 7. Here is a solution:
Keep the instructions currently, but one slight change. Create a new .py file with the same name and put it in the same folder as bom2grouped_csv_jlcpcb.xsl and create the file.
and set the commandline to: python "\KiCad\6.0\plugins\bom2grouped_csv_jlcpcb.py" "%I" "%O.csv"

Hello, thanks for the update, unfortunately it is not working for me. :( I get the following error:

    tree = ET.parse(xml_path)
    ^
IndentationError: expected an indented block

How could I make it work?

If you install this, it should work fine. The full package for JLCPCB is created without any issue and in a subdir called "Production" image

Thanks!

@rtek1000
Copy link

rtek1000 commented Jul 4, 2023

Hi.` The xsltproc appears to have been removed from KiCAD 7. Here is a solution:
Keep the instructions currently, but one slight change. Create a new .py file with the same name and put it in the same folder as bom2grouped_csv_jlcpcb.xsl and create the file.
and set the commandline to: python "\KiCad\6.0\plugins\bom2grouped_csv_jlcpcb.py" "%I" "%O.csv"

Hello, thanks for the update, unfortunately it is not working for me. :( I get the following error:

    tree = ET.parse(xml_path)
    ^
IndentationError: expected an indented block

How could I make it work?

If you install this, it should work fine. The full package for JLCPCB is created without any issue and in a subdir called "Production" image

Thanks!

Ref.:
[YouTube: How to Place a PCB Assembly Order at JLCPCB]

Note: Generating Pick and Place files (CPL file (.pos), already included in the package "Production"):

The CPL file has to be generated from the PCB editor, click on "File" -> "Fabrication output" -> "Footprint position (.pos) file" and export the file with the following settings. Source.

Note: "2. Notes on DFM - 1.The design of the PCB footprints must comply with "medium density" or "low density" standards listed in IPC-7351B. High density is not supported." (Terms and Conditions of JLCPCB Assembly Service)

Ref.:

Manufacturing defects caused by PCB Design

@hasenradball
Copy link

Hi Arthur,

with the KICAD 7.0 I got an error, and I am not clear about the root cause here.
Can You probably give an Hint?

grafik

@UnforeseenOcean
Copy link

UnforeseenOcean commented Sep 6, 2023

Hi Arthur,

with the KICAD 7.0 I got an error, and I am not clear about the root cause here. Can You probably give an Hint?

grafik

This is due to the xsltproc executable being omitted from the installation. I had to transplant it from QMK (the executable) and Git (DLL files) on Windows to get it working. (Not sure on Ubuntu which you seem to be using, please consult your system files)

You need both the library and executable on Windows. Copy over all files named xsltproc.exe and msys-*.dll from Git and QMK into KiCad/7.0/bin/ folder to get it working. It's quite annoying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment