#!/usr/bin/env python3 """ SN-L00 Full Autoroute Pipeline for 8HP Eurorack Run in KiCad PCB Editor: Tools → Scripting Console Then: exec(open('scripts/autoroute_full.py').read()) This script: 1. Updates board outline to 8HP (40mm x 100mm) 2. Deletes existing tracks 3. Places all components 4. Exports DSN file 5. Runs Freerouting 6. Imports routed SES file """ import pcbnew import subprocess import os import time board = pcbnew.GetBoard() board_path = board.GetFileName() board_dir = os.path.dirname(board_path) def mm(val): return pcbnew.FromMM(val) def place(x, y): return pcbnew.VECTOR2I(mm(x), mm(y)) print("=" * 50) print("SN-L00 Full Autoroute Pipeline") print("=" * 50) # Step 1: Update board outline to 8HP (40mm x 100mm) print("\n[1/6] Updating board outline to 40mm x 100mm...") for drawing in board.GetDrawings(): if drawing.GetClass() == "PCB_SHAPE": if drawing.GetShape() == pcbnew.SHAPE_T_RECT: drawing.SetStart(pcbnew.VECTOR2I(mm(0), mm(0))) drawing.SetEnd(pcbnew.VECTOR2I(mm(40), mm(100))) print(" Board outline updated") break # Step 2: Delete all tracks print("\n[2/6] Deleting existing tracks...") tracks = list(board.GetTracks()) for track in tracks: board.Delete(track) print(f" Deleted {len(tracks)} tracks") # Step 3: Place components (v2 - fixed spacing) print("\n[3/6] Placing components...") placements = { # Top - OLED display (rotated 90°) "MOD3": (20, 15, 90), # Audio jacks - centered with good spacing "J3": (10, 35, 0), # RETURN_IN "J4": (30, 35, 0), # TRIG_OUT # Button + LED "SW1": (20, 48, 0), "D5": (32, 48, 0), # LED near button # RP2040-Zero module - centered "MOD2": (20, 62, 0), # Signal conditioning ICs - well spaced "U2": (8, 78, 0), # 74LVC1G17 "U4": (32, 78, 0), # MCP6001 # Decoupling caps near ICs "C4": (4, 78, 90), "C5": (14, 78, 90), "C6": (28, 82, 90), # Protection diodes near jacks circuits "D3": (4, 82, 0), "D4": (36, 78, 0), # Resistors - spread across middle "R2": (4, 86, 90), "R3": (10, 86, 90), "R4": (16, 86, 90), "R5": (24, 86, 90), "R6": (30, 86, 90), "R7": (36, 86, 90), # Power section - J2 moved up, components spread out "J2": (20, 88, 0), # Eurorack power header (extends ~6mm down) "D2": (4, 92, 0), # Protection diode "U3": (32, 94, 180), # LDO - moved left from edge "C2": (10, 94, 90), # Input cap "C3": (26, 94, 90), # Output cap } placed = 0 for ref, (x, y, rot) in placements.items(): fp = board.FindFootprintByReference(ref) if fp: fp.SetPosition(place(x, y)) fp.SetOrientationDegrees(rot) placed += 1 print(f" Placed {placed} components") # Step 4: Save and export DSN print("\n[4/6] Saving board and exporting DSN...") pcbnew.Refresh() board.Save(board_path) dsn_path = os.path.join(board_dir, "SN-L00.dsn") ses_path = os.path.join(board_dir, "SN-L00.ses") # Export DSN if hasattr(pcbnew, 'ExportSpecctraDSN'): pcbnew.ExportSpecctraDSN(dsn_path) print(f" Exported: {dsn_path}") else: # Fallback for older KiCad versions exporter = pcbnew.SPECCTRA_DB() exporter.ExportPCB(dsn_path, False) print(f" Exported: {dsn_path}") # Step 5: Run Freerouting print("\n[5/6] Running Freerouting...") freerouting_jar = "/tmp/freerouting.jar" if not os.path.exists(freerouting_jar): print(" Downloading Freerouting...") url = "https://github.com/freerouting/freerouting/releases/download/v2.0.1/freerouting-2.0.1.jar" subprocess.run(["curl", "-L", "-o", freerouting_jar, url], check=True) result = subprocess.run( ["java", "-jar", freerouting_jar, "-de", dsn_path, "-do", ses_path, "-mp", "200", "-mt", "1", "-oit"], capture_output=True, text=True, cwd=board_dir ) if result.returncode == 0: print(" Freerouting completed successfully") else: print(f" Freerouting error: {result.stderr}") # Step 6: Import SES print("\n[6/6] Importing routed session...") if os.path.exists(ses_path): if hasattr(pcbnew, 'ImportSpecctraSES'): pcbnew.ImportSpecctraSES(ses_path) print(f" Imported: {ses_path}") else: # Fallback importer = pcbnew.SPECCTRA_DB() importer.ImportSES(ses_path) print(f" Imported: {ses_path}") pcbnew.Refresh() print("\n" + "=" * 50) print("DONE! Save (Ctrl+S) and run DRC to verify.") print("=" * 50) else: print(f" ERROR: SES file not found: {ses_path}")