Table of Contents
Introduction
When prototyping, I usually order PCBs together with small, frameless stencils (usually from Eurocircuits if I'm in a rush and from seeed or JLC when I can wait a few weeks). Previously, for applying solder paste to the PCB, I've used different arrangements of old PCBs taped to my desk and the stencil taped over all of it and all of it was quite cumbersome. For a recent project I manually designed the printed part which holds the PCB and stencil in place and it proved to be of great help. Since I have few boards to assemble and test in the upcoming weeks I decided to automate the whole process.
Stencilframer is a Python script which takes the KiCAD PCB or Gerber file (outline, Edge_Cuts layer) as an input and, using OpenSCAD in the background, produces STL/AMF mesh which can be sliced and 3D printed. It can also export PNG image or the actual OpenSCAD code used for the model (for further adjustments). Only dependencies for running it are Python and OpenSCAD
UPDATE 2023/10/29: Stencilframer code updated to support KiCAD 6.0 and newer (I've also completely rewritten the parsing of the KiCAD PCB file, so it should be more robust now)
Video
How it works
Stencilframer script takes the KiCAD PCB or gerber (outline/Edge_Cuts layer, supported extensions are .kicad_pcb, .gbr, .gm1) file and extracts the shapes from the Edge.Cuts layer (i.e. board outline). Then it converts it into OpenSCAD polygon (list of vertices) which is extruded to get the shape of the PCB (arcs are replaced by a series of short straight lines). Based on the PCB and the stencil margins (margin between PCB edge and stencil edge can be adjusted for each side) it also creates and extrudes the shape for the stencil. A small offset (0.1mm default) is added to both shapes. Those two shapes are subtracted from the base element and some cutouts are added to simplify taking out the PCB and stencil.
In addition, a separate frame/cover can be generated (by using the --frame
argument) which is placed on top of the stencil to keep it in place.
When the OpenSCAD code is generated, the OpenSCAD executable is called in order to generate the output files. The format of the output file is set based on the output filename extension.
If the PCB has some cutouts (i.e. more than one closed shape on the Edge.Cuts layer) the script will recognize more than one shape in the file and desired shape can be selected using the --shape
argument.
Usage
The script allows most of the parameters to be set from command line
Alternative use
Since the holder has a slot in which the PCB fits so perfectly, by skipping holes for easy removal and adjusting margins, it can be turned into nice frame for showing off your PCBs :)
Code
Code is available at https://bitbucket.org/igor_b/stencilframer/
Comments
35 Comments
Needs a couple small tweaks in process_kicad_layer function to run with format changes for 5.99 nightly builds: []
def process_kicad_layer(outline, arc_subdivision=1):
paths = []
for ln in outline:
if "(layer \"Edge.Cuts\")" not in ln:
# extract only the Edge.Cuts layer
continue
p = {
'type': ln.strip("()").split(' ')[0].split('_')[-1]
}
if p['type'] not in ('line', 'arc', 'rect'):
continue
for el in re.findall(r'\([0-9a-zA-Z\. -]*\)', ln):
el_p = el.strip("()").split(' ')
if el_p[0] in ('start', 'end'):
p[el_p[0]] = tuple((float(pp) for pp in el_p[1:]))
# transform data
# do the rotation
p['center'] = p['start']
p['start'] = rotate_point(point=p['end'], center=p['start'], angle_deg=-p['angle']) # angle has wrong orientation
logging.debug("interpolating arc from %s to %s", p['start'], p['end'])
if p['type']=='rect':
#add lines between all points on rect
p['type']='line'
#X2Y1 to X2Y2
tp = p.copy()
tp['start'] = tuple([tp['end'][0], tp['start'][1]])
paths.append(tp)
#X1Y1 to X2Y1
tp = p.copy()
tp['end'] = tuple([tp['end'][0], tp['start'][1]])
paths.append(tp)
#x1Y2 to X2Y2
tp = p.copy()
tp['start'] = tuple([tp['start'][0], tp['end'][1]])
paths.append(tp)
#modify P to be X1Y2 to X1Y1
p['start'] = tuple([p['start'][0], p['start'][1]])
p['end'] = tuple([p['start'][0], p['end'][1]])
else:
logging.debug("interpolating line from %s to %s", p['start'], p['end'])
paths.append(p)
return paths
Igor, what a great project! This will save a lot of time lining up stencils with PCBs! When I used it this morning, the board size was incorrect due to a mismatch in the Gerber line that specifies precision. Altium Designer starts the line with %FSA instead of %FSLA. I have fixed the problem in a new branch, and I would be glad to push it to the repository, but I only have read access. Thanks for your great work on this!
Hi Dominic! Awesome! I'm glad you like it and found it helpful. You can make a pull request (fork the repo to your bitbucket account, push the changes and there will be a pull request option somewhere in the GUI) and I will merge it. If you don't have bitbucket account or don't want to go through it you can submit an issue or send me the diff via email.
Yo Igor, SUPER cool project! Very thankful to have found it--I've made PCBs before, but never completed an SMT project. This will be my first. Wasn't excited to procure or build an overly complicated solder paste jig for a single PCB measuring under 50x50mm.
As I'm trying to read a .kicad_pcb file, I'm getting the following errors:
"Traceback (most recent call last): File "C:\stencilframer\stencilframer.py", line 592, in sys.exit(main()) File "C:\stencilframer\stencilframer.py", line 429, in main shapes = sort_paths(process_kicad_layer(outline_raw)) File "C:\stencilframer\stencilframer.py", line 327, in sort_paths current = paths[0] IndexError: list index out of range" win10, python 3.10.4, openscad 2021.01 When I open the file as a gerber file, the 3d model stencil holder seems to be sized approximately correctly, but the PCB slot isn't the right shape. I'll investigate more tomorrow. I have a feeling my easiest option will be to manually adjust the stl to correct the size of the PCB slot in the holder--my pcb is a rectangle, so it should be a quick adjustment after dimension calculations.
Cheers again for a sick, sick project!
I think it's related to kicad 6 (I've never really tested it with v6). Try to export gerbers and run stencilframer with the outline gerber
you can send me the gerber or the kicad file by mail and i'll see what's going on when i find some time
Thanks for offering! 😊 I installed kicad v5 (sans 3d footprint files, obviously) and did the same gerber .zip ➡ .kicad_pcb file process and your script worked perfectly. I hope whoever tries v6 in the future finds this comment to simply use v5, even if they design and export out of kicad v6. I've done a little python scripting in the past, so maybe I'll take a peak under the hood when I get some time. 😏
It's in the works. But using it with gerber file as an input still works
Hi, I'm having issues running the script. It's complaining that it cannot find the args.openscad file, am I missing something obvious to fix this? I've uninstalled and reinstalled openscad 3 times now
Thanks in advance
it means that it can't find the openscad executable in your path. you should add --openscad '/path/to/openscad/executable' to the argument list
Hi Igor,
I think this project will help me!
However, i have issue with the realization :
- PCB width and lenght : 74 x 49 mm (CAD file)
- PCB width and lenght : 74.07 x 49.15 mm (Physical PCB)
- StencilHolder width and lenght : 73.75 x 48.8 mm
As you can see, the physical PCB dimensions are > to the CAD and my printing is <
Is there a factor argument i can specify ?
Thanks
Hi Vincent! You should be able to adjust this with the --offset argument which sets the distance to the frame for both pcb and stencil. if you need bigger distance for pcb than for the stencil the set the --offset to a value for pcb and then --stencil-offset to the value for stencil (default is 0.1)
Very nice project, I will try it. It would be interesting to add automatically some holes under the jig "free space" area to combine this with the another pcb jig idea which uses vacuum cleaner to suck the air and that way hold the stencil and pcb flat on each others. Idea and models for that are presended in these 2 web pages. https://cults3d.com/en/3d-model/tool/smt-stencil-pcb-jig https://www.youtube.com/watch?v=hbHJ6JBdSCs
Interesting idea, especially for bigger PCBs. I'm not sure if I'll add it (first I need to fix the KiCad 6/7 support but can't seem to find the time for it) but for now maybe the easiest way to achieve just that (although it's not automatic :)) would be to use stencilframer to generate OpenSCAD file and then add the holes in OpenSCAD (or even simpler, add holes in Prusa Slicer when slicing the model)
Yes, thinking prerry much same way that producing first openscad files for holder and frame with your script, then add to those openscad files the code to generate holes and finally generate the stl from those openscad files.
I also noticed that the process_kicad_layer method is broken for kicad 7 format in your script. But I can workaround that by exporting the gerber files from kicad and then giving the edge cut gerbel file as a parameter like this way:
./stencilframer.py -p 1.55 kicad_pcb_test-Edge_Cuts.gbr pcb_holder.stl ./stencilframer.py --frame kicad_pcb_test-Edge_Cuts.gbr pcb_frame.stl
I first tried to do that with front copper layer gerber file and that did not work well. Maybe it would be good to mention in documentation that one should use the edge cut gerber file as an input.
Good point, haven't noticed it before! Thanks! I added a mention outline/Edge_Cuts layer in the text and will add it to the script when I update it to support newer Kicad.
Hello and great project. I am really interested in testing this out but I keep getting a simple error message when trying to run it.
stencilframer.py: error: unrecognized arguments: holder.stl
Im using your simple cmd line execution but it keeps complaining about my outfile.
what is your command line?
what i meant was, what exactly command you were using? which arguments?
python stencilframer.py C:\Users\xxxx\Documents\stencilframer\test_board\test_board-Edge_cuts.gbr holder.stl