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/replace-imported-footprints.py

#!/usr/bin/env python3
"""Transplant selected supplier footprints from a current unrouted seed.

This preserves other footprints and existing copper. It does not approve that
copper: refill zones, repair affected connections and run independent DRC.
"""
import argparse
import json
from pathlib import Path
import pcbnew

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base", required=True)
parser.add_argument("--seed", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("references", nargs="+")
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
base = pcbnew.LoadBoard(args.base)
seed = pcbnew.LoadBoard(args.seed)
old = {f.GetReference(): f for f in base.GetFootprints()}
fresh = {f.GetReference(): f for f in seed.GetFootprints()}
catalog = {p["reference"]: p for p in json.loads((root/"docs/component-catalog.json").read_text())}
nets = {n.GetNetname(): n for n in base.GetNetInfo().NetsByNetcode().values()}
for ref in args.references:
    source = fresh[ref]
    entry = catalog[ref]
    lcsc = entry["lcsc"]
    if source.GetValue() != entry["mpn"]:
        raise RuntimeError(f"Stale seed footprint: {ref}")
    footprint = pcbnew.FootprintLoad(str(root/"vendor/jlcpcb/library"), lcsc)
    footprint.SetReference(ref)
    footprint.SetValue(source.GetValue())
    footprint.SetPosition(source.GetPosition())
    footprint.SetOrientationDegrees(source.GetOrientationDegrees())
    footprint.Reference().SetVisible(False)
    footprint.Value().SetVisible(False)
    assigned = {p.GetNumber(): p.GetNetname() for p in source.Pads()}
    for pad in footprint.Pads():
        name = assigned.get(pad.GetNumber())
        if name:
            pad.SetNet(nets[name])
    for graphic in footprint.GraphicalItems():
        if graphic.GetLayer()==pcbnew.F_SilkS:
            graphic.SetLayer(pcbnew.F_Fab)
    base.Remove(old[ref])
    base.Add(footprint)
    print(f"Replaced {ref} with supplier {lcsc} {entry['mpn']}")
for footprint in base.GetFootprints():
    if footprint.GetReference() in catalog:
        footprint.SetAttributes(pcbnew.FP_SMD)
pcbnew.SaveBoard(args.output, base)