michaelgale / pcbflow

Python based Printed Circuit Board (PCB) layout and design package based on CuFlow.
BSD 3-Clause "New" or "Revised" License
133 stars 16 forks source link

Unable to open KiCad footprints and libraries #9

Open ghost opened 7 months ago

ghost commented 7 months ago

I cannot complete any of the examples, except for the ones that do not include any parts:

l_code$ python esp32_motor-pcb-x3_final.py
Circuit:  Parts: 17  Nets: 36
Traceback (most recent call last):
  File "/home/twinlizzie/Ngnuity-Repos/sylvie-2024/schematics/skidl_code/esp32_motor-pcb-x3_final.py", line 270, in <module>
    sp = SkiPart(brd.DC(part.loc), part, side=side)
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 286, in __init__
    lfn = self._find_footprint_file(skipart.footprint)
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 316, in _find_footprint_file
    raise FileNotFoundError("Unable to find KiCAD footprints directory")
FileNotFoundError: Unable to find KiCAD footprints directory

Not sure how to set the footprint directory. I've tried modifying kicad.py itself, with a little bit of luck, though it only leads to more errors.

Code:

import os
import sys
import math
import glob
import shapely.geometry as sg

from pcbflow import *
from skidl import *

# Set the default tool to KiCad 7 for footprint management.
set_default_tool(KICAD7)

if __name__ == "__main__":
    ###
    ### SKiDL Circuit Declarations
    ###

    # Create ESP32 and TMC5160 headers with updated part names
    esp32_header1 = Part("Connector_Generic", "Conn_01x15", footprint="PinSocket_1x15_P2.54mm_Vertical")
    esp32_header2 = Part("Connector_Generic", "Conn_01x15", footprint="PinSocket_1x15_P2.54mm_Vertical")

    tmc5160_header1 = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")
    tmc5160_header2 = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")

    tmc5160_header1b = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")
    tmc5160_header2b = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")

    tmc5160_header1c = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")
    tmc5160_header2c = Part("Connector_Generic", "Conn_01x08", footprint="PinSocket_1x08_P2.54mm_Vertical")

    # Create 1x4 connector for stepper motor connections
    stepper_motor_header = Part("Connector_Generic", "Conn_01x04", footprint="PinHeader_1x04_P2.54mm_Vertical")
    stepper_motor_header_b = Part("Connector_Generic", "Conn_01x04", footprint="PinHeader_1x04_P2.54mm_Vertical")
    stepper_motor_header_c = Part("Connector_Generic", "Conn_01x04", footprint="PinHeader_1x04_P2.54mm_Vertical")

    # Create 1x4 connector for CAN Bus connections
    can_bus_header = Part("Connector_Generic", "Conn_01x04", footprint="PinHeader_1x04_P2.54mm_Vertical")

    # Create 100uF 63V capacitor with correct footprint
    capacitor = Part("Device", "C", value="100uF", voltage="63V", footprint="Capacitor_THT:CP_Radial_D10.0mm_P5.00mm")
    capacitor_b = Part("Device", "C", value="100uF", voltage="63V", footprint="Capacitor_THT:CP_Radial_D10.0mm_P5.00mm")
    capacitor_c = Part("Device", "C", value="100uF", voltage="63V", footprint="Capacitor_THT:CP_Radial_D10.0mm_P5.00mm")

    # Create a 3-pin terminal block for 36V connections
    terminal_block = Part("Connector_Generic", "Conn_01x03", footprint="TerminalBlock_Phoenix_PT-1,5-2-5.0-H_1x02_P5.00mm_Horizontal")

    # Create through-hole solder pads for 36V+ and 36V_GND connections with 2.54mm spacing
    terminal_block_2 = Part("Connector_Generic", "Conn_01x02", footprint="TerminalBlock_Phoenix_MPT-0,5-2-2.54_1x02_P2.54mm_Horizontal")

    # Create 36V_V+ and 36V_GND nets.
    V_plus_36V = Net("36V_V+")
    GND_36V = Net("36V_GND")

    # Define the GPIO pins according to ESP32 30pin version.
    esp32_vin = esp32_header1[1]
    esp32_gnd1 = esp32_header1[2]
    esp32_3v3 = esp32_header2[1]
    esp32_gnd2 = esp32_header2[2]
    esp32_gpio2 = esp32_header2[4]
    esp32_gpio4 = esp32_header2[5]
    esp32_gpio5 = esp32_header2[8]
    esp32_gpio12 = esp32_header1[4]
    esp32_gpio13 = esp32_header1[3]
    esp32_gpio14 = esp32_header1[5]
    esp32_gpio15 = esp32_header2[3]
    esp32_gpio16 = esp32_header2[6]
    esp32_gpio17 = esp32_header2[7]
    esp32_gpio18 = esp32_header2[9]
    esp32_gpio19 = esp32_header2[10]
    esp32_gpio21 = esp32_header2[11]
    esp32_gpio22 = esp32_header2[14]
    esp32_gpio23 = esp32_header2[15]
    esp32_gpio25 = esp32_header1[8]
    esp32_gpio26 = esp32_header1[7]
    esp32_gpio27 = esp32_header1[6]
    esp32_gpio32 = esp32_header1[10]
    esp32_gpio33 = esp32_header1[9]

    # Define the pins for TMC5160
    tmc5160_pins = {
        'gnd2': tmc5160_header1[8], 
        '3v3': tmc5160_header1[7],
        'm2b': tmc5160_header1[6],
        'm1b': tmc5160_header1[5],
        'm1a': tmc5160_header1[4],
        'm2a': tmc5160_header1[3],
        'gnd1': tmc5160_header1[2],
        'vmot': tmc5160_header1[1],
        'en': tmc5160_header2[1],
        'sdi': tmc5160_header2[2],
        'sck': tmc5160_header2[3],
        'csn': tmc5160_header2[4],
        'sdo': tmc5160_header2[5],
        'step': tmc5160_header2[7],
        'dir': tmc5160_header2[8]
    }

    # Define the pins for TMC5160b
    tmc5160b_pins = {
        'gnd2': tmc5160_header1b[8], 
        '3v3': tmc5160_header1b[7],
        'm2b': tmc5160_header1b[6],
        'm1b': tmc5160_header1b[5],
        'm1a': tmc5160_header1b[4],
        'm2a': tmc5160_header1b[3],
        'gnd1': tmc5160_header1b[2],
        'vmot': tmc5160_header1b[1],
        'en': tmc5160_header2b[1],
        'sdi': tmc5160_header2b[2],
        'sck': tmc5160_header2b[3],
        'csn': tmc5160_header2b[4],
        'sdo': tmc5160_header2b[5],
        'step': tmc5160_header2b[7],
        'dir': tmc5160_header2b[8]
    }

    # Define the pins for TMC5160c
    tmc5160c_pins = {
        'gnd2': tmc5160_header1c[8], 
        '3v3': tmc5160_header1c[7],
        'm2b': tmc5160_header1c[6],
        'm1b': tmc5160_header1c[5],
        'm1a': tmc5160_header1c[4],
        'm2a': tmc5160_header1c[3],
        'gnd1': tmc5160_header1c[2],
        'vmot': tmc5160_header1c[1],
        'en': tmc5160_header2c[1],
        'sdi': tmc5160_header2c[2],
        'sck': tmc5160_header2c[3],
        'csn': tmc5160_header2c[4],
        'sdo': tmc5160_header2c[5],
        'step': tmc5160_header2c[7],
        'dir': tmc5160_header2c[8]
    }

    # Create VIN and GND nets.
    vin = Net("VIN")
    gnd = Net("GND")

    # Create 3.3v gnd net.
    vcc_gnd = Net("VCC_GND")

    # Connect 36V_V+ and 36V_GND to the terminal block
    V_plus_36V += terminal_block[1]  # Connect 36V_V+ to the first pin of the PT terminal block slot 1
    GND_36V += terminal_block[2]     # Connect 36V_GND to the third pin of the PT terminal block slot 2

    # Connect VIN and GND to ESP32 header 2.
    vin += terminal_block_2[1] # Connect VIN to the MPT TerminalBlock slot 1
    gnd += terminal_block_2[2] # Connect GND to the MPT TerminalBlock slot 2

    # Connect capacitor between 36V_V+ and 36V_GND nets
    capacitor[1, 2] += V_plus_36V, GND_36V
    capacitor_b[1, 2] += V_plus_36V, GND_36V
    capacitor_c[1, 2] += V_plus_36V, GND_36V

    # Connect one pin of the limit switch header to the resistor and the other pin to ESP32 3.3v (Pull-down Resistor!)
    # limit_switch_header[1] += resistor1[2], esp32_header1[5]  # Connect pin 1 of limit switch header to one pin of the resistor, and pin 5 on esp32 header 1
    # limit_switch_header[2] += esp32_header2[1]           # Connect pin 2 of limit switch header to ESP32 3.3v pin

    # Connect VIN and GND to ESP32 and TMC5160 pins.
    vin += esp32_vin
    gnd += esp32_gnd1

    vcc_gnd += esp32_gnd2

    # vcc_gnd += resistor1[1] # GND connected to Resistor pin 1

    # Connect all the CAN Bus headers to the ESP32 Header 2
    can_bus_header[1] += esp32_3v3 # 3.3v
    can_bus_header[2] += esp32_gnd2 # GND
    can_bus_header[3] += esp32_gpio4 # CAN TX
    can_bus_header[4] += esp32_gpio5 # CAN RX

    # First TMC5160 Instance
    esp32_3v3 += tmc5160_pins['3v3']
    esp32_gnd2 += tmc5160_pins['gnd2']
    V_plus_36V += tmc5160_pins['vmot']
    GND_36V += tmc5160_pins['gnd1']
    vcc_gnd += tmc5160_pins['gnd2']
    esp32_gpio13 += tmc5160_pins['dir']
    esp32_gpio12 += tmc5160_pins['step']
    esp32_gpio15 += tmc5160_pins['en']
    esp32_gpio23 += tmc5160_pins['sdi']
    esp32_gpio19 += tmc5160_pins['sdo']
    esp32_gpio18 += tmc5160_pins['sck']
    esp32_gpio14 += tmc5160_pins['csn']
    stepper_motor_header[1] += tmc5160_pins['m2a']
    stepper_motor_header[2] += tmc5160_pins['m1a']
    stepper_motor_header[3] += tmc5160_pins['m1b']
    stepper_motor_header[4] += tmc5160_pins['m2b']

    # Second TMC5160 Instance
    esp32_3v3 += tmc5160b_pins['3v3']
    esp32_gnd2 += tmc5160b_pins['gnd2']
    V_plus_36V += tmc5160b_pins['vmot']
    GND_36V += tmc5160b_pins['gnd1']
    vcc_gnd += tmc5160b_pins['gnd2']
    esp32_gpio27 += tmc5160b_pins['dir']
    esp32_gpio26 += tmc5160b_pins['step']
    esp32_gpio17 += tmc5160b_pins['en']
    esp32_gpio23 += tmc5160b_pins['sdi']
    esp32_gpio19 += tmc5160b_pins['sdo']
    esp32_gpio18 += tmc5160b_pins['sck']
    esp32_gpio25 += tmc5160b_pins['csn']
    stepper_motor_header_b[1] += tmc5160b_pins['m2a']
    stepper_motor_header_b[2] += tmc5160b_pins['m1a']
    stepper_motor_header_b[3] += tmc5160b_pins['m1b']
    stepper_motor_header_b[4] += tmc5160b_pins['m2b']

    # Third TMC5160 Instance
    esp32_3v3 += tmc5160c_pins['3v3']
    esp32_gnd2 += tmc5160c_pins['gnd2']
    V_plus_36V += tmc5160c_pins['vmot']
    GND_36V += tmc5160c_pins['gnd1']
    vcc_gnd += tmc5160c_pins['gnd2']
    esp32_gpio32 += tmc5160c_pins['dir']
    esp32_gpio2 += tmc5160c_pins['step']
    esp32_gpio22 += tmc5160c_pins['en']
    esp32_gpio23 += tmc5160c_pins['sdi']
    esp32_gpio19 += tmc5160c_pins['sdo']
    esp32_gpio18 += tmc5160c_pins['sck']
    esp32_gpio33 += tmc5160c_pins['csn']
    stepper_motor_header_c[1] += tmc5160c_pins['m2a']
    stepper_motor_header_c[2] += tmc5160c_pins['m1a']
    stepper_motor_header_c[3] += tmc5160c_pins['m1b']
    stepper_motor_header_c[4] += tmc5160c_pins['m2b']

    ###
    ### pcbflow PCB Declarations
    ###

    # Create a pcbflow Board instance
    brd = Board((100, 100))  # Adjust the board size as per your requirement

    brd.add_inner_copper_layer(2)

    # Place 2 mm mounting holes in the corners
    holes = ((5, 5), (5, 25), (50, 5), (50, 25))
    for hole in holes:
        brd.add_hole(hole, 2.0)

    # Assign a convenient reference to the default SKiDL circuit
    ckt = default_circuit

    print("Circuit:  Parts: %d  Nets: %d" % (len(ckt.parts), len(ckt.nets)))

    # Assign part locations
    esp32_header1.loc = (35, 15)
    esp32_header2.loc = (45, 15)
    tmc5160_header1.loc = (25, 15)
    tmc5160_header2.loc = (55, 15)
    tmc5160_header1b.loc = (20, 10)
    tmc5160_header2b.loc = (60, 10)
    tmc5160_header1c.loc = (15, 5)
    tmc5160_header2c.loc = (65, 5)
    stepper_motor_header.loc = (30, 5)
    stepper_motor_header_b.loc = (50, 5)
    stepper_motor_header_c.loc = (70, 5)
    can_bus_header.loc = (65, 5)
    capacitor.loc = (25, 5)
    capacitor_b.loc = (45, 5)
    capacitor_c.loc = (65, 5)
    terminal_block.loc = (70, 5)
    terminal_block_2.loc = (75, 5)

    # Instantiate SkiPart(PCBPart) instances
    sides = ["top"] * 17  # Assuming all parts are on the top side

    for part, side in zip(ckt.parts, sides):
        sp = SkiPart(brd.DC(part.loc), part, side=side)
        # "fanout" GND and VDD vias from parts with GND and VDD net connections
        sp.fanout(["VDD"])
        sp.fanout(["GND"], relative_to="inside")

    # Finish the PCB with an outline and poured copper layers
    brd.add_outline()
    brd.fill_layer("GTL", "GND")
    brd.fill_layer("GBL", "GND")

    # Save the rendered PCB to asset files
    brd.save("%s" % (os.path.basename(__file__)[:-3]))

    # Generate the netlist (if required)
    generate_netlist()  # If you need to generate a netlist, add this line

    print("PCB design completed.")
ghost commented 7 months ago

Here's the result if I change FP_LIB_PATH to 'usr/share/kicad/footprints' in kicad.py

(base) twinlizzie@twinlizzie-MS-7C51:~/Ngnuity-Repos/sylvie-2024/schematics/skidl_code$ python esp32_motor-pcb-x3_final.py
Circuit:  Parts: 17  Nets: 36
Traceback (most recent call last):
  File "/home/twinlizzie/Ngnuity-Repos/sylvie-2024/schematics/skidl_code/esp32_motor-pcb-x3_final.py", line 270, in <module>
    sp = SkiPart(brd.DC(part.loc), part, side=side)
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 288, in __init__
    super().__init__(
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 42, in __init__
    self.parse()
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 273, in parse
    self._parse_fp_text(v["fp_text"])
  File "/home/twinlizzie/miniconda3/lib/python3.10/site-packages/pcbflow/kicad.py", line 148, in _parse_fp_text
    layer = self._map_layers(e["layer"])[0]
IndexError: list index out of range
ghost commented 7 months ago

Bug fixed by adding this layer to the list in kicad.py:

KI_LAYER_DICT = { "F.SilkS": "GTO", "F.Paste": "GTP", "F.Mask": "GTS", "F.Cu": "GTL", "F.Fab": "GTD", "F.CrtYd": "GTO", # Assuming F.CrtYd should map to the same layer as F.SilkS }

Credits to ChatGPT.

It also seems like I have to use Kicad 5 footprints, though it could have just been an isolated bug with a special component (in this case, a capacitor) that was causing issues.

michaelgale commented 7 months ago

@khanumballz glad you found a fix! I would not be surprised if pcbflow has issues with newer versions of KiCAD libraries since the data format and mapping of layers can change. I am surprised ChatGPT was able to offer assistance--I guess pcbflow is somehow part of its massive training dataset!