#!/usr/bin/env python3 """ KiCad PCB Component Placement Script for SN-L00 Run this script in KiCad's Scripting Console (Tools > Scripting Console) after running "Update PCB from Schematic" to place all components. Usage in KiCad scripting console: exec(open('/path/to/place_components.py').read()) """ import pcbnew # Component placement coordinates (mm) based on CPL_PCBWay.csv # Format: {Reference: (x, y, rotation, layer)} # Layer: "F.Cu" for top, "B.Cu" for bottom PLACEMENTS = { # Capacitors "C1": (10.0, 60.0, 0, "F.Cu"), "C2": (18.0, 60.0, 0, "F.Cu"), "C3": (21.0, 60.0, 0, "F.Cu"), "C4": (7.0, 68.0, 0, "F.Cu"), "C5": (21.0, 68.0, 0, "F.Cu"), # Resistors "R1": (10.0, 35.0, 0, "F.Cu"), "R2": (24.0, 36.0, 90, "F.Cu"), "R3": (7.0, 76.0, 90, "F.Cu"), "R4": (21.0, 76.0, 90, "F.Cu"), "R5": (18.0, 76.0, 90, "F.Cu"), "R6": (18.0, 72.0, 90, "F.Cu"), # Diodes "D1": (7.0, 65.0, 0, "B.Cu"), # Bottom layer "D2": (24.0, 31.5, 90, "F.Cu"), "D3": (24.0, 70.0, 90, "F.Cu"), "D4": (24.0, 74.0, 90, "F.Cu"), # ICs "U1": (14.0, 65.0, 0, "B.Cu"), # LDO on bottom "U2": (7.0, 72.0, 0, "F.Cu"), # Schmitt trigger "U3": (21.0, 72.0, 0, "F.Cu"), # Op-amp # Connectors "J1": (14.0, 92.0, 0, "F.Cu"), # Power header "J2": (7.0, 83.0, 0, "F.Cu"), # TRIG jack "J3": (21.0, 83.0, 0, "F.Cu"), # RETURN jack # Switch "SW1": (14.0, 31.5, 0, "F.Cu"), # Modules (hand-solder, placement reference only) "MOD1": (14.0, 46.5, 0, "F.Cu"), # RP2040-Zero "MOD2": (14.0, 17.5, 0, "F.Cu"), # OLED } def place_components(): """Place all components according to the PLACEMENTS dictionary.""" board = pcbnew.GetBoard() if not board: print("Error: No board loaded!") return placed = 0 not_found = [] for ref, (x, y, rotation, layer) in PLACEMENTS.items(): # Find the footprint by reference fp = board.FindFootprintByReference(ref) if fp is None: not_found.append(ref) continue # Convert mm to internal units (nanometers) pos = pcbnew.VECTOR2I(pcbnew.FromMM(x), pcbnew.FromMM(y)) fp.SetPosition(pos) # Set rotation (in tenths of degrees) fp.SetOrientationDegrees(rotation) # Set layer (flip if on bottom) if layer == "B.Cu" and fp.GetLayer() == pcbnew.F_Cu: fp.Flip(pos, False) elif layer == "F.Cu" and fp.GetLayer() == pcbnew.B_Cu: fp.Flip(pos, False) placed += 1 print(f"Placed {ref} at ({x}, {y}) rot={rotation}° on {layer}") if not_found: print(f"\nNot found (run 'Update PCB from Schematic' first): {not_found}") print(f"\nPlaced {placed}/{len(PLACEMENTS)} components") # Refresh the board view pcbnew.Refresh() def add_mounting_holes(): """Add M3 mounting holes at standard Eurorack positions.""" board = pcbnew.GetBoard() # Check if mounting holes already exist for fp in board.GetFootprints(): if "MountingHole" in fp.GetValue(): print("Mounting holes already exist, skipping...") return # Would need to load footprint from library - simplified for this script print("Add mounting holes manually:") print(" MH1: (14, 5) - Top") print(" MH2: (14, 95) - Bottom") print(" Footprint: MountingHole:MountingHole_3.2mm_M3") if __name__ == "__main__": print("SN-L00 Component Placement Script") print("=" * 40) place_components() add_mounting_holes() print("\nDone! Run DRC and then route traces.")