The basics of scripting in pcbnew

I’ve found that, so far, 1 I’m able to do all of the layout queries and manipulations I’ve wanted to do.
Edit April 3, 2018 The comment below about 4.0.5 is not true. Even the most recent 4.0.7 is missing a ton of python related stuff. Until kicad 5.0 comes out, I recommend using one of the nightly builds from the kicad downloads page. End Edit
Note that these examples don’t work on the latest release as of Feb 2017 (4.0.5). You want one of the nightly builds, also in the kicad download area. Or you can build it
The interface is lacking some consistency but it’s fine if you have a map of the classes (click for a larger version or download it from here):

 
In this post, I’ll focus on querying a board for information about it’s contents. The code can be found in this github repo.

Getting started

To invoke any of these examples, you’ll want pcbnew’s scripting window. Tools->scripting console
You’ll probably want these in your scripts
import pcbnew
# most queries start with a board
board = pcbnew.GetBoard()

Nets

Want to know all of the nets in your board?
Nets can be looked up in two ways:
  • by name
  • by netcode – a unique integer identifier for your net.
If you run this code:
# returns a dictionary netcode:netinfo_item
netcodes = board.GetNetsByNetcode()
# list off all of the nets in the board.
for netcode, net in netcodes.items():
    print("netcode {}, name {}".format(netcode, net.GetNetname()))
# here's another way of doing the same thing.
print("here's the other way to do it")
nets = board.GetNetsByName()
for netname, net in nets.items():
    print("method2 netcode {}, name{}".format(net.GetNet(), netname))
# maybe you just want a single net
# the find method returns an iterator to all matching nets.
# the value of an iterator is a tuple: name, netinfo
clknet = nets.find("/clk").value()[1]
clkclass = clknet.GetNetClass()
print("net {} is on netclass {}".format(clknet.GetNetname(),
 clkclass))
You’ll get something like this:
netcode 49, name /ihg
netcode 50, name /ihh
netcode 51, name /data_in
netcode 52, name /data_out
netcode 53, name /clk
here's the other way to do it
method2 netcode 0, name
method2 netcode 23, name+5V
method2 netcode 53, name/clk
method2 netcode 55, name/data_contd
method2 netcode 51, name/data_in

Physical dimensions

The coordinate space of kicad_pcb is in mm. At the beginning of this wiki about Kicad’s Board_File_Format

“All physical units are in mils (1/1000th inch) unless otherwise noted.”

Then later in historical notes, it says,

“As of 2013, the PCBnew application creates ‘.kicad_pcb’ files that begin with (kicad_pcb (version 3)”. All distances are in millimeters.

In short, for the data that I’ve recently created, the internal coordinate space of pcbnew is 10E-6 mm. (a millionth of a mm)
For example, the coordinate 121550000 corresponds to 121.550000mm
SCALE = 1000000.0
boardbbox = board.ComputeBoundingBox()
boardxl = boardbbox.GetX()
boardyl = boardbbox.GetY()
boardwidth = boardbbox.GetWidth()
boardheight = boardbbox.GetHeight()
print("this board is at position {},{} {} wide and {} high".format(boardxl,
    boardyl,
    boardwidth,
    boardheight))

Modules/Pads

Each of your placed modules can be found with its reference name. The module connection points are pads, of course.
# generate a LUT with shape integers to a string
padshapes = {
    pcbnew.PAD_SHAPE_CIRCLE: "PAD_SHAPE_CIRCLE",
    pcbnew.PAD_SHAPE_OVAL: "PAD_SHAPE_OVAL",
    pcbnew.PAD_SHAPE_RECT: "PAD_SHAPE_RECT",
    pcbnew.PAD_SHAPE_TRAPEZOID: "PAD_SHAPE_TRAPEZOID"
}
# new in the most recent kicad code
if hasattr(pcbnew, 'PAD_SHAPE_ROUNDRECT'):
    padshapes[pcbnew.PAD_SHAPE_ROUNDRECT] = "PAD_SHAPE_ROUNDRECT",
modref = "U1"
mod = board.FindModuleByReference(modref)
for pad in mod.Pads():
    print("pad {}({}) on {}({}) at {},{} shape {} size {},{}"
        .format(pad.GetPadName(),
                pad.GetNet().GetNetname(),
                mod.GetReference(),
                mod.GetValue(),
                pad.GetPosition().x, pad.GetPosition().y,
                padshapes[pad.GetShape()],
                pad.GetSize().x, pad.GetSize().y
    ))
Gives you this:
pad 1(/ilb) on U1(74HC595) at 127635000,106520000 shape PAD_SHAPE_RECT size 1500000,600000
pad 2(/ilc) on U1(74HC595) at 126365000,106520000 shape PAD_SHAPE_RECT size 1500000,600000
pad 3(/ild) on U1(74HC595) at 125095000,106520000 shape PAD_SHAPE_RECT size 1500000,600000

Layer

Most of the pcb data is on a layer. pcbnew stores layers as numbers. Here we can print them all out
layertable = {}
numlayers = pcbnew.LAYER_ID_COUNT
   for i in range(numlayers):
       layertable[i] = board.GetLayerName(i)
       print("{} {}".format(i, board.GetLayerName(i)))
Which gives you this:
0 F.Cu
1 In1.Cu
2 In2.Cu
3 In3.Cu
...

Tracks (wires and vias)

A wire is stored in a TRACK object. Vias are in a class derived from TRACK. Let’s list some of them.
# clk net was defined above as was SCALE
clktracks = board.TracksInNet(clknet.GetNet())
   for track in clktracks:
       print("{},{}->{},{} width {} layer {}".format(track.GetStart().x/SCALE,
             track.GetStart().y/SCALE,
             track.GetEnd().x/SCALE,
             track.GetEnd().y/SCALE,
             track.GetWidth()/SCALE,
             layertable[track.GetLayer()]))
And here are the wires
121.92,97.79->126.492,97.79 width 0.25 layer F.Cu
127.762,100.33->125.984,100.33 width 0.25 layer F.Cu
127.762,99.314->127.762,100.33 width 0.25 layer B.Cu
So that’s how to query data. In my next post, I’ll talk about making changes
The basics of scripting in pcbnew

  1. thanks to recent work by Kicad contributor/developer Dick Hollenbeck

Leave a Reply

Your email address will not be published. Required fields are marked *