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/add-amplifier-control.py

#!/usr/bin/env python3
"""Insert the already-selected supplier resistor and isolate SYS-referenced SD_MODE."""
import json,pathlib,sys,os
sys.path.insert(0,'/workspace/.cache/native-python')
import pcbnew
from shapely.geometry import Polygon,LineString,Point
from shapely.ops import unary_union
root=pathlib.Path(__file__).resolve().parents[1];path=root/'dist/MuseVoiceRemote-library.kicad_pcb'
board=pcbnew.LoadBoard(str(path));fs=list(board.GetFootprints());byref={f.GetReference():f for f in fs};pads={ref:list(f.Pads()) for ref,f in byref.items()}
if 'R_AMP_CONTROL' in byref:raise RuntimeError('Control resistor is already present')
catalog={p['reference']:p for p in json.loads((root/'docs/component-catalog.json').read_text())};entry=catalog['R_AMP_CONTROL']
net_objects=list(board.GetNetsByNetcode().values());codes={n.GetNetname():n.GetNetCode() for n in net_objects}
host=pcbnew.NETINFO_ITEM(board,'AMP_ENABLE_HOST');board.Add(host);codes['AMP_ENABLE_HOST']=host.GetNetCode()
sd=next(p for p in pads['U_AMP'] if p.GetNumber()=='4');effective=sd.GetEffectivePolygon(pcbnew.F_Cu);outlines=[]
for i in range(effective.OutlineCount()):
 c=effective.COutline(i);outlines.append(Polygon([(pcbnew.ToMM(c.CPoint(j).x),pcbnew.ToMM(c.CPoint(j).y)) for j in range(c.PointCount())]))
land=unary_union(outlines);tracks=list(board.GetTracks());retag=[];remove=[]
for t in tracks:
 if t.GetNetname()!='AMP_ENABLE':continue
 if isinstance(t,pcbnew.PCB_VIA):
  at=t.GetPosition();shape=Point(pcbnew.ToMM(at.x),pcbnew.ToMM(at.y));radius=pcbnew.ToMM(t.GetWidth(pcbnew.F_Cu))/2
 elif t.GetLayer()==pcbnew.F_Cu:
  a,b=t.GetStart(),t.GetEnd();shape=LineString([(pcbnew.ToMM(a.x),pcbnew.ToMM(a.y)),(pcbnew.ToMM(b.x),pcbnew.ToMM(b.y))]);radius=pcbnew.ToMM(t.GetWidth())/2
 else:shape=None;radius=0
 (remove if shape is not None and shape.buffer(radius+.15).intersects(land) else retag).append(t)
for ref in ['U1','R_AMP_PD']:
 for p in pads[ref]:
  if catalog[ref]['padNets'].get(p.GetNumber())=='AMP_ENABLE_HOST':p.SetNetCode(codes['AMP_ENABLE_HOST'])
fresh=pcbnew.FootprintLoad(str(root/'vendor/jlcpcb/library'),entry['lcsc']);fresh.SetReference(entry['reference']);fresh.SetValue(entry['mpn']);fresh.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(entry['x']),pcbnew.FromMM(-entry['y'])));fresh.SetOrientationDegrees(entry['rotation']);fresh.SetAttributes(pcbnew.FP_SMD)
fresh.Reference().SetVisible(False);fresh.Value().SetVisible(False);board.Add(fresh)
for p in list(fresh.Pads()):p.SetNetCode(codes[entry['padNets'][p.GetNumber()]])
for g in list(fresh.GraphicalItems()):
 if g.GetLayer()==pcbnew.F_SilkS:g.SetLayer(pcbnew.F_Fab)
for t in retag:t.SetNetCode(codes['AMP_ENABLE_HOST'])
for t in remove:board.Remove(t)
pcbnew.SaveBoard(str(path),board)
anchor=root/'docs/jlcpcb-native-anchor-offsets.json';data=json.loads(anchor.read_text());data['R_AMP_CONTROL']={'dx':0,'dy':0};anchor.write_text(json.dumps(data,indent=2)+'\n')
print(f'Added {entry["lcsc"]} {entry["mpn"]}; preserved {len(retag)} host tracks/vias; cleared {len(remove)} terminal items for isolation',flush=True);os._exit(0)