I’ve been making a KiCad PCBNew action plugin recently for generating coils - you can watch a video of the results below, but I wanted to capture what I had learnt for future reference - mostly so that I have something to refer to, but hopefully this will be useful to others as well.
There’s some pretty reasonable documentation here: https://dev-docs.kicad.org/en/python/pcbnew/ that gives you the basic structure of a plugin, but beyond that you’ll need to look at other peoples examples and the auto-generated documentation - http://docs.kicad.org/doxygen-python/namespacepcbnew.html
Quite often you’ll have to dive into the C++ code to really understand what is going on and how to use the API.
The first thing you will need to do in your plugin is get hold of the current board:
board = pcbnew.GetBoard()
Once you have this you can start adding things to it. You will probably want to add things to a particular layer and you may also want to assign tracks to a particular net. You can get the layer and net objects from the board:
front_copper = pcbnew.F_Cu
back_copper = pcbnew.B_Cu
front_silk = pcbnew.F_SilkS
back_silk = pcbnew.B_SilkS
If you go here: https://docs.kicad.org/doxygen-python/namespacepcbnew.html and search for F_Cu
you’ll find the complete list of layer constants.
# find the matching net for the track
net = board.FindNet("NET NAME")
# optionally you can create the net if it doesn't exist - handy if you are creating a PCB from scratch
if net is None:
net = pcbnew.NETINFO_ITEM(board, "NET NAME")
board.Add(net)
Once you have the layer and the net you can to use you can create tracks - you just need to specify the start and end points along with the width. You can also specify the layer and net:
track = pcbnew.PCB_TRACK(board)
track.SetStart(pcbnew.wxPointMM(x1, y1))
track.SetEnd(pcbnew.wxPointMM(x2, y2))
track.SetWidth(int(thickness * pcbnew.IU_PER_MM))
track.SetLayer(layer)
track.SetNetCode(net.GetNetCode())
board.Add(track)
Similarly you can create vias - you just need to specify the position, the diameter and the drill size. You can also specify the layer and net:
pcb_via = pcbnew.PCB_VIA(board)
pcb_via.SetPosition(pcbnew.wxPointMM(x, y))
pcb_via.SetWidth(int(via_diameter * pcbnew.IU_PER_MM))
pcb_via.SetDrill(int(via_drill_diameter * pcbnew.IU_PER_MM))
pcb_via.SetNetCode(net.GetNetCode())
board.Add(pcb_via)
One thing that I did find was that I needed to add the vias after I had added the tracks - if I didn’t do this it seemed to cause some errors when running the DRC checks.
Adding silk screen text is also pretty straightforward:
pcb_txt = pcbnew.PCB_TEXT(board)
pcb_txt.SetText("Hellorld")
pcb_txt.SetPosition(pcbnew.wxPointMM(x, y))
pcb_txt.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER)
pcb_txt.Rotate(pcbnew.wxPointMM(x, y), text["angle"])
pcb_txt.SetTextSize(pcbnew.wxSizeMM(size, size))
pcb_txt.SetLayer(pcbnew.F_SilkS)
board.Add(pcb_txt)
If you want the text to flip to the other side of the board them you specify a bottom silk later, or you can just flip the text:
pcb_txt.Flip(pcbnew.wxPointMM(x, y), True)
The first argument is the point to flip around, and the second argument is whether to mirror the text as well.
You’ll probably also need some way of connecting things to your PCB - pins and pads to connect leads components and leads to your board.
I haven’t yet worked out how to add a footprint from the footprint libaries to the board, but I have worked out how to add pins and pads.
To create a plated through hole pin you can do this:
module = pcbnew.FOOTPRINT(board)
module.SetPosition(pcbnew.wxPointMM(x, y))
board.Add(module)
pcb_pad = pcbnew.PAD(module)
pcb_pad.SetSize(pcbnew.wxSizeMM(pin_diameter, pin_diameter))
pcb_pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE)
pcb_pad.SetAttribute(pcbnew.PAD_ATTRIB_PTH)
pcb_pad.SetLayerSet(pcb_pad.PTHMask())
pcb_pad.SetDrillSize(pcbnew.wxSizeMM(pin_drill, pin_drill))
pcb_pad.SetPosition(pcbnew.wxPointMM(x, y))
pcb_pad.SetNetCode(net.GetNetCode())
module.Add(pcb_pad)
And to create a pad you can do this - I’m still not sure on the use of LSET here, but it seems to work - if you change F_Cu to B_Cu the pad will be on the back of the board:
lset = pcbnew.LSET()
lset.AddLayer(pcbnew.F_Cu)
module = pcbnew.FOOTPRINT(board)
module.SetPosition(pcbnew.wxPointMM(x, y))
board.Add(module)
pcb_pad = pcbnew.PAD(module)
pcb_pad.SetSize(pcbnew.wxSizeMM(width, height))
pcb_pad.SetShape(pcbnew.PAD_SHAPE_RECT)
pcb_pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
pcb_pad.SetLayerSet(pcb_pad.SMDMask())
pcb_pad.SetLayerSet(lset)
pcb_pad.SetPosition(pcbnew.wxPointMM(x, y))
pcb_pad.SetNetCode(net.GetNetCode())
pcb_pad.Flip(pcbnew.wxPointMM(x, y), False)
module.Add(pcb_pad)
To make mounting holes you can create pins, but change them so that they are not plated through holes:
module = pcbnew.FOOTPRINT(board)
module.SetPosition(pcbnew.wxPointMM(x, y))
board.Add(module)
pcb_pad = pcbnew.PAD(module)
pcb_pad.SetSize(pcbnew.wxSizeMM(diameter, diameter))
pcb_pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE)
pcb_pad.SetAttribute(pcbnew.PAD_ATTRIB_NPTH)
pcb_pad.SetDrillSize(pcbnew.wxSizeMM(diameter, diameter))
pcb_pad.SetPosition(pcbnew.wxPointMM(x, y))
module.Add(pcb_pad)
Finally you need to add edge cuts to the board - this is the outline of the board - to make a circular board you can do this:
circle = pcbnew.PCB_SHAPE(board)
circle.SetShape(pcbnew.SHAPE_T_CIRCLE)
circle.SetFilled(False)
circle.SetStart(pcbnew.wxPointMM(CENTER_X, CENTER_Y))
circle.SetEnd(pcbnew.wxPointMM(CENTER_X + radius, CENTER_Y))
circle.SetCenter(pcbnew.wxPointMM(CENTER_X, CENTER_Y))
circle.SetLayer(pcbnew.Edge_Cuts)
circle.SetWidth(int(0.1 * pcbnew.IU_PER_MM))
board.Add(circle)
Note the use to SetStart
and SetEnd
to set the radius of the circle - there’s no SetRadius
method.
Hopefully the above will be useful for other people. I’ll add and update this post as I learn more and correct any errors in my code…