From 42df3597ad9444f8ee5296672d066020fb12ef5a Mon Sep 17 00:00:00 2001 From: ac Date: Tue, 12 Mar 2024 10:08:33 +1000 Subject: [PATCH] WIP: footprint generation based off of text on 'User.Drawings' layer --- .scripts/foot_gen.py | 182 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 .scripts/foot_gen.py diff --git a/.scripts/foot_gen.py b/.scripts/foot_gen.py new file mode 100644 index 0000000..2326499 --- /dev/null +++ b/.scripts/foot_gen.py @@ -0,0 +1,182 @@ +import sys +import os + +from pcbnew import * + +filename = sys.argv[1] + +pcb = LoadBoard(filename) + +ToUnits = ToMM +FromUnits = FromMM + +print("") +print("List drawings:") + + +# layers = pcb.GetEnabledLayers() +top_layer_id = pcb.GetLayerID("F.Cu") + + +new_foots = {} +foot_name = "" +foot_path = "" +for item in pcb.GetDrawings(): + if type(item) is PCB_TEXT and "User.Drawings" in item.GetLayerName(): + lines = str(item.GetText()).splitlines() + # Must have the correct header + if "Foot Pinout" not in lines[0]: + continue + if not lines[1].startswith("Name:"): + continue + foot_name = lines[1].split(":")[-1].strip() + if not lines[2].startswith("Path:"): + continue + foot_path = lines[2].split(":")[-1].strip() + for line in lines[3:]: + sp = line.split(':', 1) + if len(sp) != 2: + continue + # Path to substitue footprint + ref = sp[0] + foot_sp = sp[1].split(',') + foot = foot_sp[0].strip() + offset = (0,0) + # Optional offset tuple + if len(foot_sp) == 2: + offset = tuple(foot_sp[1]) + + new_foots[ref] = [foot, offset] + # pins = sp[1] + # pin_refs = [] + # for pin in pins.split(','): + # pin_refs.push(int(pin)) + + break + elif type(item) is PCB_TEXT: + print("Text: ", item.GetText()) + print("Layer: ", item.GetLayerName()) + +print("") +print("New Footprint List: ", new_foots) + +# Clear tracks +pcb.Tracks().clear() +# Clear zones +pcb.Zones().clear() +# Clear Drawings +pcb.Drawings().clear() + +# Keep only required foots +saved = [] +while len(pcb.Footprints()) > 1: + foot = pcb.Footprints().pop() + if foot.GetReference() in new_foots.keys(): + saved.append(foot) +# just in case +pcb.DeleteAllFootprints() +# Add them back +sorted_pads = [] +for foot in saved: + vals = new_foots[foot.GetReference()] + # Change the footprint + foot.SetFPIDAsString(vals[0]) + # Save the original postion of footprint + pads + orig_cent = foot.GetBoundingBox(False,False).Centre() + pads = [] + for pad in foot.Pads(): + pads.append([pad.GetCenter(), pad.GetNet()]) + + # Flip to other side + foot.SetLayerAndFlip(top_layer_id) + # Put back in original position with an offset + box = foot.GetBoundingBox(False,False) + new_cent = foot.GetBoundingBox(False,False).Centre() + cent_diff = orig_cent - new_cent + foot.SetX(foot.GetX() + cent_diff.x + vals[1][0]) + foot.SetY(foot.GetY() + cent_diff.y + vals[1][1]) + # Flip the net assignments of the pads + for pad in foot.Pads(): + sorted_pads.append(pad) + # Check position diff from old + # to new + new_cent = pad.GetCenter() + diffs = [] + for [old_cent,_] in pads: + diffs.append(new_cent - old_cent) + min_diff = min(diffs) + # If pad is within 0.05mm it's a match + if (min_diff.x**2 + min_diff.y**2)**0.5 < 50000: + # Set the net to what the old pad was + i = diffs.index(min_diff) + match_pad = pads[i][1:] + pad.SetNet(match_pad[0]) + + pcb.Add(foot) + + +# Sort by Y +sorted_pads.sort(key=lambda p: p.GetY()) + +box = pcb.GetBoundingBox() +brd_width = ToUnits(box.GetWidth()) +brd_height = ToUnits(box.GetHeight()) + +print("Box: ", box) +print("W: ", brd_width, "H: ", brd_height) + + +sys.path.append(os.path.join(sys.path[0],"../../../../../dev/kicad-footprint-generator")) + + +from KicadModTree import * +from KicadModTree.nodes.specialized.PadArray import PadArray + +footprint_name = "" + +# init kicad footprint +kicad_mod = Footprint(footprint_name, FootprintType.SMD) +kicad_mod.setDescription("A example footprint") +kicad_mod.setTags("example") + +# set general values +kicad_mod.append(Text(type='reference', text='REF**', at=[0,-3], layer='F.SilkS')) +kicad_mod.append(Text(type='value', text=footprint_name, at=[1.5,3], layer='F.Fab')) + +# create silscreen +kicad_mod.append(RectLine(start=[-brd_width/2,-brd_height/2], end=[brd_width/2,brd_height/2], layer='F.SilkS', width=0.15)) + +# create courtyard +kicad_mod.append(RectLine(start=[-brd_width/2 - 0.1,-brd_height/2 - 0.1], end=[brd_width/2 + 0.1,brd_height/2 + 0.1], layer='F.CrtYd', width=0.05, offset=2)) + +# create pads + +for x, pad in enumerate(sorted_pads): + cent = ToUnits(pad.GetCenter()) + pad_size = ToUnits(pad.GetSize()) + drill_size = ToUnits(pad.GetDrillSize()) + kicad_mod.append(Pad(number=x, type=Pad.TYPE_THT, shape=Pad.SHAPE_RECT, at=list(cent), size=list(pad_size), drill=list(drill_size), layers=['*.Cu', '*.Mask', 'F.SilkS'])) +# kicad_mod.append(Pad(number=22, type=Pad.TYPE_THT, shape=Pad.SHAPE_CIRCLE, at=[3,0], size=[2,2], drill=1.2, layers=['*.Cu', '*.Mask', 'F.SilkS'])) +# +# kicad_mod.append(Pad(number=12, type=Pad.TYPE_SMT, shape=Pad.SHAPE_RECT, at=[3,0], size=[2,2], drill=1.2, layers=['*.Cu', '*.Mask', 'F.SilkS'])) + +# add model +# kicad_mod.append(Model(filename="example.3dshapes/example_footprint.wrl" +# ,at=[0,0,0] +# ,scale=[1,1,1] +# ,rotate=[0,0,0])) + +# kicad_mod.append(PadArray(pincount=10,spacing=[1,-1],center=[0,0], initial=5, increment=2, type=Pad.TYPE_SMT, shape=Pad.SHAPE_RECT, size=[1,2], layers=["*.Cu"])) + +# output kicad model +#print(kicad_mod) + +# print render tree +#print(kicad_mod.getRenderTree()) +#print(kicad_mod.getCompleteRenderTree()) + +# write file +file_handler = KicadFileHandler(kicad_mod) +file_handler.writeFile('example_footprint.kicad_mod') + +SaveBoard("test/test.kicad_pcb", pcb)