Files
SN-L00/hardware/kicad/scripts/place_components.py
T
florian.berthold a8e63aa45c Fix PCB file format and add placement script
- Fixed KiCad 8 PCB file format (text rotation syntax)
- PCB now loads correctly with board outline and labels
- Added Python placement script for KiCad scripting console
- Updated PCB_LAYOUT.md with quick start instructions

Next step: Open in KiCad, run Update PCB from Schematic,
then use Freerouting or manual routing to complete traces.
2026-01-23 05:36:14 +01:00

125 lines
3.6 KiB
Python

#!/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.")