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/swap-identical-import.py
#!/usr/bin/env python3
"""Use a selected supplier import only when its placed copper matches exactly.
Useful for same-package value changes. Different copper requires rerouting;
this helper rejects that case rather than rewriting an electronic land pattern.
"""
import argparse,json,pathlib,os
import pcbnew
p=argparse.ArgumentParser();p.add_argument('board');p.add_argument('reference');a=p.parse_args()
root=pathlib.Path(__file__).resolve().parents[1]
catalog=json.loads((root/'docs/component-catalog.json').read_text());entry=next(p for p in catalog if p['reference']==a.reference)
board=pcbnew.LoadBoard(a.board);footprints=list(board.GetFootprints());old=next(f for f in footprints if f.GetReference()==a.reference);old_pads=list(old.Pads())
nets={p.GetNumber():p.GetNetCode() for p in old_pads}
fresh=pcbnew.FootprintLoad(str(root/'vendor/jlcpcb/library'),entry['lcsc'])
fresh.SetReference(a.reference);fresh.SetValue(entry['mpn']);fresh.SetPosition(old.GetPosition());fresh.SetOrientationDegrees(old.GetOrientationDegrees());fresh.SetAttributes(pcbnew.FP_SMD)
fresh.Reference().SetVisible(False);fresh.Value().SetVisible(False)
board.Add(fresh) # Attach to the board before assigning numeric net codes.
fresh_pads=list(fresh.Pads())
for pad in fresh_pads:pad.SetNetCode(nets.get(pad.GetNumber(),0))
def geometry(pads):
result={}
for pad in pads:
effective=pad.GetEffectivePolygon(pcbnew.F_Cu);outlines=[]
for i in range(effective.OutlineCount()):
c=effective.COutline(i);outlines.append([(c.CPoint(j).x,c.CPoint(j).y) for j in range(c.PointCount())])
result[pad.GetNumber()]={'net':pad.GetNetCode(),'polygons':outlines,'drill':[pad.GetDrillSize().x,pad.GetDrillSize().y],'layers':pad.GetLayerSet().FmtBin()}
return result
if geometry(old_pads)!=geometry(fresh_pads):raise RuntimeError('Supplier copper/position/drill/layers differ; controlled rerouting required')
for g in list(fresh.GraphicalItems()):
if g.GetLayer()==pcbnew.F_SilkS:g.SetLayer(pcbnew.F_Fab)
board.Remove(old);pcbnew.SaveBoard(a.board,board)
print(f'Imported {a.reference}: {entry["lcsc"]} {entry["mpn"]}; copper/drills/position/layers unchanged',flush=True);os._exit(0)