Python script for KiCad-Pre-place footprint almost according to the schematic layout

Let's create a script for KiCad while studying Python

Since the 4.0 version of KiCad, Pcbnew supports Python scripts, and it is possible to acquire information from the board, change attributes collectively, and automate work, so I created this script as well as studying Python. Did. Even if you take the parts layout into consideration when drawing the circuit diagram, it is no wonder that such information disappears when you try to import the net into the board CAD.

Some commercial CAD can reflect not only the component layout but also the wiring order and the point of one-point grounding, but it will be much easier just to reflect the approximate layout.

Script execution video (Youtube)

Sites and reference materials that I referred to this time

KiCad Python Script Reference

hanya's blog

Tokyo Institute of Technology Robot Technology Study Group

This is a document of Eeschema file format translated by volunteers of kicad.jp before, but the structure of the schematic file is the same in 4.0.1 version as in BZR4022 version. is.

Regarding various formats of KiCad, there was a document in the installation directory in the previous version, but it is not found in v4.0 or later, and pdf is published on launchpad. http://bazaar.launchpad.net/~stambaughw/kicad/doc-read-only/download/head:/1115%4016bec504-3128-0410-b3e8-8e38c2123bca:trunk%252Fkicad-doc%252Fdoc%252Fhelp%252Ffile_formats%252Ffile_formats.pdf/file_formats.pdf

Script description

The script is as follows. The basic flow is

  1. Get board information
  2. Read the schematic file
  3. Extract the "$ Comp" section (information on individual schematic symbols) and store it in a list
  4. Exclude power symbols such as GND
  5. Extract the first unit
  6. Extract references and coordinates
  7. Parts placement based on the reference (change of absolute coordinate value)

is.

ki_pre_place.py


# coding: utf-8
# KiCad footprint pre place tool (2016-01-30)
# 
# This script places footprints based on the XY coordinate
# of Eeschema's schematic components.


# import sys
import codecs
import pcbnew

def input_num():
    while True:
        try:
            v = float(sys.stdin.readline())
            return v
        except ValueError:
            print 'Value Error!'

comp_flg    = False    # For Detect $Comp section
qty         = -1       # Comp counter
zuwaku      = []       # Sch sheet size
sch_comp    = []       # All Comp info (expect PWR-Symbol)
sch_comp_xy = []       # Comp Location List
pcb_comp_xy = []       # pcb location

scl_size  = float(0.4)  # Scaling for pcb
offset_x  = float(2000) # PCB Place offset X(mils)
offset_y  = float(2000) #                  Y(mils)

pcb      = pcbnew.GetBoard()        # Get pcb all info
pcb_name = pcb.GetFileName()        # current pcb file name
sch_name = pcb_name.replace('.kicad_pcb','.sch') # Generate Sch file name

org_aux  = pcb.GetAuxOrigin()  # fab. origin(gerber etc.) and conv string (non used)
org_grid = pcb.GetGridOrigin() # grid origin and conv string (non used)

#Start Sch operation
f   = codecs.open(sch_name,'r','utf-8') # Sch file reading (utf-8)
sch = f.readlines()
f.close()

for line in sch: # reading each sch lines...

    line = line.lstrip().rstrip()  # Del \t and \n
        
    if line.startswith('$Descr') : # Detect $Descr section...
        
        zuwaku    = line.replace('$Descr ','').split(' ')
        zuwaku[1] = float(zuwaku[1]) # X size
        zuwaku[2] = float(zuwaku[2]) # Y size
                
    if line.startswith('$Comp') : # Detect $Comp section...
        comp_flg = True           # flg on
        qty += 1                  # Component count
        sch_comp.append([])       # ex) sch_comp[0] = []  , sch_comp[1] = [] ....

    elif line.startswith('$EndComp') : # $EndComp -> flg off
        comp_flg = False

    if comp_flg :   # from $Comp to $EndComp
        
        if not line.startswith('$Comp') :  # except 1st line($Comp)
            sch_comp[qty].append(line)     # add comp all info
                        
            # ex) sch_comp[0][0] <- 'L 74AC04 U2'
            #             [0][1] <- 'U 1 1 512E0139'
            #             [0][2] <- 'P 4050 6950'
            #     ....



# Get Ref.No and XY
for c_line in sch_comp:

    if  '#' not in c_line[0] :          # except PWR SYMBOL (GND,Vcc,PWR_FLG etc.)
        ref      = c_line[0].split(' ')
        unit_num = c_line[1].split(' ')
        xy       = c_line[2].split(' ')
        angle    = c_line[-1]           # orientation matrix (angle and mirror)
        
        if unit_num[1] == '1': # detect 1st unit ( for OPAMP , logic IC etc.)
            
            xy_raw = [ref[2],float(xy[1]),float(xy[2]),angle] # make Ref,x,y list
            sch_comp_xy.append(xy_raw)
            
            # ex) sch_comp_xy[0][0] <- R1
            #                [0][1] <- X(mils)
            #                [0][2] <- Y(mils)
            #                [0][3] <- orientation matrix
            #
            #                [1][0] <- C1
            #                        .....

# End Sch operation

# print sch_comp     <- all comp list
# print sch_comp_xy  <- comp xy list
# print zuwaku       <- Sch paper size

# Start PCB operation

place_flg = 'n'
judge = 'n'

while place_flg == 'n' :

    if judge != 'n' :
        pass
        
    else :
        print 'scale = ' + str(scl_size)
        print 'offset x,y(mil) = ' + '(' +str(offset_x) + ',' + str(offset_y) + ')'
        
        for posi in sch_comp_xy:
        
            pcb_x = int(posi[1] * scl_size) # convert xy Sch --> PCB (scaling)
            pcb_y = int(posi[2] * scl_size)
        
            module = pcb.FindModuleByReference(posi[0])           # Get Footprint info
            module.SetPosition(pcbnew.wxPointMils(pcb_x , pcb_y)) # Move abs x,y(mils)
            module.Move(pcbnew.wxPointMils(offset_x , offset_y))  # Move inc(offset from origin, mils)
        
        print '''
    Place Finished , Press[F3] Key(Redraw).
'''
        
        
        
    print 'Is this floor plan all right(y/n,a:abort)?' # Layout check...
    
    judge = sys.stdin.readline()
    
    if judge == 'y':          # Layout OK
        print 'Place Completed.'
        place_flg = 'y'
        
    elif judge == 'n':        # Change place scale and origin
        print 'place scale = '
        scl_size  = input_num()
        print 'offset x(mil) = '
        offset_x  = input_num()
        print 'offset y(mil) = '
        offset_y  = input_num()
    
    elif judge == 'a':        # abort
        print 'Aborted.'
        place_flg = 'a'
    
    else :
        pass

Coordinate extraction of schematic symbols (components)

I'm lucky if I can use the intermediate netlist that Eeschema spits out! I thought, but of course, unfortunately, there is no coordinate information of the symbol. Therefore, I create a schematic file name from the name of the board file that is currently open, read the schematic file from the beginning, and extract the necessary parts. My environment is Win7 64bit, but since the Eeschema file was UTF-8, I have specified the character code for the time being.

Part names starting with \ # are excluded because they are GND symbols and PWR_FLG. Moreover, when there are multiple units in one package such as operational amplifiers and logic ICs, only the first unit is extracted.

Although it is unused, the placement angle and mirror information of each symbol are stored in the "angle" (orientation matrix). I think that the angle is often rotated later, and since it was quite troublesome to judge the matrix, it is stored as an array. It also stores the schematic sheet size.

Placement on the board

After extracting the reference and coordinates, use the acquired board information to rotate the for statement in the coordinate value list and place (move) it on the board in absolute coordinates. Since the actual size arrangement in A4 is too wide, the coordinate values are scaled before arrangement.

I thought about getting the board size from PCBNew and scaling it automatically, but at present it seems that the approximate board size cannot be obtained with the script, so I am using the method of inputting the scale value. Eeschema's grid is 50mil by default, so a scale value of 0.25 to 0.5 seems to be just right.

We also modify the script to extract the grid origin and mounting origin so that the placement origin can be selected. ~~ (Commented out on lines 22 and 23) ~~

How to use

Before running the script

  1. Creating a netlist
  2. Creating and allocating footprints
  3. Reflection of netlist on the board
  4. Footprint placement (Global move / placement in footprint mode → Expand all footprints

To make it look like the figure below.

kicad_pre_place_1.png

Place the script in a suitable location, from the Pcbnew menu bar, from the Python script window.

execfile ('absolute path \ ki_pre_place.py')

Execute as, enter the placement origin and scale, and redisplay with the F3 key, the placement will be as shown below.

kicad_pre_place_2.png

important point

――It may be only my environment, but ** If you skip step 4 above, it will not be placed. ** It seems that the footprint needs to be on the board before running the script. -** It will not be redisplayed after placement , so please do it manually. We are investigating whether it can be redisplayed in a script. - It does not support hierarchical files of schematics. ** ** --You can undo after execution, but if you save the file once in the state of 4 above, reopen it, and execute it immediately after execution, it seems that you cannot undo. It may be a problem on the Pcbnew side, but it is unknown. Please note that the actual harm is small. --We have confirmed the operation on Win7. --Please note that no error handling is done.

Future improvement plans

-~~ The placement origin and scale are given as initial values, but if there is a way to redisplay with a script after footprint placement, a dialogue such as changing the scale and placement origin after seeing the result and re-executing I want to change it to input. ~~

--As for the correspondence to the hierarchical file, the $ Sch section has the hierarchical file name and coordinates, so it seems quite so, but is the above realization prioritized? .. .. ――Please point out any problems.

2016-03-21 Addendum It supports scaling, origin specification, and retry, but it is still convenient.

Impressions of creating the script

I also tried to study Python, but I was impressed to be able to realize the functions I wanted. We would like to thank the developers and those who provided the script information.

Although it is a solid script, I would like to be able to reuse the part that extracts the circuit diagram information. Since all the information in the "$ Comp" section is stored in the list once, it may be easier to pull it out of the list than to create a Bom from the intermediate netlist. I think it's until Eeschema supports scripts. ..

As an aside, it seems that the 3D CAD "FreeCAD" of OSS also has a Python script function. It seems that it can be conveniently used for linking libraries and board outlines with each other and checking for interference.

Recommended Posts

Python script for KiCad-Pre-place footprint almost according to the schematic layout
The fastest way for beginners to master Python
Python constants like None (according to the reference)
Python script to get a list of input examples for the AtCoder contest
Template of python script to read the contents of the file
[python] How to use the library Matplotlib for drawing graphs
[For beginners] Script within 10 lines (4. Connection from python to sqlite3)
Tips for Python beginners to use the Scikit-image example for themselves
I tried changing the python script from 2.7.11 to 3.6.0 on windows10
Dot according to the image
~ Tips for beginners to Python ③ ~
Introduction to Python For, While
How to change the log level of Azure SDK for Python
Create a shell script to run the python file multiple times
[Introduction to Python] How to use the in operator in a for statement?
Atom: Note for Indentation Error when copying Python script to shell
Script for backing up folders on the server to Google Drive
A script that returns 0, 1 attached to the first Python prime number
Get the script path in Python
In the python command python points to python3.8
How to get the Python version
[python] Copy script to generate copy log
[Python] How to import the library
AWS Layer Creation Script for python
See python for the first time
What is the python underscore (_) for?
An introduction to Python for non-engineers
Command for the current directory Python
[Python] Change the alphabet to numbers
I tried to refer to the fun rock-paper-scissors poi for beginners with Python
Add syntax highlighting for the Kv language to Spyder in the Python IDE
[Introduction to Udemy Python3 + Application] 47. Process the dictionary with a for statement
Tips for Python beginners to use the Scikit-image example for themselves 9 Use from C
Python> sys.path> List of strings indicating the path to search for modules
AtCoder writer I wrote a script to aggregate the contests for each writer
Take the free "Introduction to Python for Machine Learning" online until 4/27 application
Switch the module to be loaded for each execution environment in Python
Tips for Python beginners to use the Scikit-image example for themselves 6 Improve Python code