#!/usr/bin/env python3 """ESP32-S3 Sensor Node — PCB Generator v3. Tabs, like pcbnew.""" import os T = '\t' PCB = os.path.expanduser("~/Documents/KiCad/10.0/esp32-s3-sensor-node.kicad_pcb") NETS = ["GND","3V3","VBUS","EN","IO0","IO12","SDA","SCL","USB_D_P","USB_D_N","CC1","CC2","IO2_LED","D1_A","RXD0","TXD0"] W,H = 65.0,40.0 def ni(n): return NETS.index(n) if n in NETS else -1 def smd(n,x,y,w,h,net,rot=0): return f'{T*4}(pad "{n}" smd rect (at {x:.3f} {y:.3f} {rot}) (size {w:.3f} {h:.3f}) (layers "F.Cu" "F.Paste" "F.Mask")' + (f' (net {ni(net)})' if ni(net)>=0 else '') + ')' def fp(ref,lib,x,y,rot,pads): p = [f'{T*3}(footprint "{lib}"', f'{T*4}(layer "F.Cu")', f'{T*4}(at {x:.3f} {y:.3f} {rot})', f'{T*4}(descr "{ref}")', f'{T*4}(property "Reference" "{ref}" (at {x:.3f} {y-3:.3f}) (layer "F.SilkS") (effects (font (size 1 1) (thickness 0.15))))', f'{T*4}(property "Value" "{ref}" (at {x:.3f} {y+3:.3f}) (layer "F.Fab") (effects (font (size 1 1) (thickness 0.15))))'] p += [f'{T*4}{x}' for x in pads] p.append(f'{T*3})') return '\n'.join(p) FR = '"Resistor_SMD:R_0603_1608Metric"' FC = '"Capacitor_SMD:C_0603_1608Metric"' FC8 = '"Capacitor_SMD:C_0805_2012Metric"' FL = '"LED_SMD:LED_0603_1608Metric"' FP4 = '"Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical"' FU = '"Connector_USB:USB_C_Receptacle_USB2.0_16P"' FE = '"RF_Module:ESP32-S3-WROOM-1"' FS = '"Package_TO_SOT_SMD:SOT-223-3Lead_TabPin2"' passives = [ ("R1",FR,55,30,0,"IO2_LED","D1_A"), ("R2",FR,27,32,0,"EN","3V3"), ("R3",FR,16,7,0,"IO0","3V3"), ("R4",FR,16,12,0,"IO12","GND"), ("R5",FR,48,24,0,"SDA","3V3"), ("R6",FR,48,22,0,"SCL","3V3"), ("R7",FR,11,28,0,"CC1","GND"), ("R8",FR,11,25,0,"CC2","GND"), ("C1",FC,30,28,0,"3V3","GND"), ("C2",FC,30,25,0,"3V3","GND"), ("C3",FC,30,22,0,"3V3","GND"), ("C4",FC,26,35,0,"EN","GND"), ("C5",FC8,24,17,0,"VBUS","GND"), ("C6",FC8,24,23,0,"3V3","GND"), ] lines = [] lines.append('(kicad_pcb') lines.append(f'{T}(version 20260206)') lines.append(f'{T}(generator "pcbnew")') lines.append(f'{T}(generator_version "10.0")') lines.append(f'{T}(general') lines.append(f'{T*2}(thickness 1.6)') lines.append(f'{T*2}(legacy_teardrops no)') lines.append(f'{T})') lines.append(f'{T}(paper "A4")') lines.append(f'{T}(layers') for n,c,t in [(0,"F.Cu","signal"),(2,"B.Cu","signal"),(13,"F.Paste","user"),(15,"B.Paste","user"), (5,"F.SilkS","user"),(7,"B.SilkS","user"), (1,"F.Mask","user"),(3,"B.Mask","user"), (25,"Edge.Cuts","user"),(31,"F.CrtYd","user"),(29,"B.CrtYd","user"), (35,"F.Fab","user"),(33,"B.Fab","user")]: lines.append(f'{T*2}({n} "{c}" {t})') lines.append(f'{T})') lines.append(f'{T}(setup') lines.append(f'{T*2}(pad_to_mask_clearance 0)') lines.append(f'{T*2}(allow_soldermask_bridges_in_footprints no)') lines.append(f'{T*2}(copper_edge_clearance 0.3)') lines.append(f'{T})') # Nets lines.append(f'{T}(nets') for i,n in enumerate(NETS): lines.append(f'{T*2}(net {i} "{n}")') lines.append(f'{T})') # Net classes lines.append(f'{T}(net_class "Default" (clearance 0.15) (trace_width 0.2) (via_dia 0.6) (via_drill 0.3) (add_net "*"))') lines.append(f'{T}(net_class "POWER" (clearance 0.25) (trace_width 0.5) (via_dia 0.8) (via_drill 0.4) (add_net "3V3") (add_net "VBUS"))') # Board outline lines.append(f'{T}(polygon (layer "Edge.Cuts")') lines.append(f'{T*2}(pts') lines.append(f'{T*3}(xy 0 0)') lines.append(f'{T*3}(xy {W} 0)') lines.append(f'{T*3}(xy {W} {H})') lines.append(f'{T*3}(xy 0 {H})') lines.append(f'{T*2})') lines.append(f'{T})') # Footprints # J1 USB-C lines.append(fp("J1",FU,7,20,0,[ smd("SH",-3,0,2,2,"GND"), smd("A1",-2,5,1.5,1.5,"GND"), smd("A4",-2,-5,1.5,1.5,"VBUS"), smd("A5",-2,-3,1.5,0.8,"CC1"), smd("A6",-2,-1,1.5,0.8,"USB_D_P"), smd("A7",-2,1,1.5,0.8,"USB_D_N"), smd("B5",-2,3,1.5,0.8,"CC2"), smd("B6",-2,0,1.5,0.8,"USB_D_P"), smd("B7",-2,2,1.5,0.8,"USB_D_N"), ])) # U2 AMS1117 lines.append(fp("U2",FS,20,20,0,[ smd("1",-2.3,-3,1.5,1,"GND"), smd("2",0,3,2.5,2.5,"3V3"), smd("3",0,-3,2.5,2.5,"VBUS"), ])) # U1 ESP32-S3 lines.append(fp("U1",FE,38,20,0,[ smd("1",-8,8,1.5,1.5,"GND"), smd("2",-8,-8,1.5,1.5,"3V3"), smd("3",-8,-6,1,1,"EN"), smd("4",-8,-4,1,1,"SDA"), smd("5",-8,-2,1,1,"SCL"), smd("13",8,-4,1,1,"USB_D_N"), smd("14",8,-2,1,1,"USB_D_P"), smd("20",-8,4,1,1,"IO12"), smd("27",-8,0,1,1,"IO0"), smd("36",8,-8,1,1,"RXD0"), smd("37",8,-6,1,1,"TXD0"), smd("38",-8,2,1,1,"IO2_LED"), ])) # J2 I2C lines.append(fp("J2",FP4,56,20,0,[ smd("1",0,3.81,1.5,1.5,"3V3"), smd("2",0,1.27,1.5,1.5,"SDA"), smd("3",0,-1.27,1.5,1.5,"SCL"), smd("4",0,-3.81,1.5,1.5,"GND"), ])) # J3 PROG lines.append(fp("J3",FP4,25,3,0,[ smd("1",0,-3.81,1.5,1.5,"GND"), smd("2",0,-1.27,1.5,1.5,"RXD0"), smd("3",0,1.27,1.5,1.5,"TXD0"), smd("4",0,3.81,1.5,1.5,"3V3"), ])) # D1 LED lines.append(fp("D1",FL,55,34,0,[ smd("1",-0.8,0,0.8,0.8,"GND"), smd("2",0.8,0,0.8,0.8,"D1_A"), ])) # Passives for ref,lib,x,y,rot,n1,n2 in passives: lines.append(fp(ref,lib,x,y,rot,[ smd("1",-0.8,0.9,0.8,0.8,n1), smd("2",-0.8,-0.9,0.8,0.8,n2), ])) # Zones for layer in ["F.Cu","B.Cu"]: lines.append(f'{T}(zone (net {ni("GND")}) (layer "{layer}")') lines.append(f'{T*2}(polygon (pts (xy 0 0)(xy {W} 0)(xy {W} {H})(xy 0 {H})))') lines.append(f'{T*2}(fill (mode solid))') lines.append(f'{T})') # Antenna keepout zones for layer in ["F.Cu","B.Cu"]: lines.append(f'{T}(zone (net 0) (layer "{layer}") (priority 1)') lines.append(f'{T*2}(polygon (pts (xy 46 28)(xy {W} 28)(xy {W} {H})(xy 46 {H})))') lines.append(f'{T*2}(fill (mode solid))') lines.append(f'{T})') lines.append(')') result = '\n'.join(lines) + '\n' open(PCB,'w').write(result) print(f"Written {len(result)} bytes")