AnasSarkiz/muse-voice-remote

Muse Voice Remote — bare ESP32-S3R8, 155 TOP JLCPCB parts with real supplier CAD, native 3D assembly and GLB exports. Revised 17 mm case allocation; 0.30 mm via drill /0.45 mm pad. Checked prototype PCB; mechanical, RF and MUSE qualification pending.

Version
1.3.2
License
unset
Stars
0

scripts/seed-usb-pair.py

#!/usr/bin/env python3
"""Route the USB main path as coupled top-layer pairs with symmetric fanouts.

The USB-C duplicate D- pads use a short backside bridge. This bridge is a
receptacle stub, separate from the via-free main pair. Keep the imported lands.
"""
import pathlib,json,math,os
helper=pathlib.Path(__file__).with_name('finish-unrouted.py');api={'__file__':str(helper)}
exec(helper.read_text().split('\n# A FreeRouting pattern')[0],api)
pcbnew=api['pcbnew'];path=api['path'];before=path.read_bytes();old=api['check']();board=pcbnew.LoadBoard(str(path))
from shapely.geometry import LineString
footprints=list(board.GetFootprints());refs={f.GetReference():f for f in footprints};netcodes={n.GetNetname():n.GetNetCode() for n in board.GetNetsByNetcode().values()}
def pad(ref,pin):return next(p for p in refs[ref].Pads() if p.GetNumber()==pin)
def pos(ref,pin):return api['xy'](pad(ref,pin).GetPosition())
def track(net,points,layer=pcbnew.F_Cu,width=.15):
 for a,b in zip(points,points[1:]):
  if math.dist(a,b)<1e-6:continue
  t=pcbnew.PCB_TRACK(board);t.SetStart(api['vector'](a));t.SetEnd(api['vector'](b));t.SetLayer(layer);t.SetWidth(pcbnew.FromMM(width));t.SetNetCode(netcodes[net]);t.SetLocked(True);board.Add(t)
def via(net,at):
 v=pcbnew.PCB_VIA(board);v.SetPosition(api['vector'](at));v.SetDrill(pcbnew.FromMM(.3));v.SetViaType(pcbnew.VIATYPE_THROUGH);v.SetLayerPair(pcbnew.F_Cu,pcbnew.B_Cu);v.SetNetCode(netcodes[net]);v.SetLocked(True)
 for l in api['layers']:v.SetWidth(l,pcbnew.FromMM(.45))
 board.Add(v)
metrics=[]
def pair(name,dm_net,dp_net,dm_start,dp_start,dm_end,dp_end,centre,src_extra=None):
 line=LineString(centre);offsets=[]
 for distance in [-.15,.15]:
  curve=line.offset_curve(distance,join_style=2)
  if curve.geom_type!='LineString':raise RuntimeError('Invalid offset pair')
  points=list(curve.coords)
  if math.dist(points[-1],centre[0])<math.dist(points[0],centre[0]):points.reverse()
  offsets.append(points)
 offsets.sort(key=lambda points:math.dist(points[0],dm_start))
 paths=[]
 for net,start,end,offset,i in [(dm_net,dm_start,dm_end,offsets[0],0),(dp_net,dp_start,dp_end,offsets[1],1)]:
  points=[start]+([src_extra[i]] if src_extra else [])+offset+[end];track(net,points);paths.append(points)
 lengths=[sum(math.dist(a,b) for a,b in zip(p,p[1:])) for p in paths]
 metrics.append(dict(branch=name,layer='F.Cu',width_mm=.15,edge_gap_mm=.15,main_path_vias=0,dm_length_mm=lengths[0],dp_length_mm=lengths[1],length_mismatch_mm=abs(lengths[0]-lengths[1])))
pair('MCU to series resistors','USB_DM_MCU','USB_DP_MCU',pos('U1','25'),pos('U1','26'),pos('R_USB_DM','2'),pos('R_USB_DP','2'),[(1.6,2.0),(1.6,2.3),(4.2,2.3),(4.2,4.2),(6,4.2),(6,4.9)])
pair('ESD to series resistors','USB_DM','USB_DP',pos('D_USB_ESD','6'),pos('D_USB_ESD','4'),pos('R_USB_DM','1'),pos('R_USB_DP','1'),[(-2,9.8),(-2,9.3),(3,7.45),(6,7.45),(6,7.2)],[(-2.95,10.7),(-1.05,10.7)])
pair('USB-C to ESD','USB_DM','USB_DP',pos('J_USB','B7'),pos('J_USB','A6'),pos('D_USB_ESD','1'),pos('D_USB_ESD','3'),[(-.5,16.4),(-.5,16.1),(-2,15.1),(-2,14.8)])
# Connect both ends of each flow-through ESD signal in native PCB connectivity.
track('USB_DM',[pos('D_USB_ESD','1'),pos('D_USB_ESD','6')]);track('USB_DP',[pos('D_USB_ESD','3'),pos('D_USB_ESD','4')])
track('USB_DP',[pos('J_USB','A6'),(-.25,16.1),(.75,16.1),pos('J_USB','B6')])
for pin,x in [('A7',.25),('B7',-.75)]:track('USB_DM',[pos('J_USB',pin),(x,19.2)]);via('USB_DM',(x,19.2))
track('USB_DM',[(.25,19.2),(-.75,19.2)],pcbnew.B_Cu)
track('GND',[pos('D_USB_ESD','2'),(-2,12.9)],width=.25);via('GND',(-2,12.9))
pcbnew.SaveBoard(str(path),board);after=api['check']();errors=[v for v in after['violations'] if v['type'] not in ['track_dangling','via_dangling']]
report=dict(status='accepted' if not errors and len(after['unconnected_items'])<len(old['unconnected_items']) else 'rejected',stackup='JLC04101H-3313',target_differential_ohms=90,impedance_status='geometry target; fabricator controlled-impedance verification required',branches=metrics,duplicate_dm_bridge_vias=2,native_errors=errors)
(api['root']/'docs/usb-pair-routing.json').write_text(json.dumps(report,indent=2)+'\n')
if report['status']=='rejected':
 path.write_bytes(before)
 print('USB candidate rejected; restored native PCB. Errors:',[(v['type'],v['description']) for v in errors],flush=True)
 os._exit(1)
print('Coupled USB top-layer paths accepted; mismatches (mm):',[round(m['length_mismatch_mm'],4) for m in metrics],flush=True)
os._exit(0)