main
John Lauer MCP2562FD-E/SN component page: batch 2 2069f2d 2d ago
#!/usr/bin/env python3
"""Write models.json, lod/manifest.json, provenance/3d-model.json and verification.json for the page from the files
on disk (hashes, sizes, triangle counts). Run from the page root. usage: page_meta.py"""
import json, hashlib, os, subprocess, sys
from pygltflib import GLTF2
D = "2026-10-21"; S = "mcp2562fd-e-sn"; MPN = "MCP2562FD-E/SN"
def sha(p): return hashlib.sha256(open(p, "rb").read()).hexdigest()
def blob(p): return subprocess.check_output(["git", "hash-object", p]).decode().strip()
def glbstats(p):
    g = GLTF2().load(p)
    tris = sum(g.accessors[pr.indices].count // 3 for m in g.meshes for pr in m.primitives)
    return dict(triangles=tris, primitives=sum(len(m.primitives) for m in g.meshes),
                nodes=[n.name for n in g.nodes if n.mesh is not None])
mark = {"text": "MCP2562 / FD-E/SN (two lines)", "surface": "top, along the pin rows, upright with pin 1 lower left",
        "colour": "light grey on black epoxy", "virtual": True,
        "note": "virtual identification mark (the orderable MPN) by adom-chip-laser; Microchip's real marking is 2562FDE / SN e3 YYWW / NNN (DS20005284B p.17)"}
def rec(path, **k):
    r = dict(path=path, format=path.rsplit(".", 1)[1], sha256=sha(path), git_blob=blob(path), bytes=os.path.getsize(path)); r.update(k); return r
models = [
 rec(f"{S}-etched.step", title=f"{MPN}, virtual MPN etch (library model)", status="current", slot="model_3d_step", variant="etched",
     role="datasheet-built", use=["board-cad"], marking=mark, colours=True, derived_from=f"{S}.step",
     unique="The board-CAD model: built from Microchip's SN drawing values with the MPN etched on top in the Adom.LaserEtch group; bound by the KiCad footprint, the Fusion .lbr and the Altium PcbLib",
     source="provenance/tools/build_soic8.py + adom-chip-laser 0.2.0 (service-occt etch, placed by adom-aiflow-etch)", thumb=f"docs/models/{S}-etched-glb-top.png", thumb_bottom=f"docs/models/{S}-etched-bottom.png"),
 rec(f"{S}.step", title=f"{MPN}, unmarked", status="current", slot="model_3d_step_plain", variant="plain", role="datasheet-built",
     use=["board-cad", "archive"], marking=None, colours=True, derived_from="Microchip DS20005284B p.23-24 (C04-00057-SN Rev L)",
     unique="The same package without any marking", source="provenance/tools/build_soic8.py with provenance/tools/params.json", thumb=f"docs/models/{S}-glb-top.png"),
 rec(f"{S}-etched.glb", title=f"{MPN}, virtual MPN etch (GLB, board use and page viewer)", status="current", slot="model_3d", variant="etched",
     role="derived", use=["render", "board-insertion", "wiki-hero"], marking=mark, colours=True, derived_from=f"{S}-etched.step",
     unique="Web copy of the etched STEP, clean (no pad outlines, labels or silkscreen), primitives batched per part, matte-tin leads and epoxy body",
     source="step2glb 0.13.2 --compress none + provenance/tools/glb_batch.py + materials.py", thumb=f"docs/models/{S}-etched-glb-top.png"),
 rec(f"{S}.glb", title=f"{MPN}, unmarked (GLB)", status="current", slot="model_3d_plain", variant="plain", role="derived",
     use=["render", "board-insertion"], marking=None, colours=True, derived_from=f"{S}.step",
     unique="Web copy of the unmarked STEP", source="step2glb 0.13.2 --compress none + glb_batch.py + materials.py", thumb=f"docs/models/{S}-glb-top.png"),
 rec("provenance/kicad-reference/SOIC-8_3.9x4.9mm_P1.27mm.step", title="KiCad library SOIC-8_3.9x4.9mm_P1.27mm (compared, not used)", status="previous",
     variant="original", role="derived", use=["archive"], marking=None, colours=True, derived_from="gitlab.com/kicad/libraries/kicad-packages3D (master, fetched 2026-10-21)",
     unique="KiCad's generic SOIC-8 model, kept as the comparison reference: its gauge-plane foot is 0.362 mm, under Microchip's 0.40 mm minimum",
     source="KiCad packages3D, CC-BY-SA 4.0 with the KiCad libraries exception"),
]
for l in ["high", "medium", "low", "super-low"]:
    p = f"lod/{S}-{l}.glb"
    models.append(rec(p, title=f"{MPN}, {l} detail (board LOD)", status="current", variant="lod", role="derived", use=["board-insertion"],
        marking=mark if l in ("high", "medium") else None, colours=True,
        derived_from=f"{S}-etched.step" if l == "high" else ("provenance/tools/params-low.json" if l in ("medium", "low") else "provenance/tools/params-superlow.json"),
        unique={"high": "Identical to the etched GLB", "medium": "Bends as 3 straight segments, etch grafted from the high level",
                "low": "Bends as 3 straight segments, no etch", "super-low": "Undrafted body, one-segment bends, no etch"}[l],
        source="build_soic8.py detail level + step2glb + glb_batch.py", thumb=f"lod/{S}-{l}.png"))
json.dump({"schema": "adom/models@1", "part": MPN, "updated": D, "models": models}, open("models.json", "w"), indent=1)

levels = []
for l in ["high", "medium", "low", "super-low"]:
    p = f"lod/{S}-{l}.glb"; st = glbstats(p)
    levels.append(dict(level=l, path=p, sha256=sha(p), sizeBytes=os.path.getsize(p), triangles=st["triangles"], primitives=st["primitives"],
        nodes=st["nodes"], boundsMm=[-3.0, -2.45, 0.0, 3.0, 2.45, 1.755 if l in ("high", "medium") else 1.75],
        markingIncluded=l in ("high", "medium"), helperNodes=[], extensions=[],
        derivation={"high": "the etched GLB itself: etched STEP, step2glb uncompressed, primitives batched per part (lossless)",
                    "medium": "the same generator at detail=low (bends as 3 chords, drafted body, pin-1 dimple), Adom.LaserEtch mesh grafted unchanged from high",
                    "low": "generator detail=low, no marking (unreadable at this size)",
                    "super-low": "generator detail=superlow: undrafted body, one-chord bends, pin-1 dimple kept, no marking"}[l],
        review="candidate: inspected as cached images (depth-buffered render); not qualified in a streaming LOD viewer",
        preview=dict(path=f"lod/{S}-{l}.png", sha256=sha(f"lod/{S}-{l}.png"), view="iso (front-left, from above), same camera on every level")))
json.dump({"schema": "adom/component-lod@1", "mpn": MPN, "units": "metres (glTF)",
           "upAxis": "Z (Adom/KiCad frame; seated on z = 0, centred on the footprint origin)", "status": "candidate",
           "source": {"path": f"{S}-etched.step", "sha256": sha(f"{S}-etched.step")},
           "generator": "provenance/tools/build_soic8.py (detail full/low/superlow) -> step2glb 0.13.2 convert --compress none -> provenance/tools/glb_batch.py (lossless batching per part; etch graft on medium) -> materials.py; previews by render_glb.py",
           "levels": levels}, open("lod/manifest.json", "w"), indent=1)

dm = json.load(open("provenance/dims-model.json")); dk = json.load(open("provenance/dims-kicad-library.json"))
dims = [("e pitch", "1.27 BSC", "1.270", "1.270"), ("A overall height", "max 1.75", f"{dm['A_overall_height']:.3f}", f"{dk['A_overall_height']:.3f}"),
        ("A2 molded thickness", "min 1.25", f"{dm['A2_molded_thickness']:.3f}", f"{dk['A2_molded_thickness']:.3f}"),
        ("A1 standoff", "0.10 / - / 0.25", f"{dm['A1_standoff']:.3f}", f"{dk['A1_standoff']:.3f}"),
        ("E overall width", "6.00 BSC", f"{dm['E_tip_to_tip']:.3f}", f"{dk['E_tip_to_tip']:.3f}"),
        ("E1 molded width", "3.90 BSC", f"{dm['E1_body_width']:.3f}", f"{dk['E1_body_width']:.3f}"),
        ("D overall length", "4.90 BSC", f"{dm['D_body_length']:.3f}", f"{dk['D_body_length']:.3f}"),
        ("L foot length (gauge plane 0.25 mm)", "0.40 / - / 1.27", f"{dm['L_gauge']:.3f}", f"{dk['L_gauge']:.3f}"),
        ("L foot, flat bottom only", "0.40 / - / 1.27", "0.413", "0.275"),
        ("L1 footprint (tip to body)", "1.04 REF", "1.050", "1.050"),
        ("c lead thickness", "0.17 / - / 0.25", f"{dm['c_lead_thickness']:.3f}", f"{dk['c_lead_thickness']:.3f}"),
        ("b lead width", "0.31 / - / 0.51", "0.410", "0.410"),
        ("R, R1 bend radius (inner)", "min 0.07", "0.10", "0.10"),
        ("theta1 mold draft", "5 to 15 deg", "9.0 (bottom) / 11.6 (top)", "9.0 / 11.6"),
        ("h chamfer", "0.25 to 0.50 (optional)", "not modelled", "not modelled")]
prov = {
 "schema": "adom/3d-model-provenance@1", "mpn": MPN, "date": D,
 "classification": "Datasheet-built: a CadQuery model generated from Microchip's printed SN package values, with KiCad's library model's bend radii, lead angle, mold draft and pin-1 dimple; produced by Claude (AI) for Adom. Not manufacturer CAD and not a Microchip certification.",
 "method_chosen": "3 (build from the drawing): Microchip's own STEP was not reachable and KiCad's library model fails the drawing's L minimum",
 "manufacturer_step": {"tried": "https://www.microchip.com/en-us/product/MCP2562FD (CAD/CAE models on the product page; served through a third-party CAD portal)", "result": "HTTP 403 to a plain request on 2026-10-21; no browser session was used, so the Microchip model was not obtained", "status": "not reachable"},
 "datasheet": {"doc": "Microchip DS20005284B (Revision B, March 2026), MCP2561/2FD High-Speed CAN Flexible Data Rate Transceiver",
   "file": "docs/mcp2562fd-e-sn-datasheet.pdf", "url": "https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP2561-2FD-High-Speed-CAN-Flexible-Data-Rate-Transceiver-DS20005284.pdf",
   "sha256": sha("docs/mcp2562fd-e-sn-datasheet.pdf"), "drawing_pages": "p.23-24 (C04-00057-SN Rev L), p.25 land pattern (C04-02057-SN Rev L)", "marking_page": "p.17"},
 "kicad_library_check": {
   "model": "KiCad packages3D Package_SO.3dshapes/SOIC-8_3.9x4.9mm_P1.27mm.step (CC-BY-SA 4.0)", "file": "provenance/kicad-reference/SOIC-8_3.9x4.9mm_P1.27mm.step",
   "sha256": sha("provenance/kicad-reference/SOIC-8_3.9x4.9mm_P1.27mm.step"),
   "measured": "L, the foot length", "datasheet": "0.40 min / 1.27 max (no nominal printed)", "kicad_library": round(dk["L_gauge"], 3), "built": round(dm["L_gauge"], 3),
   "method": "section through pin 1's centre line (plane y = +1.905 in the 3D frame), cut at the 0.25 mm gauge plane (z = 0.25); L = distance from the lead tip to where the lead's lower outer surface crosses the gauge plane (JEDEC definition); provenance/tools/dims.py. The flat bottom alone (the reading of Microchip's View C without a gauge plane) is 0.275 mm on KiCad's model and 0.413 mm on this one, so the verdict holds either way.",
   "verdict": "Every other printed dimension of KiCad's model is inside the drawing, but its foot is 0.362 mm at the gauge plane (0.275 mm flat), under Microchip's 0.40 mm minimum, so it was not used as is. KiCad's package generator was not run (it would be downloaded code); the model was rebuilt with the same bend shape and a longer flat foot instead.",
   "evidence": "docs/proof/mcp2562fd-e-sn-lead-vs-kicad.png"},
 "model": {"file": f"{S}.step", "sha256": sha(f"{S}.step"), "builder": "provenance/tools/build_soic8.py (CadQuery 2.x, OpenCascade)", "params": "provenance/tools/params.json",
   "how": "drafted two-part body loft (parting line 0.825), eight swept leads (rectangle b x c along a centreline with two 0.20 mm centreline-radius bends, 9.3 deg off vertical), pin-1 dimple 0.60 mm diameter, 0.05 mm deep; flat foot 0.413 mm tuned so the gauge-plane L is 0.50 mm",
   "L_target": "0.50 mm, chosen inside 0.40 to 1.27 (neither Microchip nor JEDEC MS-012 prints a nominal)",
   "frame": "Z up, seated on z = 0, centred on the KiCad footprint origin, pin 1 at (-x, +y) = footprint pad 1 at (-2.70, -1.905) in KiCad's Y-down footprint frame; binds with zero offset and rotation",
   "parts": {"body": "black epoxy", "pins": "matte tin"},
   "dimensions_mm": [dict(item=a, datasheet_min_typ_max=b, model=c, kicad_library=d) for a, b, c, d in dims],
   "pin1_mark": "0.60 mm dimple at (-1.11, +1.61) on the top face, KiCad's position for its pin-1 circle; Microchip note 1: the pin-1 index may vary but lies in the hatched pin-1 quadrant",
   "not_modelled": "optional end chamfer h; mold flash", "measurements": ["provenance/dims-model.json", "provenance/dims-kicad-library.json"]},
 "etched": {"file": f"{S}-etched.step", "sha256": sha(f"{S}-etched.step"), "tool": "adom-chip-laser 0.2.0 service (service-occt /laser-etch, flat light mark 5 um above the face, Adom.LaserEtch group), placed by adom-aiflow-etch's planner with the page footprint",
   "lines": ["MCP2562", "FD-E/SN"], "capMm": 0.473, "virtual": True, "gate": "adom-aiflow-etch check: PASS (axis, upright, size, band, flat, group, colour, widget)",
   "first_try": "a direct adom-chip-laser etch (surface=top) failed the gate's size check (0.27 mm text, 56 % of what fits) and is not published",
   "check_image": "docs/proof/mcp2562fd-e-sn-etched.check.png", "plan": "docs/proof/mcp2562fd-e-sn-etched.mark-plan.json",
   "real_marking": "Microchip marks the SOIC as 2562FDE / SN e3 YYWW / NNN (DS20005284B p.17); the etch is the orderable MPN instead, as a virtual identification aid"},
 "glb": {f: sha(f) for f in [f"{S}.glb", f"{S}-etched.glb"]},
 "outline": {"file": "artwork/MCP2562FD-E-SN-3d-outline-dark-named.svg", "sha256": sha("artwork/MCP2562FD-E-SN-3d-outline-dark-named.svg"),
   "tool": "adom-aiflow-etch outline (service body outline + single-stroke name on the planned band)", "gate": "PASS"},
 "tools": {"adom-chip-laser": "0.2.0", "adom-aiflow-etch": "adom/adom-aiflow (installed)", "step2glb": "0.13.2", "adom-symbol": "1.2.6", "adom-footprint": "1.0.26", "adom-lbr": "2.47.5", "cadquery": "2.x (local venv)"},
 "license": "Model, etch and artwork authored by Adom: CC-BY-4.0. The KiCad reference model in provenance/kicad-reference keeps CC-BY-SA 4.0 with the KiCad libraries exception. The Microchip datasheet keeps Microchip's terms."
}
json.dump(prov, open("provenance/3d-model.json", "w"), indent=1)

files = {p: sha(p) for p in [f"{S}.kicad_sym", f"{S}.kicad_mod", f"{S}.step", f"{S}-etched.step", f"{S}-etched.glb", f"{S}.lbr",
                             "altium/MCP2562FD-E-SN.SchLib", "altium/MCP2562FD-E-SN.PcbLib", "docs/mcp2562fd-e-sn-datasheet.pdf"]}
BY = {"name": "Claude (AI), automated checks, not yet reviewed by a person", "handle": "claude"}
def ev(*paths): return [{"label": p.rsplit("/", 1)[-1], "href": p} for p in paths]
def chk(*items): return [{"text": t, "result": r} for t, r in items]
ver = {"schema": "adom/verification@1", "part": MPN,
 "variants": [{"mpn": MPN, "note": "tube packaging; MCP2562FDT-E/SN is the same part on tape and reel (DS20005284B p.29)", "stocked": False}],
 "entries": [
  {"date": D, "by": BY, "status": "verified", "title": "Pinout matches the datasheet", "scope": ["symbol"], "variants": ["*"],
   "checks": chk(("Symbol pins match DS20005284B Table 1-2 (p.4), MCP2562FD PDIP/SOIC: 1 TXD, 2 VSS, 3 VDD, 4 RXD, 5 VIO, 6 CANL, 7 CANH, 8 STBY", "pass"),
                 ("Electrical types: TXD and STBY input, RXD output, CANH and CANL bidirectional, VDD, VIO and VSS power", "pass"),
                 ("Pin n maps to pad n, read back from the Fusion .lbr and the Altium SchLib/PcbLib", "pass"),
                 ("Every pin sits on the 2.54 mm grid (adom-symbol 1.2.6 put the first pin of each column at y = -2.00; snapped by provenance/tools/snap_symbol.py)", "note")),
   "evidence": ev("docs/figures/mcp2562fd-e-sn-ds-p1.png", "docs/figures/mcp2562fd-e-sn-ds-p4.png"),
   "files": {k: files[k] for k in [f"{S}.kicad_sym", f"{S}.lbr", "altium/MCP2562FD-E-SN.SchLib"]}},
  {"date": D, "by": BY, "status": "verified", "title": "Footprint equals Microchip's recommended land pattern", "scope": ["footprint"], "variants": ["*"],
   "checks": chk(("All 8 pads equal C04-02057-SN Rev L: 1.55 x 0.60 mm rectangles at x = +-2.70 (C 5.40), pitch 1.27, pad 1 at (-2.70, -1.905)", "pass"),
                 ("KiCad's SOIC-8_3.9x4.9mm_P1.27mm differs (1.95 x 0.60 at +-2.475, 0.425 mm further under the body); recorded in provenance/footprint-check.json", "note"),
                 ("Courtyard 7.50 x 5.40 mm, 0.25 mm beyond pads and body", "pass")),
   "evidence": ev("docs/proof/mcp2562fd-e-sn-footprint-check.png", "docs/figures/mcp2562fd-e-sn-ds-p25.png", "provenance/footprint-check.json"),
   "files": {f"{S}.kicad_mod": files[f"{S}.kicad_mod"]}},
  {"date": D, "by": BY, "status": "verified", "title": "Pin 1 orientation agrees across footprint, 3D model, symbol and datasheet", "scope": ["footprint", "3d"], "variants": ["*"],
   "checks": chk(("Footprint pad 1 top left (KiCad Y down) with the silkscreen pin-1 bar above it", "pass"),
                 ("3D pin 1 lead at (-x, +y) in the Z-up model, which is pad 1 after KiCad's Y flip; pin-1 dimple in the same corner", "pass"),
                 ("Pins run counter-clockwise seen from the top, as in the datasheet top view (p.23): the footprint is that view turned 90 degrees, not mirrored", "pass"),
                 ("The MPN etch reads upright with pin 1 lower left (adom-aiflow-etch gate PASS)", "pass")),
   "evidence": ev("docs/proof/mcp2562fd-e-sn-footprint-check.png", "docs/proof/mcp2562fd-e-sn-etched.check.png", "docs/figures/mcp2562fd-e-sn-ds-p23.png"),
   "files": {k: files[k] for k in [f"{S}.kicad_mod", f"{S}-etched.step"]}},
  {"date": D, "by": BY, "status": "verified", "title": "3D model inside Microchip's package drawing", "scope": ["3d"], "variants": ["*"],
   "checks": chk(("E 6.00, E1 3.90, D 4.90, A 1.75, A1 0.10, c 0.20, b 0.41: inside C04-00057-SN Rev L (p.24)", "pass"),
                 ("Foot L 0.500 mm at the 0.25 mm gauge plane (0.413 mm flat): inside 0.40 to 1.27", "pass"),
                 ("KiCad's library SOIC-8 model was not used: L 0.362 mm at the gauge plane (0.275 mm flat), under the 0.40 mm minimum", "note"),
                 ("Microchip's own STEP was not reachable from the product page without a signed-in browser", "note")),
   "evidence": ev("docs/proof/mcp2562fd-e-sn-lead-vs-kicad.png", "provenance/3d-model.json", "docs/figures/mcp2562fd-e-sn-ds-p24.png"),
   "files": {k: files[k] for k in [f"{S}.step", f"{S}-etched.step", f"{S}-etched.glb"]}},
  {"date": D, "by": BY, "status": "open", "title": "Still to do", "scope": ["3d", "library"], "variants": ["*"],
   "checks": chk(("Compare against Microchip's own STEP once someone downloads it through a signed-in browser", "open"),
                 ("Open the KiCad, Fusion and Altium libraries in the native tools; so far they are checked by lint and read-back only", "open"),
                 ("Annotated hero GLB (adom-hero pad outlines and labels) not built; the viewer shows the clean etched model", "open"),
                 ("Overlay of the model on the datasheet drawing views not made", "open")),
   "evidence": [], "files": {}}
 ]}
json.dump(ver, open("verification.json", "w"), indent=1)
print("OK")