Saturday, April 16, 2016

Scripting KiCad Pcbnew exports

For the past few months I’ve been designing an open source split-flap display in my free time — the kind of retro electromechanical display that used to be in airports and train stations before LEDs and LCDs took over and makes that distinctive “tick tick tick tick” sound as the letters and numbers flip into place.

I designed the electronics in KiCad, and one of the things I wanted to do was include a nice picture of the current state of the custom PCB design in the project’s README file. Of course, I could generate a snapshot of the PCB manually whenever I made a change by using the “File→Export SVG file” menu option and then check that image into my git repo…


…but that gets tedious, is prone to human error, pollutes the git history with a bunch of old binary files, and isn’t very customizable.

For instance, the manual SVG export uses opaque colors which make it hard to see features that overlap, as well as using two different colors for items on the same layer (yellow and teal are both part of the front silkscreen layer below):

Functional rendering, but not exactly what I wanted.
Luckily, Pcbnew has built-in Python bindings which make it pretty straightforward to invoke certain features from standalone Python scripts. As a simple example, here’s how to plot a single layer to an SVG:

import pcbnew

# Load board and initialize plot controller
board = pcbnew.LoadBoard("splitflap.kicad_pcb")
pc = pcbnew.PLOT_CONTROLLER(board)
po = pc.GetPlotOptions()
po.SetPlotFrameRef(False)

# Set current layer
pc.SetLayer(pcbnew.F_Cu)

# Plot single layer to file
pc.OpenPlotfile("front_copper", pcbnew.PLOT_FORMAT_SVG, "front_copper")
print("Plotting to " + pc.GetPlotFileName())
pc.PlotLayer()
pc.ClosePlot()


As a minor note, there's not much documentation of the Python bindings, but if you search through the KiCad source code you can find the C++ interfaces that are exposed to Python. E.g. above, pcbnew.F_Cu is one of many possible layer constants and pcbnew.PLOT_FORMAT_SVG is one of several different plot formats.

While it’s in theory possible to specify the colors to use when plotting, I ran into issues where certain items were always plotted in their default color. For instance, when I plot the front silkscreen layer with the following options, the footprints are plotted in teal rather than the specified color, red:

pc.SetLayer(pcbnew.F_SilkS)
pc.SetColorMode(True)
po.SetColor(pcbnew.RED) # <-- NOTE THIS LINE po.SetReferenceColor(pcbnew.GREEN)
po.SetValueColor(pcbnew.BLUE)


A lot of the silkscreen ended up teal instead of red.

So instead of trying to get Pcbnew to output the exact SVG I wanted, I decided to export each layer as a separate monochrome SVG image and then post-process them to apply colors and merge them into a single output file. Since SVG images are just XML, it was easy to write a script, svg_processor.py, which allowed me to override the “fill” and “stroke” style attributes of the shapes, and then wrap all of the shapes in a <g> group tag to set the desired opacity.

(Note: the reason for wrapping in a group before applying opacity is that things like traces are rendered as a combination of multiple shapes, like a line + circle, so if you applied alpha=0.5 to each shape individually, a single trace would have varying degrees of opacity depending on how its subcomponents overlapped)

This allowed me to write a simple definition of the PCB layers to export and turn that into a nice, customizable rendering:

layers = [
  {'layer': pcbnew.B_SilkS, 'color': '#CC00CC', 'alpha': 0.8 },
  {'layer': pcbnew.B_Cu, 'color': '#33EE33', 'alpha': 0.5 },
  {'layer': pcbnew.F_Cu, 'color': '#CC0000', 'alpha': 0.5 },
  {'layer': pcbnew.F_SilkS, 'color': '#00CCCC', 'alpha': 0.8},
]

Ooooh, so beautiful!

As a final step after processing and merging, I use Inkscape's command line interface to shrink the .svg canvas to fit the image and convert the vector .svg file into a raster .png image like you see above:

inkscape --export-area-drawing --export-width=320 --export-png output.png --export-background '#FFFFFF' input.svg

The complete script to export .svg and .png renderings of the PCB can be found at https://github.com/scottbez1/splitflap/blob/580a11538d801041cedf59a3c5d1c91b5f56825d/electronics/generate_svg.py

In the next post, I cover how I automated this rendering process on every commit using Travis CI with S3 deployments to keep the image and gerbers referenced in the README always up to date!

3 comments:

  1. Have you read this: https://lists.launchpad.net/kicad-developers/msg25213.html ?

    ReplyDelete
  2. Have you considered breaking out the SVG code into a separate Python library? I think your approach remains useful, even if all the quirks on the C++ side get worked out.

    ReplyDelete