My first footprint creation script, feedback wanted

I’ve made a script that makes the footprints for the TE Connectivity MICRO-MATCH SMD FTE connectors. As this is my first footprint creation script, I would like some feedback before I post it as a merge request.

#
# Parts script module for TE Connectivity MICRO-MATCH SMD FTE footprints for KicCad
#
# This module is built on top of the kicad-footprint-generator framework
# by Thomas Pointhuber, https://github.com/pointhi/kicad-footprint-generator
#
# This module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This module is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kicad-footprint-generator. If not, see < http://www.gnu.org/licenses/ >.

#
# (C) 2020 Cedric de Wijs, <https://gitlab.com/Cedric5008>
#
#!/usr/bin/env python3

import sys
import os
import re

# load parent path of KicadModTree
sys.path.append(os.path.join(sys.path[0], "..", ".."))

# load scripts
sys.path.append(os.path.join(sys.path[0], ".."))

from KicadModTree import *

series = "MICRO-MATCH SMD FTE"
manufacturer = 'TE Connectivity'
orientation = 'H'
number_of_rows = 2
pitch_x = 2.54 #between pins in a single row
x_offset_betweens_rows = 1.27
pad_size_x = 1.5
pad_size_y = 3.0
y_offset_between_rows = pad_size_y+1.5

y_centerline_offset_from_pin1 = y_offset_between_rows/2

y_total_pin_size = pad_size_y + y_offset_between_rows
x_courtyard_clearance = 0.5 #see F5.3
y_courtyard_clearance = 0.5 #see F5.3
silkscreen_clearance = 0.25
y_courtyard_size = y_total_pin_size + 2*y_courtyard_clearance #ignores the clearance needed for the mating connector
y_silkscreen_size = y_total_pin_size + 2*silkscreen_clearance

x_overhang_from_middle_of_pin = 2.1 #B size in datasheet is rounded, this is the max value, taken from the 20 pin model
y_body_size = 5.2

y_component_value_offset = 7.3

datasheet = 'https://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Customer+Drawing%7F188275%7FS4%7Fpdf%7FEnglish%7FENG_CD_188275_S4.pdf%7F2-188275-0'

myParameters = [[2,"0-188275-4"],
		[3,"0-188275-6"],
		[4,"0-188275-8"],
		[5,"1-188275-0"],
		[6,"1-188275-2"],
		[7,"1-188275-4"],
		[8,"1-188275-6"],
		[9,"1-188275-8"],
		[10,"2-188275-0"]]

numrows = len(myParameters)

def generate_one_footprint(number_of_pins,part_number):
	footprint_name = part_number

	x_centerline_offset_from_pin1 = (number_of_pins/2)*2.54 - x_offset_betweens_rows/2

	# init kicad footprint
	kicad_mod = Footprint(footprint_name)
	kicad_mod.setDescription(part_number)
	kicad_mod.setTags("connector top entry")

	

	# set general values
	kicad_mod.append(Text(type='reference', text='REF**', at=[x_centerline_offset_from_pin1, -3], layer='F.SilkS'))
	
	# create silk screen
	kicad_mod.append(RectLine(start=[-x_overhang_from_middle_of_pin-silkscreen_clearance, (y_centerline_offset_from_pin1-y_silkscreen_size/2)], end=[(number_of_pins-0.5)*pitch_x+x_overhang_from_middle_of_pin+silkscreen_clearance, (y_centerline_offset_from_pin1+y_silkscreen_size/2)], layer='F.SilkS'))

	# pin 1 designator
	kicad_mod.append(Polygon(nodes=[[0,-2.54], [1.27,-3.81], [-1.27,-3.81]], layer='F.SilkS'))

	# create courtyard (see F5.3 - Courtyard layer requirements)
	kicad_mod.append(RectLine(start=[-x_overhang_from_middle_of_pin-x_courtyard_clearance, (y_centerline_offset_from_pin1-y_courtyard_size/2)], end=[(number_of_pins-0.5)*pitch_x+x_overhang_from_middle_of_pin+x_courtyard_clearance, (y_centerline_offset_from_pin1+y_courtyard_size/2)], layer='F.CrtYd'))

	# create fabrication layer
	kicad_mod.append(RectLine(start=[-x_overhang_from_middle_of_pin, (y_centerline_offset_from_pin1-y_body_size/2)], end=[(number_of_pins-0.5)*pitch_x+x_overhang_from_middle_of_pin, (y_centerline_offset_from_pin1+y_body_size/2)], layer='F.Fab'))
	
	kicad_mod.append(Text(type='value', text=footprint_name, at=[x_centerline_offset_from_pin1, y_component_value_offset], layer='F.Fab'))
	kicad_mod.append(Text(type='user', text='%R', at=[x_centerline_offset_from_pin1, y_centerline_offset_from_pin1], layer='F.Fab'))


	# create pads
	for pinnr in list (range(number_of_pins)):
	    kicad_mod.append(Pad(number=(pinnr*2)+1, type=Pad.TYPE_SMT, shape=Pad.SHAPE_RECT,
		             at=[pinnr*pitch_x, 0], size=[pad_size_x, pad_size_y], layers=Pad.LAYERS_SMT))
	    kicad_mod.append(Pad(number=(pinnr*2)+2, type=Pad.TYPE_SMT, shape=Pad.SHAPE_RECT,
		             at=[pinnr*pitch_x+x_offset_betweens_rows, y_offset_between_rows], size=[pad_size_x, pad_size_y], layers=Pad.LAYERS_SMT))

	# add model
	wlrname = '{fp_name:s}.wrl'.format(fp_name=footprint_name)
	kicad_mod.append(Model(filename=wlrname,
		               at=[0, 0, 0], scale=[1, 1, 1], rotate=[0, 0, 0]))

	# output kicad model
	filename =  '{fp_name:s}.kicad_mod'.format(fp_name=footprint_name)
	file_handler = KicadFileHandler(kicad_mod)
	file_handler.writeFile(filename)


for i in list (range(numrows)):	
    number_of_pins = myParameters[i][0]
    part_number = myParameters[i][1]
    generate_one_footprint(number_of_pins,part_number)

In general i would suggest getting things like the courtyard clearance and line width from the central yaml files that are part of the script repo but let the user overwrite the paths via command line parameters (argparse library).

Plus use the pinarray node to generate multiple pads in a row instead of using a for loop.

Using a filled polygon on the silk layer for point to pin 1 is at least untypical but well why not (might be worth having a command line parameter here as well)
I am missing the pin 1 marker on the fab layer. (Only necessary if you want to be KLC complient)

For adding the text fields i also suggest the use of the addTextFields function in from footprint_text_fields (in the tools subfolder). It interfaces with the yaml config files and basically allows configurating how many text fields, on what layer and in what size there will be in the footprint all from the config file.

Set the footprint attribute to “smd”.

One example to follow could be conn_ffc_molex_200528.py (it contains all the features i suggested above).

Especially the use of the yaml configuration file is something i really suggest you look into as it makes it really easy to tailor how the footprint looks like. It allows making them for your own standard while easily switching to KLC compatible just by exchanging the config file.

1 Like

Thanks for the in-dept feedback, there’s a lot more to writing a footprint generator than I thought.

After reading this documentation[1], I conclude pinarray can not make staggered pins, so i think i need two calls to pinarray, one for the even pads (with initial=2, increment=2,), and one for the odd pads (with initial=1, increment=2,). Is this correct?

[1] https://kicad-footprint-generator.readthedocs.io/en/latest/KicadModTree.nodes.specialized.html#module-KicadModTree.nodes.specialized.PadArray

1 Like

Yes this is correct.

By the way i am not sure how up to date the docu is. I personally always just look at the code itself to find ouf what a node is capable of doing.

I’ve found the configuration file, but is does not have a value for courtyard clearance as far as I can see:

$ cat scripts/tools/global_config_files/config_KLCv3.0.yaml
3d_model_prefix: '${KISYS3DMOD}/'

silk_line_width: 0.12
silk_pad_clearance: 0.2
silk_fab_offset: 0.11
silk_line_lenght_min: 0.2
allow_silk_below_part: 'tht' # tht | smd | all | none

fab_line_width: 0.1
fab_pin1_marker_length: 1
fab_bevel_size_absolute: 1 # Bevel size of footprints in mm
fab_bevel_size_relative: 0.25 # Bevel size of footprints relative to package size

courtyard_line_width: 0.05
courtyard_grid: 0.01
courtyard_offset:
    default: 0.25
    connector: 0.5
    bga: 1

edge_cuts_line_width: 0.12

# IPC-7351C will most likely suggest the use of rounded rectangle pads
# with 25% radius ratio but at a maximum of 0.25mm
round_rect_max_radius: 0.25
round_rect_radius_ratio: 0.25

references:
    -
          layer: 'F.SilkS'
          position_y: 'outside_top' # outside_top | inside | outside_bottom
          size: [1,1]
          fontwidth: 0.15
    -
          layer: 'F.Fab'
          position_y: 'inside' # outside_top | inside | outside_bottom
          size_max: [1,1]
          size_min: [0.25, 0.25]
          thickness_factor: 0.15
          # size: [1,1]
          # fontwidth: 0.15
values:
    -
          layer: 'F.Fab'
          position_y: 'outside_bottom' # outside_top | inside | outside_bottom
          size: [1,1]
          fontwidth: 0.15

It is called courtyard offset here

Thank you again for your feedback. I’ve not yet verified the footprint with the physical device, I’ll do that shortly.

Does this version comply with the KLC?
Is there anything I can do to improve it?

#
# Parts script module for TE Connectivity MICRO-MATCH SMD FTE footprints for KicCad
#
# This module is built on top of the kicad-footprint-generator framework
# by Thomas Pointhuber, https://github.com/pointhi/kicad-footprint-generator
#
# This module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This module is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kicad-footprint-generator. If not, see < http://www.gnu.org/licenses/ >.

#
# (C) 2020 Cedric de Wijs, <https://gitlab.com/Cedric5008>
#
#!/usr/bin/env python3

import sys
import os
#sys.path.append(os.path.join(sys.path[0],"..","..","kicad_mod")) # load kicad_mod path

# export PYTHONPATH="${PYTHONPATH}<path to kicad-footprint-generator directory>"
sys.path.append(os.path.join(sys.path[0], "..", "..", ".."))  # load parent path of KicadModTree
from math import floor
import argparse
import yaml
from helpers import *
from KicadModTree import *

sys.path.append(os.path.join(sys.path[0], "..", "..", "tools"))  # load parent path of tools
from footprint_text_fields import addTextFields
from footprint_keepout_area import addRectangularKeepout

pinrange = range(4, 22, 2) #4,22,2 produces 4,6,8,10,12,14,16,18,20. It does not include 22

series = "MICRO-MATCH SMD FTE"
series_long = ('TE Connectivity 1.27mm Pitch Micro-MaTch, ' +
               'Female-On-Board SMD')
manufacturer = 'TE Connectivity'
orientation = 'V'
number_of_rows = 2

conn_category = "MICROMATCH"

lib_by_conn_category = True

part_code = "{:01d}-188275-{:01d}"

pitch = 1.27
pad_size_x = 1.5
pad_size_y = 3.0
y_offset_between_rows = pad_size_y+1.5

y_centerline_offset_from_pin1 = y_offset_between_rows/2

y_total_pin_size = pad_size_y + y_offset_between_rows


b20_size_from_datasheet = 27.5  #B size in datasheet is rounded, this is the max value, taken from the 20 pin model
x_overhang_from_middle_of_pin = (b20_size_from_datasheet - 19 * pitch) / 2
y_body_size = 5.2

y_component_value_offset = 7.3

datasheet = 'https://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Customer+Drawing%7F188275%7FS4%7Fpdf%7FEnglish%7FENG_CD_188275_S4.pdf%7F2-188275-0'

def make_module(pin_count, configuration):

	mpn = part_code.format(int(floor(pin_count/10)),(pin_count%10))
		
	orientation_str = configuration['orientation_options'][orientation]
	footprint_name = configuration['fp_name_format_string'].format(
		man = manufacturer,
		series = series,
		mpn = mpn,
		num_rows = number_of_rows,
		pins = pin_count,
		pins_per_row = int(floor(pin_count/2)),
		mounting_pad = "",
		pitch = pitch,
		orientation = orientation_str)
	
	footprint_name = footprint_name.replace("__",'_')

	kicad_mod = Footprint(footprint_name)
	kicad_mod.setAttribute('smd')
	kicad_mod.setDescription(
		("Molex {:s}, {:s}, {:d} Circuits ({:s}), " +
		 "generated with kicad-footprint-generator").format(
		series_long, mpn, pin_count, datasheet)) #todo adjust
	kicad_mod.setTags(configuration['keyword_fp_string'].format(
		man=manufacturer,
		series=series,
		orientation=orientation_str,
		entry=configuration['entry_direction'][orientation]))

	

	pad_silk_off = (configuration['silk_pad_clearance'] +
		(configuration['silk_line_width'] / 2))
	fab_silk_off = configuration['silk_fab_offset']
	silk_pad_clearance = configuration['silk_pad_clearance']
	silk_line_width = configuration['silk_line_width']

	x_centerline_offset_from_pin1 = (pin_count/2)*2.54 - pitch/2

	## Pads ##
	
	kicad_mod.append(
		PadArray(start = [0, 0],
			initial = 1,
			increment = 2,
			pincount = int(floor(pin_count/2)),
			x_spacing = pitch*2,
			type = Pad.TYPE_SMT,
			shape = Pad.SHAPE_RECT,
			size = [pad_size_x,pad_size_y],
			#size = pad_size,
			layers = Pad.LAYERS_SMT))
			
	kicad_mod.append(
		PadArray(start = [pitch, y_offset_between_rows],
			initial = 2,
			increment = 2,
			pincount = int(floor(pin_count/2)),
			x_spacing = pitch*2,
			type = Pad.TYPE_SMT,
			shape = Pad.SHAPE_RECT,
			size = [pad_size_x,pad_size_y],
			layers = Pad.LAYERS_SMT))

	## Fab ##
	
	fab_body_outline = [
		{'x': -x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1-y_body_size/2)}, #bot left
		{'x': -x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1+y_body_size/2)}, #top left
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1+y_body_size/2)}, #top right
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1-y_body_size/2)}, #bot right
		{'x': -x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1-y_body_size/2)}, #bot left again, close the rect
	]

	kicad_mod.append(PolygoneLine(
		polygone = fab_body_outline,
		layer = 'F.Fab',
		width = configuration['fab_line_width']))

	fab_pin1_mark = [
		{'x': - 0.5, 'y': y_centerline_offset_from_pin1-y_body_size/2},
		{'x': 0, 'y': y_centerline_offset_from_pin1-y_body_size/2+0.75},
		{'x': 0.5, 'y': y_centerline_offset_from_pin1-y_body_size/2}
	]

	kicad_mod.append(PolygoneLine(
		polygone = fab_pin1_mark,
		layer = 'F.Fab',
		width = configuration['fab_line_width']))




	## SilkS ##
		
	# pin1 designator and leftside outline
	silk_outline_pin1 = [
		{'x': -pad_size_x/2-silk_pad_clearance-silk_line_width/2 ,'y': -pad_size_y/2}, #top of pin1 designator
		{'x': -pad_size_x/2-silk_pad_clearance-silk_line_width/2 ,'y': y_centerline_offset_from_pin1 + -y_body_size/2}, #top of body
		{'x': -x_overhang_from_middle_of_pin ,'y': y_centerline_offset_from_pin1  -y_body_size/2}, #left top edge of the body
		{'x': -x_overhang_from_middle_of_pin ,'y': y_centerline_offset_from_pin1 - y_body_size/4}, #one quarter bodysize down
		{'x': -x_overhang_from_middle_of_pin+y_body_size/8 ,'y': y_centerline_offset_from_pin1 - y_body_size/4}, #1/8 of bodysize in
		{'x': -x_overhang_from_middle_of_pin+y_body_size/8 ,'y': y_centerline_offset_from_pin1 + y_body_size/4}, #1/2 bodysize down
		{'x': -x_overhang_from_middle_of_pin ,'y': y_centerline_offset_from_pin1 + y_body_size/4}, #1/8 of bodysize out
		{'x': -x_overhang_from_middle_of_pin ,'y': y_centerline_offset_from_pin1 + y_body_size/2}, #left bot edge of the body
		{'x': pitch-pad_size_x/2-silk_pad_clearance-silk_line_width/2  ,'y': y_centerline_offset_from_pin1 + y_body_size/2}, #pin 2
	]

	kicad_mod.append(PolygoneLine(
		polygone = silk_outline_pin1,
		layer = 'F.SilkS',
		width = configuration['silk_line_width']))
	
	# rightside outline
	silk_outline_right = [
		{'x': (pin_count-2)*pitch+pad_size_x/2+silk_pad_clearance+silk_line_width/2  ,'y': (y_centerline_offset_from_pin1-y_body_size/2)}, #top last pin
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1-y_body_size/2)}, #top right
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (y_centerline_offset_from_pin1+y_body_size/2)}, #bot right
		{'x': (pin_count-1)*pitch+pad_size_x/2+silk_pad_clearance+silk_line_width/2 ,'y': (y_centerline_offset_from_pin1+y_body_size/2)}, #bot last pin
	]
	
	kicad_mod.append(PolygoneLine(
		polygone = silk_outline_right,
		layer = 'F.SilkS',
		width = configuration['silk_line_width']))

	# set general values
	kicad_mod.append(Text(type='reference', text='REF**', at=[x_centerline_offset_from_pin1, -3], layer='F.SilkS'))


	## CrtYd ##

	bounding_box = {
		'top': (y_centerline_offset_from_pin1-y_total_pin_size/2),
		'left': -x_overhang_from_middle_of_pin,
		'bottom': (y_centerline_offset_from_pin1+y_total_pin_size/2),
		'right': ((pin_count-1)*pitch+x_overhang_from_middle_of_pin)}

	cx1 = roundToBase(bounding_box['left']-configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])
	cy1 = roundToBase(bounding_box['top']-configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])

	cx2 = roundToBase(bounding_box['right']+configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])
	cy2 = roundToBase(bounding_box['bottom']+configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])

	kicad_mod.append(RectLine(
		start=[cx1, cy1], end=[cx2, cy2],
		layer='F.CrtYd', width=configuration['courtyard_line_width']))

	## Text ##
	
	addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=bounding_box, courtyard={'top':cy1, 'bottom':cy2}, fp_name=footprint_name, text_y_inside_position='center')


	##################### Output and 3d model ############################
	model3d_path_prefix = configuration.get('3d_model_prefix','${KISYS3DMOD}/')

	if lib_by_conn_category:
		lib_name = configuration['lib_name_specific_function_format_string'].format(category=conn_category)
	else:
		lib_name = configuration['lib_name_format_string'].format(series=series, man=manufacturer)

	model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'.format(
		model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=footprint_name)
	kicad_mod.append(Model(filename=model_name))

	output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
	if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity)
		os.makedirs(output_dir)
	filename =  '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=footprint_name)

	file_handler = KicadFileHandler(kicad_mod)
	file_handler.writeFile(filename)
	


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='use confing .yaml files to create footprints.')
    parser.add_argument('--global_config', type=str, nargs='?', help='the config file defining how the footprint will look like. (KLC)', default='../../tools/global_config_files/config_KLCv3.0.yaml')
    parser.add_argument('--series_config', type=str, nargs='?', help='the config file defining series parameters.', default='../conn_config_KLCv3.yaml')
    args = parser.parse_args()

    with open(args.global_config, 'r') as config_stream:
        try:
            configuration = yaml.safe_load(config_stream)
        except yaml.YAMLError as exc:
            print(exc)

    with open(args.series_config, 'r') as config_stream:
        try:
            configuration.update(yaml.safe_load(config_stream))
        except yaml.YAMLError as exc:
            print(exc)

    for pincount in pinrange:
        make_module(pincount, configuration)

For the generated footprint, you can test it yourself :wink:

It turns out the footprints do not comply to the KLC:
$ ./kicad-git-lib/kicad-library-utils/klc-check/check_footprint.py -v --nocolor
Checking footprint ‘TE Connectivity_MICRO-MATCH SMD FTE_1-188275-4_2x07_P1.27mm_Vertical’:

Violating G1.1
Only standard characters are used for naming libraries and components
Footprint name must contain only legal characters
This one is fixed by adding this line:
footprint_name = footprint_name.replace(" ",’_’)

Violating F5.2
Fabrication layer requirements
Second Reference Designator missing

Violating F6.2
For surface-mount devices, footprint anchor is placed in the middle of the footprint (IPC-7351).
Footprint anchor does not match calculated center of Pads or F.Fab
This is a result of my assumption that SMD and true-hole components have the same idea about the footprint anchor. It turns out the anchor for true-hole is the center of pin 1, the anchor for SMD is the center of the part.

To be continued…

Yea space chars are dangerous. Do not use them in file names.

This should be fixed if you use the standard way to add all text fields.
addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=bounding_box, courtyard={'top':cy1, 'bottom':cy2}, fp_name=footprint_name, text_y_inside_position='center')
I wonder why it complains as you have this in. Is it possible you did not read in the KLC compliant config file?

TLDR: The check code for F5.2 and the code that generates the second refdef are not in agreement. I don’t know which is correct.

I guess it did read the configuration file. My script is at this location:
$ pwd
/home/cedric/kicad-git-lib/kicad-footprint-generator/scripts/Connector/Connector_TE-Connectivity
$ ls conn_te_188275.py
conn_te_188275.py
The configuration file is in the correct location:
$ ls …/…/tools/global_config_files/config_KLCv3.0.yaml
…/…/tools/global_config_files/config_KLCv3.0.yaml
The configuration is read in, as these lines should print an exception otherwise:

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='use confing .yaml files to create footprints.')
    parser.add_argument('--global_config', type=str, nargs='?', help='the config file defining how the footprint will look like. (KLC)', default='../../tools/global_config_files/config_KLCv3.0.yaml')
    parser.add_argument('--series_config', type=str, nargs='?', help='the config file defining series parameters.', default='../conn_config_KLCv3.yaml')
    args = parser.parse_args()

    with open(args.global_config, 'r') as config_stream:
        try:
            configuration = yaml.safe_load(config_stream)
        except yaml.YAMLError as exc:
            print(exc)

    with open(args.series_config, 'r') as config_stream:
        try:
            configuration.update(yaml.safe_load(config_stream))
        except yaml.YAMLError as exc:
            print(exc)

    for pincount in pinrange:
        make_module(pincount, configuration)

The config file does have two sections for references:

    -
          layer: 'F.SilkS'
          position_y: 'outside_top' # outside_top | inside | outside_bottom
          size: [1,1]
          fontwidth: 0.15
    -
          layer: 'F.Fab'
          position_y: 'inside' # outside_top | inside | outside_bottom
          size_max: [1,1]
          size_min: [0.25, 0.25]
          thickness_factor: 0.15
          # size: [1,1]
          # fontwidth: 0.15

The generated footprint does contain %R on the layer F.Fab:
TE_Connectivity_MICRO-MATCH_SMD_FTE_0-188275-4_2x02_P1.27mm_Vertical.kicad_mod (3.0 KB)

(fp_text user %R (at 1.91 2.25) (layer F.Fab)
    (effects (font (size 1 1) (thickness 0.15)))

In the footprint editor, the second reference on F.Fab is visible:

This is the code that checks for the second ref: (F5_2.py)
F5_2.py (9.4 KB)

    def getSecondRef(self):
        texts = self.module.userText

        ref = None

        count = 0

        for text in texts:
            if text['user'] == '${REFERENCE}':
                ref = text
                count += 1

        self.multiple_second_ref = count > 1

        return ref

    # Check that there is a second ref '${REFERENCE}' on the fab layer
    def checkSecondRef(self):

        ref = self.getSecondRef()

        # No second ref provided? That is ok for virtual footprints
        if not ref:
            if self.module.attribute != 'virtual':
                self.error("Second Reference Designator missing")
                self.errorExtra("Add RefDes to F.Fab layer with '${REFERENCE}'")
                return True
            else:
                return False

Replacing this line in my footprint file:
(fp_text user %R (at 5.72 2.25) (layer F.Fab)
With this line removes violation F5.2:
(fp_text user ${REFERENCE} (at 1.91 2.25) (layer F.Fab)

Conclusion: The check code for F5.2 and the code that generates the second refdef are not in agreement. I don’t know which is correct.

Lets summon the big guns: @chschlue it seems either the KLC test script is too strict here or the footprint generators ALL need updating.

They do. The check script has already been updated to v6 environment variable style ${REFERENCE}.

Rome wasn’t built in a day, either :wink:

Edit: Missed https://gitlab.com/kicad/libraries/kicad-footprint-generator/-/merge_requests/661

1 Like

Confirmed. Updating kicad-footprints-generator with the following commands solved the problem:

$ cd kicad-footprint-generator/
[cedric@cedric-p4 kicad-footprint-generator]$ git fetch upstream
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 17 (delta 5), reused 12 (delta 4), pack-reused 0
Unpacking objects: 100% (17/17), 7.38 KiB | 686.00 KiB/s, done.
From https://gitlab.com/kicad/libraries/kicad-footprint-generator
   aebd914..a7e1d5c  master     -> upstream/master
[cedric@cedric-p4 kicad-footprint-generator]$ git merge upstream/master
Merge made by the 'recursive' strategy.
 .../size_definitions/sot.yaml                             | 15 ++++++++++++++-
 scripts/Packages/ipc_definitions.yaml                     | 15 ++++++++++-----
 scripts/tools/footprint_text_fields.py                    |  2 +-
 3 files changed, 25 insertions(+), 7 deletions(-)
1 Like

I think the name of the footprint the script produces is quite long. Is this a problem?

Connector_MICROMATCH.pretty/TE_Connectivity_MICRO-MATCH_SMD_FTE_0-188275-4_2x02_P1.27mm_Vertical.kicad_mod

This version of the script is fully KLC compliant. I’ve made a pull request for it:

Thanks for all the support and encouragements.

# This module is built on top of the kicad-footprint-generator framework
# by Thomas Pointhuber, https://github.com/pointhi/kicad-footprint-generator
#
# This module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This module is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kicad-footprint-generator. If not, see < http://www.gnu.org/licenses/ >.

#
# (C) 2020 Cedric de Wijs, <https://gitlab.com/Cedric5008>
#
#!/usr/bin/env python3

import sys
import os
#sys.path.append(os.path.join(sys.path[0],"..","..","kicad_mod")) # load kicad_mod path

# export PYTHONPATH="${PYTHONPATH}<path to kicad-footprint-generator directory>"
sys.path.append(os.path.join(sys.path[0], "..", "..", ".."))  # load parent path of KicadModTree
from math import floor
import argparse
import yaml
from helpers import *
from KicadModTree import *

sys.path.append(os.path.join(sys.path[0], "..", "..", "tools"))  # load parent path of tools
from footprint_text_fields import addTextFields
from footprint_keepout_area import addRectangularKeepout

pinrange = range(4, 22, 2) #4,22,2 produces 4,6,8,10,12,14,16,18,20. It does not include 22

series = "MICRO-MATCH SMD FTE"
series_long = ('TE Connectivity 1.27mm Pitch Micro-MaTch, ' +
               'Female-On-Board SMD')
manufacturer = 'TE Connectivity'
orientation = 'V'
number_of_rows = 2

conn_category = "MICROMATCH"

lib_by_conn_category = True

part_code = "{:01d}-188275-{:01d}"

pitch = 1.27
pad_size_x = 1.5
pad_size_y = 3.0
y_offset_between_rows = pad_size_y+1.5

y_centerline_offset_from_pin1 = y_offset_between_rows/2 #replace

y_total_pin_size = pad_size_y + y_offset_between_rows


b20_size_from_datasheet = 27.5  #B size in datasheet is rounded, this is the max value, taken from the 20 pin model
x_overhang_from_middle_of_pin = (b20_size_from_datasheet - 19 * pitch) / 2
y_body_size = 5.2

y_component_value_offset = 7.3

datasheet = 'https://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Customer+Drawing%7F188275%7FS4%7Fpdf%7FEnglish%7FENG_CD_188275_S4.pdf%7F2-188275-0'

def make_module(pin_count, configuration):

	mpn = part_code.format(int(floor(pin_count/10)),(pin_count%10))
		
	orientation_str = configuration['orientation_options'][orientation]
	footprint_name = configuration['fp_name_format_string'].format(
		man = manufacturer,
		series = series,
		mpn = mpn,
		num_rows = number_of_rows,
		pins = pin_count,
		pins_per_row = int(floor(pin_count/2)),
		mounting_pad = "",
		pitch = pitch,
		orientation = orientation_str)
	
	footprint_name = footprint_name.replace(" ",'_')
	footprint_name = footprint_name.replace("__",'_')

	kicad_mod = Footprint(footprint_name)
	kicad_mod.setAttribute('smd')
	kicad_mod.setDescription(
		("Molex {:s}, {:s}, {:d} Circuits ({:s}), " +
		 "generated with kicad-footprint-generator").format(
		series_long, mpn, pin_count, datasheet)) #todo adjust
	kicad_mod.setTags(configuration['keyword_fp_string'].format(
		man=manufacturer,
		series=series,
		orientation=orientation_str,
		entry=configuration['entry_direction'][orientation]))

	

	pad_silk_off = (configuration['silk_pad_clearance'] +
		(configuration['silk_line_width'] / 2))
	fab_silk_off = configuration['silk_fab_offset']
	silk_pad_clearance = configuration['silk_pad_clearance']
	silk_line_width = configuration['silk_line_width']

	x_centerline_offset_from_pin1 = (pin_count/2)*2.54 - pitch/2
	x_pin1_offset_from_center = -(pin_count/2-0.5)*pitch

	## Pads ##
			
	kicad_mod.append(
		PadArray(start = [x_pin1_offset_from_center, -y_offset_between_rows/2],
			initial = 1,
			increment = 2,
			pincount = int(floor(pin_count/2)),
			x_spacing = pitch*2,
			type = Pad.TYPE_SMT,
			shape = Pad.SHAPE_RECT,
			size = [pad_size_x,pad_size_y],
			#size = pad_size,
			layers = Pad.LAYERS_SMT))
			
	kicad_mod.append(
		PadArray(start = [x_pin1_offset_from_center+pitch, y_offset_between_rows/2],
			initial = 2,
			increment = 2,
			pincount = int(floor(pin_count/2)),
			x_spacing = pitch*2,
			type = Pad.TYPE_SMT,
			shape = Pad.SHAPE_RECT,
			size = [pad_size_x,pad_size_y],
			layers = Pad.LAYERS_SMT))

	## Fab ##
	
	fab_body_outline = [
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center,'y': (-y_body_size/2)}, #bot left
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center,'y': (y_body_size/2)}, #top left
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin+x_pin1_offset_from_center,'y': (y_body_size/2)}, #top right
		{'x': (pin_count-1)*pitch+x_overhang_from_middle_of_pin+x_pin1_offset_from_center,'y': (-y_body_size/2)}, #bot right
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center,'y': (-y_body_size/2)}, #bot left again, close the rect
	]

	kicad_mod.append(PolygoneLine(
		polygone = fab_body_outline,
		layer = 'F.Fab',
		width = configuration['fab_line_width']))

	fab_pin1_mark = [
		{'x': - 0.5+x_pin1_offset_from_center, 'y': -y_body_size/2},
		{'x': x_pin1_offset_from_center, 'y': -y_body_size/2+0.75},
		{'x': 0.5+x_pin1_offset_from_center, 'y': -y_body_size/2}
	]

	kicad_mod.append(PolygoneLine(
		polygone = fab_pin1_mark,
		layer = 'F.Fab',
		width = configuration['fab_line_width']))




	## SilkS ##
		
	# pin1 designator and leftside outline
	silk_outline_pin1 = [
		{'x': -pad_size_x/2-silk_pad_clearance-silk_line_width/2+x_pin1_offset_from_center ,'y': -y_centerline_offset_from_pin1-pad_size_y/2}, #top of pin1 designator
		{'x': -pad_size_x/2-silk_pad_clearance-silk_line_width/2+x_pin1_offset_from_center ,'y': -y_body_size/2}, #top of body
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center ,'y': -y_body_size/2}, #left top edge of the body
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center ,'y': - y_body_size/4}, #one quarter bodysize down
		{'x': -x_overhang_from_middle_of_pin+y_body_size/8+x_pin1_offset_from_center ,'y': - y_body_size/4}, #1/8 of bodysize in
		{'x': -x_overhang_from_middle_of_pin+y_body_size/8+x_pin1_offset_from_center ,'y': + y_body_size/4}, #1/2 bodysize down
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center ,'y': y_body_size/4}, #1/8 of bodysize out
		{'x': -x_overhang_from_middle_of_pin+x_pin1_offset_from_center ,'y': y_body_size/2}, #left bot edge of the body
		{'x': pitch-pad_size_x/2-silk_pad_clearance-silk_line_width/2+x_pin1_offset_from_center  ,'y': y_body_size/2}, #pin 2
	]

	kicad_mod.append(PolygoneLine(
		polygone = silk_outline_pin1,
		layer = 'F.SilkS',
		width = configuration['silk_line_width']))
	
	# rightside outline
	silk_outline_right = [
		{'x': x_pin1_offset_from_center+(pin_count-2)*pitch+pad_size_x/2+silk_pad_clearance+silk_line_width/2  ,'y': (-y_body_size/2)}, #top last pin
		{'x': x_pin1_offset_from_center+(pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (-y_body_size/2)}, #top right
		{'x': x_pin1_offset_from_center+(pin_count-1)*pitch+x_overhang_from_middle_of_pin,'y': (y_body_size/2)}, #bot right
		{'x': x_pin1_offset_from_center+(pin_count-1)*pitch+pad_size_x/2+silk_pad_clearance+silk_line_width/2 ,'y': (y_body_size/2)}, #bot last pin
	]
	
	kicad_mod.append(PolygoneLine(
		polygone = silk_outline_right,
		layer = 'F.SilkS',
		width = configuration['silk_line_width']))

	# set general values
	kicad_mod.append(Text(type='reference', text='REF**', at=[x_centerline_offset_from_pin1, -3], layer='F.SilkS'))


	## CrtYd ##

	bounding_box = {
		'top': (-y_total_pin_size/2),
		'left': x_pin1_offset_from_center-x_overhang_from_middle_of_pin,
		'bottom': (y_total_pin_size/2),
		'right': (x_pin1_offset_from_center+(pin_count-1)*pitch+x_overhang_from_middle_of_pin)}

	cx1 = roundToBase(bounding_box['left']-configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])
	cy1 = roundToBase(bounding_box['top']-configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])

	cx2 = roundToBase(bounding_box['right']+configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])
	cy2 = roundToBase(bounding_box['bottom']+configuration['courtyard_offset']['connector'], configuration['courtyard_grid'])

	kicad_mod.append(RectLine(
		start=[cx1, cy1], end=[cx2, cy2],
		layer='F.CrtYd', width=configuration['courtyard_line_width']))

	## Text ##
	
	addTextFields(kicad_mod=kicad_mod, configuration=configuration, body_edges=bounding_box, courtyard={'top':cy1, 'bottom':cy2}, fp_name=footprint_name, text_y_inside_position='center')


	##################### Output and 3d model ############################
	model3d_path_prefix = configuration.get('3d_model_prefix','${KISYS3DMOD}/')

	if lib_by_conn_category:
		lib_name = configuration['lib_name_specific_function_format_string'].format(category=conn_category)
	else:
		lib_name = configuration['lib_name_format_string'].format(series=series, man=manufacturer)

	model_name = '{model3d_path_prefix:s}{lib_name:s}.3dshapes/{fp_name:s}.wrl'.format(
		model3d_path_prefix=model3d_path_prefix, lib_name=lib_name, fp_name=footprint_name)
	kicad_mod.append(Model(filename=model_name))

	output_dir = '{lib_name:s}.pretty/'.format(lib_name=lib_name)
	if not os.path.isdir(output_dir): #returns false if path does not yet exist!! (Does not check path validity)
		os.makedirs(output_dir)
	filename =  '{outdir:s}{fp_name:s}.kicad_mod'.format(outdir=output_dir, fp_name=footprint_name)

	file_handler = KicadFileHandler(kicad_mod)
	file_handler.writeFile(filename)
	


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='use confing .yaml files to create footprints.')
    parser.add_argument('--global_config', type=str, nargs='?', help='the config file defining how the footprint will look like. (KLC)', default='../../tools/global_config_files/config_KLCv3.0.yaml')
    parser.add_argument('--series_config', type=str, nargs='?', help='the config file defining series parameters.', default='../conn_config_KLCv3.yaml')
    args = parser.parse_args()

    with open(args.global_config, 'r') as config_stream:
        try:
            configuration = yaml.safe_load(config_stream)
        except yaml.YAMLError as exc:
            print(exc)

    with open(args.series_config, 'r') as config_stream:
        try:
            configuration.update(yaml.safe_load(config_stream))
        except yaml.YAMLError as exc:
            print(exc)

    for pincount in pinrange:
        make_module(pincount, configuration)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.