From a3da99e22540c026e4acf7bdc735aa221a4dd752 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 7 Sep 2017 21:41:39 +0200 Subject: o Adding two kicad tools from my kicad-utils repo. --- src/ee/tools/kicad_gerber.py | 230 ++++++++++++++++++++++++++++++++++ src/ee/tools/kicad_pcb_plot.py | 273 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 503 insertions(+) create mode 100755 src/ee/tools/kicad_gerber.py create mode 100755 src/ee/tools/kicad_pcb_plot.py (limited to 'src') diff --git a/src/ee/tools/kicad_gerber.py b/src/ee/tools/kicad_gerber.py new file mode 100755 index 0000000..e8604b8 --- /dev/null +++ b/src/ee/tools/kicad_gerber.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python +import sys +import os +import argparse +from pcbnew import * + + +def layer_name_parser(s): + parts = s.split('=') + if len(parts) != 2: + raise argparse.ArgumentTypeError("Invalid layer renaming: " + s) + else: + return parts + + +parser = argparse.ArgumentParser(description='KiCAD PCB to GERBER converter') + +parser.add_argument('--pcb', + required=True, + dest='pcb', + action='store', + help='A foo.kicad_pcb file') + +parser.add_argument('--output-directory', + required=True, + dest='output_directory', + action='store', + help='Directory to store output files') + +parser.add_argument('--detect-files-only', + dest='detect_files_only', + action='store_true', + help='Don\'t create the GERBER files, just list the files to be created') + +parser.add_argument('--create-drill-map-file', + dest='create_drill_map_file', + action='store_true', + help='Create drill map file') + +parser.add_argument('--protel-extensions', + dest='protel_extensions', + action='store_true', + help='Use Protel filename extensions instead of .gbr') + +parser.add_argument('--uppercase-extensions', + action='store_true', + help='Uppercase all extensions') + +parser.add_argument('--layer-extension', + dest='layer_extensions', + metavar='LAYER_AND_EXTENSION', + action='append', + type=layer_name_parser, + help='Set the file extension of a KiCAD layer. Format: =, example: F.SilkS=GSILK') + +args = parser.parse_args() +# print "args: " + str(args) +# print "args.name_layer: " + str(args.name_layers) + +renames = {} +if args.layer_extensions is not None: + for s in args.layer_extensions: + renames[s[0]] = s[1] +# print("renames: " + str(renames)) + +board = LoadBoard(args.pcb) + + +class Plan: + def __init__(self, layerNum, layerName, description): + self.layerNum = layerNum + self.layerName = layerName + self.description = description + self.postfix = "" + self.ext = None + + @staticmethod + def standard(layerNum, description): + layerName = board.GetLayerName(layerNum) + return Plan(layerNum, layerName, description) + + @staticmethod + def copper(layerNum): + layerName = board.GetLayerName(layerNum) + description = "Copper Layer " + layerName + return Plan(layerNum, layerName, description) + + +plot_plan = [Plan.standard(layerNum, description) for (layerNum, description) in [ + (F_SilkS, "Silk front"), + (F_Mask, "Mask front"), + (F_Paste, "Paste front"), + (B_SilkS, "Silk bottom"), + (B_Mask, "Mask bottom"), + (B_Paste, "Paste bottom"), + (Edge_Cuts, "Edges")]] + +layers = board.GetEnabledLayers() +for layerNum in layers.CuStack(): + plot_plan.append(Plan.copper(layerNum)) + +pctl = PLOT_CONTROLLER(board) +popt = pctl.GetPlotOptions() +popt.SetOutputDirectory(args.output_directory) + +# A nasty hack to get the base filename +pctl.SetLayer(F_Cu) +pctl.OpenPlotfile("", PLOT_FORMAT_GERBER, "") +filename = pctl.GetPlotFileName() +try: + os.remove(filename) +except: + pass +pctl.ClosePlot() + +if args.protel_extensions: + popt.SetUseGerberProtelExtensions(True) + +basename = os.path.splitext(filename)[0] + +drlFileOut = drlFile = basename + ".drl" +drlNpthFileOut = drlNpthFile = basename + "-NPTH.drl" + +if args.protel_extensions: + n, e = os.path.splitext(drlFileOut) + drlFileOut = n + ".txt" + +if args.uppercase_extensions: + n, e = os.path.splitext(drlFileOut) + drlFileOut = n + e.upper() + n, e = os.path.splitext(drlNpthFileOut) + drlNpthFileOut = n + e.upper() + +values = vars(args) +for plan in plot_plan: + pctl.SetLayer(plan.layerNum) + pctl.OpenPlotfile("", PLOT_FORMAT_GERBER, plan.description) + filename = pctl.GetPlotFileName() + pctl.ClosePlot() + # By opening and closing the plot we create an empty plot file. + try: + os.remove(filename) + except: + pass + + filename, ext = os.path.splitext(filename) + if args.uppercase_extensions: + ext = ext.upper() + + if plan.layerName in renames: + ext = "." + renames[plan.layerName] + + # if plan.arg is not None: + # newExt = values[plan.arg] + # if newExt is not None: + # ext = "." + newExt + + # if not args.protel_extensions: + plan.postfix = plan.layerName + plan.filename = basename + "-" + plan.postfix + ext + + # print "filename = " + plan.filename + ", postfix=" + plan.postfix + # print "filename: " + plan.filename + +if args.detect_files_only: + for plan in plot_plan: + print plan.filename + + if args.protel_extensions: + print drlFileOut + print drlNpthFileOut + sys.exit(0) + +# Set some important plot options: +popt.SetPlotFrameRef(False) +popt.SetLineWidth(FromMM(0.35)) + +popt.SetAutoScale(False) +popt.SetScale(1) +popt.SetMirror(False) +popt.SetUseGerberAttributes(True) +popt.SetScale(1) +popt.SetUseAuxOrigin(True) + +# This by gerbers only (also the name is truly horrid!) +popt.SetSubtractMaskFromSilk(False) + +for plan in plot_plan: + pctl.SetLayer(plan.layerNum) + + # print "filename = " + plan.filename + ", postfix=" + plan.postfix + pctl.OpenPlotfile(plan.postfix, PLOT_FORMAT_GERBER, plan.description) + + actualFilename = pctl.GetPlotFileName() + expectedFilename = plan.filename + + if expectedFilename != actualFilename: + os.rename(actualFilename, expectedFilename) + + pctl.PlotLayer() + pctl.ClosePlot() + +popt.SetDrillMarksType(PCB_PLOT_PARAMS.FULL_DRILL_SHAPE) + +drlwriter = EXCELLON_WRITER(board) +drlwriter.SetMapFileFormat(PLOT_FORMAT_GERBER) + +mirror = False +minimalHeader = False +offset = wxPoint(0, 0) +# False to generate 2 separate drill files (one for plated holes, one for non plated holes) +# True to generate only one drill file +mergeNPTH = False +drlwriter.SetOptions(mirror, minimalHeader, offset, mergeNPTH) + +metricFmt = True +drlwriter.SetFormat(metricFmt) + +genDrl = True +genMap = args.create_drill_map_file + +drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), genDrl, genMap) + +if drlFile != drlFileOut: + os.rename(drlFile, drlFileOut) + pass + +if drlFile != drlNpthFileOut: + os.rename(drlNpthFile, drlNpthFileOut) + pass diff --git a/src/ee/tools/kicad_pcb_plot.py b/src/ee/tools/kicad_pcb_plot.py new file mode 100755 index 0000000..d6f123d --- /dev/null +++ b/src/ee/tools/kicad_pcb_plot.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +''' +Heavily borrowed from this location: https://github.com/blairbonnett-mirrors/kicad/blob/master/demos/python_scripts_examples/plot_board.py + +Parameters + +Takes 2 parameters +1. Path to kicad file +2. Location of where to save pdfs + + A python script example to create various plot files from a board: + Fab files + Doc files + Gerber files + + Important note: + this python script does not plot frame references. + the reason is it is not yet possible from a python script because plotting + plot frame references needs loading the corresponding page layout file + (.wks file) or the default template. + + This info (the page layout template) is not stored in the board, and therefore + not available. + + Do not try to change SetPlotFrameRef(False) to SetPlotFrameRef(true) + the result is the pcbnew lib will crash if you try to plot + the unknown frame references template. + + +Usage + + There are 4 main lines that generate a file. e.g.. + + pctl.SetLayer(F_SilkS) + pctl.OpenPlotfile("Silk", PLOT_FORMAT_PDF, "Assembly guide") + pctl.PlotLayer() + pctl.ClosePlot() + + +The first line takes the F.Silks layer +The second line takes 3 parameters (file-name-append, file type, unknown) +The third line actually plots the layer +The forth line reads the temp file and writes it out to a pdf + +You can write to the following formats + +PLOT_FORMAT_SVG +PLOT_FORMAT_PDF +PLOT_FORMAT_GERBER + + +''' +import sys + +from pcbnew import * + +filename = sys.argv[1] # e.g left-main/left-main.kicad_pcb + +board = LoadBoard(filename) + +pctl = PLOT_CONTROLLER(board) + +popt = pctl.GetPlotOptions() + +# popt.SetOutputDirectory("plot/") +popt.SetOutputDirectory(sys.argv[2]) + +# Set some important plot options: +popt.SetPlotFrameRef(False) +popt.SetLineWidth(FromMM(0.35)) + +popt.SetAutoScale(False) +popt.SetScale(1) +popt.SetMirror(False) +popt.SetUseGerberAttributes(True) +popt.SetExcludeEdgeLayer(False); +popt.SetScale(1) +popt.SetUseAuxOrigin(True) + +# This by gerbers only (also the name is truly horrid!) +popt.SetSubtractMaskFromSilk(False) + +pctl.SetLayer(F_SilkS) +pctl.OpenPlotfile("Silk", PLOT_FORMAT_PDF, "Assembly guide") +pctl.PlotLayer() + +######################### +#### CuBottom.gbr #### +#### CuTop.gbr #### +#### EdgeCuts.gbr #### +#### MaskBottom.gbr #### +#### MaskTop.gbr #### +#### PasteBottom.gbr #### +#### PasteTop.gbr #### +#### SilkBottom.gbr #### +#### SilkTop.gbr #### +######################### + +# Once the defaults are set it become pretty easy... +# I have a Turing-complete programming language here: I'll use it... +# param 0 is a string added to the file base name to identify the drawing +# param 1 is the layer ID +plot_plan = [ + ("F.Cu", F_Cu, "Front layer"), + ("B.Cu", B_Cu, "Bottom layer"), + ("F.Paste", F_Paste, "Paste front"), + ("B.Paste", B_Paste, "Paste bottom"), + ("F.Silk", F_SilkS, "Silk front"), + ("B.Silk", B_SilkS, "Silk bottom"), + ("F.Mask", F_Mask, "Mask front"), + ("B.Mask", B_Mask, "Mask bottom"), + ("Edge.Cuts", Edge_Cuts, "Edges"), + ("Cmts.User", Cmts_User, "User comments"), +] + +for layer_info in plot_plan: + pctl.SetLayer(layer_info[1]) + pctl.OpenPlotfile(layer_info[0], PLOT_FORMAT_PDF, layer_info[2]) + pctl.PlotLayer() + +###################### +#### AssyTop.pdf ##### +###################### + +# Our fabricators want two additional gerbers: +# An assembly with no silk trim and all and only the references +# (you'll see that even holes have designators, obviously) +popt.SetSubtractMaskFromSilk(False) +popt.SetPlotReference(True) +popt.SetPlotValue(False) +popt.SetPlotInvisibleText(True) + +pctl.SetLayer(F_SilkS) +pctl.OpenPlotfile("AssyTop", PLOT_FORMAT_PDF, "Assembly top") +pctl.PlotLayer() + +############################### +#### AssyOutlinesTop.pdf ##### +############################### + +# And a gerber with only the component outlines (really!) +popt.SetPlotReference(False) +popt.SetPlotInvisibleText(False) +pctl.SetLayer(F_SilkS) +pctl.OpenPlotfile("AssyOutlinesTop", PLOT_FORMAT_PDF, "Assembly outline top") +pctl.PlotLayer() + +###################### +#### Layout.pdf ##### +###################### + +# The same could be done for the bottom side, if there were components +popt.SetUseAuxOrigin(False) + +## For documentation we also want a general layout PDF +## I usually use a shell script to merge the ps files and then distill the result +## Now I can do it with a control file. As a bonus I can have references in a +## different colour, too. + +popt.SetPlotReference(True) +popt.SetPlotValue(True) +popt.SetPlotInvisibleText(False) +# Remember that the frame is always in color 0 (BLACK) and should be requested +# before opening the plot +popt.SetPlotFrameRef(False) +pctl.SetLayer(Dwgs_User) + +pctl.OpenPlotfile("Layout", PLOT_FORMAT_PDF, "General layout") +pctl.PlotLayer() + +# Do the PCB edges in yellow +popt.SetColor(YELLOW) +pctl.SetLayer(Edge_Cuts) +pctl.PlotLayer() + +## Comments in, uhmm... green +popt.SetColor(GREEN) +pctl.SetLayer(Cmts_User) +pctl.PlotLayer() + +# Bottom mask as lines only, in red +# popt.SetMode(LINE) +popt.SetColor(RED) +pctl.SetLayer(B_Mask) +pctl.PlotLayer() + +# Top mask as lines only, in blue +popt.SetColor(BLUE) +pctl.SetLayer(F_Mask) +pctl.PlotLayer() + +# Top paste in light blue, filled +popt.SetColor(BLUE) +# popt.SetMode(FILLED) +pctl.SetLayer(F_Paste) +pctl.PlotLayer() + +# Top Silk in cyan, filled, references in dark cyan +popt.SetReferenceColor(DARKCYAN) +popt.SetColor(CYAN) +pctl.SetLayer(F_SilkS) +pctl.PlotLayer() + +######################## +#### Assembly.svg ##### +######################## + +popt.SetTextMode(PLOTTEXTMODE_STROKE) +pctl.SetLayer(F_Mask) +pctl.OpenPlotfile("Assembly", PLOT_FORMAT_PDF, "Master Assembly") +pctl.SetColorMode(True) + +# We want *everything* +popt.SetPlotReference(True) +popt.SetPlotValue(True) +popt.SetPlotInvisibleText(True) + +# Remember than the DXF driver assigns colours to layers. This means that +# we will be able to turn references on and off simply using their layers +# Also most of the layer are now plotted in 'line' mode, because DXF handles +# fill mode almost like sketch mode (this is to keep compatibility with +# most CAD programs; most of the advanced primitive attributes required are +# handled only by recent autocads...); also the entry level cads (qcad +# and derivatives) simply don't handle polyline widths... +# +# Here I'm using numbers for colors and layers, I'm too lazy too look them up:P +popt.SetReferenceColor(19) +popt.SetValueColor(21) + +popt.SetColor(0) +# popt.SetMode(LINE) +pctl.SetLayer(B_SilkS) +pctl.PlotLayer() +popt.SetColor(14) +pctl.SetLayer(F_SilkS) +pctl.PlotLayer() +popt.SetColor(2) +pctl.SetLayer(B_Mask) +pctl.PlotLayer() +popt.SetColor(4) +pctl.SetLayer(F_Mask) +pctl.PlotLayer() +popt.SetColor(1) +pctl.SetLayer(B_Paste) +pctl.PlotLayer() +popt.SetColor(9) +pctl.SetLayer(F_Paste) +pctl.PlotLayer() +popt.SetColor(3) +pctl.SetLayer(Edge_Cuts) +pctl.PlotLayer() + +# Export the copper layers too... exporting one of them in filled mode with +# drill marks will put the marks in the WHITE later (since it tries to blank +# the pads...); these will be obviously great reference points for snap +# and stuff in the cad. A pctl function to only plot them would be +# better anyway... + +popt.SetColor(17) +# popt.SetMode(FILLED) +popt.SetDrillMarksType(PCB_PLOT_PARAMS.FULL_DRILL_SHAPE) +pctl.SetLayer(B_Cu) +pctl.PlotLayer() +popt.SetColor(20) +popt.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) +pctl.SetLayer(F_Cu) +pctl.PlotLayer() + +# At the end you have to close the last plot, otherwise you don't know when +# the object will be recycled! +pctl.ClosePlot() + +# We have just generated 21 plotfiles with a single script -- cgit v1.2.3