KiCad 6 Python Scripting: Place Footprints, Create Tracks, Curved Tracks, Vias, and Edge Cut Lines
If you find yourself in a situation where you are placing component footprints at multiple locations on PCB using KiCad, or routing a pattern of tracks repeatedly (like in a keyboard) you’ll save time by automating through a Python script.
Contents
Introduction
KiCad 6 has decent support for scripting but documentation can be hard to grok. Reading their code is often the only recourse. I’ll cover the basics of placing footprints and routing tracks with code examples.
How to Run Python Script
Copy (or symlink) your python script to KiCad plugins directory, which (on a
Mac) is located in ~/Documents/KiCad/6.0/scripting/plugins
. You can find out
where KiCad looks for plugins and scripts by running import pcbnew; print(pcbnew.PLUGIN_DIRECTORIES_SEARCH)
from Python Console in PCB Editor window (icon is at the right hand top corner).
In the console window simply import the python module using import filename
(if your python script is named filename.py). This will execute the script. It
works the first time, but Python interpreter will not import the same module
twice. There is a solution. You reload the module again using import
importlib
followed by importlib.reload(filename)
.
There is an alternate approach of using plugins, but above method is simpler; your print output appears in the console window instead of having it redirected to a file.
KiCad Coordinate System
The Cartesian plane KiCad uses has Y-axis pointing down and X-axis pointing to
the right. Moreover, distance is specified in millionth of millimeter, and
angles are in tenths of a degree. These aspects will become clear later. Points
in space are represented by wxPoint
. There is also a millimeter
variant called wxPointMM
, and a conversion routine fromMM()
. Using mils
instead of millimeter is also possible but not covered here.
Footprints
Footprints are needed to position components on pcb.
Place Footprint
Get a reference to the footprint object from the Board object. You can then place the footprint and set the orientation by calling the footprint object itself.
In the following example we have three (SMD) footprints, for 2 resistors and a diode.
import pcbnew
from pcbnew import wxPoint, wxPointMM
board = pcbnew.GetBoard()
# Get reference to footprint objects
board = pcbnew.GetBoard()
r1 = board.FindFootprintByReference("R1")
r2 = board.FindFootprintByReference("R2")
d1 = board.FindFootprintByReference("D1")
assert(r1 and r2 and d1)
# Place footprints
r1.SetPosition(wxPointMM(20, 20)) # (x, y) = (20, 20) in mm
r1.SetOrientation(90 * 10) # rotate by 90 deg
r2.SetPosition(wxPointMM(25, 21))
d1.SetPosition(wxPointMM(23, 26))
# Update display
pcbnew.Refresh()
Tracks
KiCad 6 supports straight line as well as curved tracks.
Straight Track
To route a track, you need a start and end point. You need to also locate the center of the pads that terminate the track.
import pcbnew
from pcbnew import wxPoint, wxPointMM
def add_track(start, end, layer=pcbnew.F_Cu):
board = pcbnew.GetBoard()
track = pcbnew.PCB_TRACK(board)
track.SetStart(start)
track.SetEnd(end)
track.SetWidth(int(0.25 * 1e6))
track.SetLayer(layer)
board.Add(track)
# Route track from pad #1 of footprint R1 to pad #1 of D1 with 45-deg corner
board = pcbnew.GetBoard()
start = board.FindFootprintByReference("R1").FindPadByNumber("1").GetCenter()
end = board.FindFootprintByReference("D1").FindPadByNumber("1").GetCenter()
offset = end.x - start.x
thru = pcbnew.wxPoint(start.x, end.y - offset)
add_track(start, thru)
add_track(thru, end)
pcbnew.Refresh()
Curved Track
KiCad 6 has support for drawing curved tracks, be it circular arcs or Bezier curves.
Only circular arcs are covered here. Use PCB_ARC
object and specify start, mid and end points of arc.
For low frequency applications, curved tracks are mostly for aesthetic reasons. Moreover, during manual routing if you use the “shove” option KiCad may decide to convert rounded edges to sharp corners. To manually route a rounded track use Ctrl-/ (or Cmd-/ on Mac) shortcut to switch among following options: corners at 45 deg -> rounded corners at 45 deg -> corners at 90 deg -> rounded corners at 90 deg, after you click on the starting point of track.
The following example adds a rounded corner to two straight line tracks.
import pcbnew
import math
from pcbnew import wxPoint, wxPointMM
def add_track_arc(start, mid, end, layer=pcbnew.F_Cu):
board = pcbnew.GetBoard()
track = pcbnew.PCB_ARC(board)
track.SetStart(start)
track.SetMid(mid)
track.SetEnd(end)
track.SetWidth(int(0.25 * 1e6))
track.SetLayer(layer)
board.Add(track)
# Route track from pad #2 of footprint R1 to pad #1 of R2
# with 90-deg arc of radius 1.5mm
board = pcbnew.GetBoard()
radius = 1.5 * pcbnew.IU_PER_MM
start = board.FindFootprintByReference("R1").FindPadByNumber("2").GetCenter()
end = board.FindFootprintByReference("R2").FindPadByNumber("1").GetCenter()
start1 = pcbnew.wxPoint(end.x - radius, start.y)
add_track(start, start1)
end1 = pcbnew.wxPoint(end.x, start.y + radius)
add_track(end1, end)
# Find the mid point of the arc by translating the origin to the center of arc
# and rotating the axis by 45-deg
theta = 45
mid = wxPoint(
start1.x + radius * math.cos(math.radians(theta)),
end1.y - radius * math.sin(math.radians(theta)),
)
add_track_arc(start1, mid, end1)
pcbnew.Refresh()
Create Via
Create a via at 1mm offset from pad #2 of footprint R2 and connect a track to it.
import pcbnew
from pcbnew import wxPoint, wxPointMM
board = pcbnew.GetBoard()
pad = board.FindFootprintByReference("R2").FindPadByNumber("2").GetCenter()
via_location = wxPoint(pad.x + 1 * pcbnew.IU_PER_MM, pad.y)
add_track(pad, via_location)
via = pcbnew.PCB_VIA(board)
via.SetPosition(via_location)
via.SetDrill(int(0.4 * 1e6))
via.SetWidth(int(0.8 * 1e6))
board.Add(via)
pcbnew.Refresh()
Remove All Tracks and Vias
You may need to remove stale tracks before adding new ones.
import pcbnew
board = pcbnew.GetBoard()
for t in board.GetTracks():
board.Delete(t)
pcbnew.Refresh()
Edge Cuts
Edge Cut lines define the boundary of the pcb. KiCad 6 has support for drawing straight lines and arcs on any layer, not just on Edge Cuts. To draw a line you specify the end points. To draw an arc you specify starting point, center of the arc, and the angle. This API is slightly different from drawing curved tracks where you specify mid-point of the curve. There is also API to draw Bezier curves.
Draw Line
Use PCB_SHAPE
object and set the shape to SHAPE_T_SEGMENT
.
You can specify the layer and line width. Use the search box in the
documentation to search for symbols.
Add an edge cuts border around the components. Draw lines on all four sides and connect them by rounded corners.
import pcbnew
from pcbnew import wxPoint, wxPointMM
def add_line(start, end, layer=pcbnew.Edge_Cuts):
board = pcbnew.GetBoard()
segment = pcbnew.PCB_SHAPE(board)
segment.SetShape(pcbnew.SHAPE_T_SEGMENT)
segment.SetStart(start)
segment.SetEnd(end)
segment.SetLayer(layer)
segment.SetWidth(int(0.1 * pcbnew.IU_PER_MM))
board.Add(segment)
board = pcbnew.GetBoard()
border = 4 * pcbnew.IU_PER_MM
radius = 1 * pcbnew.IU_PER_MM
r1 = board.FindFootprintByReference("R1").GetPosition()
r2 = board.FindFootprintByReference("R2").GetPosition()
d1 = board.FindFootprintByReference("D1").GetPosition()
start = wxPoint(r1.x - border + radius, r1.y - border)
end = wxPoint(r2.x + border - radius, r1.y - border)
add_line(start, end)
start = wxPoint(end.x + radius, end.y + radius)
end = wxPoint(start.x, d1.y + border - radius)
add_line(start, end)
start = wxPoint(end.x - radius, end.y + radius)
end = wxPoint(r1.x - border + radius, start.y)
add_line(start, end)
start = wxPoint(end.x - radius, end.y - radius)
end = wxPoint(start.x, r1.x - border + radius)
add_line(start, end)
pcbnew.Refresh()
Draw Arc
Use PCB_SHAPE
object and set the shape to SHAPE_T_ARC
.
import pcbnew
from pcbnew import wxPoint, wxPointMM
def add_line_arc(start, center, angle=90, layer=pcbnew.Edge_Cuts):
board = pcbnew.GetBoard()
arc = pcbnew.PCB_SHAPE(board)
arc.SetShape(pcbnew.SHAPE_T_ARC)
arc.SetStart(start)
arc.SetCenter(center)
arc.SetArcAngleAndEnd(angle * 10, False)
arc.SetLayer(layer)
arc.SetWidth(int(0.1 * pcbnew.IU_PER_MM))
board.Add(arc)
board = pcbnew.GetBoard()
border = 4 * pcbnew.IU_PER_MM
radius = 1 * pcbnew.IU_PER_MM
r1 = board.FindFootprintByReference("R1").GetPosition()
r2 = board.FindFootprintByReference("R2").GetPosition()
d1 = board.FindFootprintByReference("D1").GetPosition()
start = wxPoint(r2.x + border - radius, r1.y - border)
center = wxPoint(start.x, start.y + radius)
add_line_arc(start, center)
start = wxPoint(start.x + radius, d1.y + border - radius)
center = wxPoint(start.x - radius, start.y)
add_line_arc(start, center)
start = wxPoint(r1.x - border + radius, start.y + radius)
center = wxPoint(start.x, start.y - radius)
add_line_arc(start, center)
start = wxPoint(start.x - radius, r1.y - border + radius)
center = wxPoint(start.x + radius, start.y)
add_line_arc(start, center)
pcbnew.Refresh()
Remove All Lines
You may want start with a fresh slate.
import pcbnew
board = pcbnew.GetBoard()
for dr in board.GetDrawings():
board.Delete(dr)
pcbnew.Refresh()
Conclusion
KiCad 6 provides adequate scripting capability for designing pcb’s of moderate complexity.
KiCad projects that use Python scripts: