component
TMC2262
Public Made by Adomby adom
Analog Devices TMC2262 65 V, 4 A RMS smart stepper driver with step/dir, SPI and encoder input, 30-pin FC2QFN 6x6. KiCad, Fusion and Altium libraries, datasheet-built 3D model, every pin cited to the data sheet.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
"""Add the footprint's heatsink vias to a component hero GLB as the Adom.HeatsinkVias layer (wiki-component standard):
copper barrels, 65 % transparent, through a pretend 1.6 mm board below the seating plane, declared as the
switchable "Heatsink Vias" group (the viewer makes every Adom.* node one; a declared adomLayer as well would list it twice).
python3 hero_post_vias.py <slug>-hero.glb <footprint.kicad_mod>
Reads the plated thru_hole pads straight off the footprint. Frame: the GLB is Z-up in metres (step2glb), KiCad is
Y-down in mm, so a pad at (x, y) becomes (x, -y) * 1e-3. Idempotent: an existing Adom.HeatsinkVias node is replaced.
"""
import math, re, struct, sys
import numpy as np
import pygltflib
from pygltflib import Accessor, BufferView, Mesh, Primitive, Attributes, Material, PbrMetallicRoughness, Node
BOARD = 1.6e-3
SEG = 28
def vias(mod):
t = open(mod).read()
out = []
for num, x, y, size, drill in re.findall(r'\(pad "([^"]+)" thru_hole circle \(at ([-\d.]+) ([-\d.]+)\) \(size ([\d.]+) [\d.]+\) \(drill ([\d.]+)\)', t):
out.append((num, float(x), float(y), float(size), float(drill)))
return out
def tube(cx, cy, ro, ri, z0, z1):
"""Triangles of a hollow barrel: outer wall, inner wall, top and bottom annuli. Returns (positions, normals, indices)."""
P, N, I = [], [], []
def add(p, n):
P.append(p); N.append(n); return len(P) - 1
for k in range(SEG):
a0, a1 = 2 * math.pi * k / SEG, 2 * math.pi * (k + 1) / SEG
c0, s0, c1, s1 = math.cos(a0), math.sin(a0), math.cos(a1), math.sin(a1)
for r, sgn in ((ro, 1), (ri, -1)):
q = [add((cx + r * c, cy + r * s, z), (sgn * c, sgn * s, 0)) for c, s, z in ((c0, s0, z0), (c1, s1, z0), (c1, s1, z1), (c0, s0, z1))]
I += [q[0], q[1], q[2], q[0], q[2], q[3]] if sgn > 0 else [q[0], q[2], q[1], q[0], q[3], q[2]]
for z, nz in ((z1, 1), (z0, -1)):
q = [add((cx + r * c, cy + r * s, z), (0, 0, nz)) for r, c, s in ((ri, c0, s0), (ro, c0, s0), (ro, c1, s1), (ri, c1, s1))]
I += [q[0], q[1], q[2], q[0], q[2], q[3]] if nz > 0 else [q[0], q[2], q[1], q[0], q[3], q[2]]
return P, N, I
def main(glb, mod):
g = pygltflib.GLTF2().load(glb)
vs = vias(mod)
if not vs:
print('OK %s: no heatsink vias in the footprint' % glb); return
# drop an earlier run
old = [i for i, n in enumerate(g.nodes) if n.name == 'Adom.HeatsinkVias']
for sc in g.scenes:
sc.nodes = [n for n in sc.nodes if n not in old]
P, N, I = [], [], []
for num, x, y, size, drill in vs:
p, n, i = tube(x * 1e-3, -y * 1e-3, size / 2 * 1e-3, drill / 2 * 1e-3, -BOARD, 0.0)
base = len(P); P += p; N += n; I += [base + k for k in i]
pos = np.array(P, dtype=np.float32); nor = np.array(N, dtype=np.float32); idx = np.array(I, dtype=np.uint32)
blob = bytearray(g.binary_blob() or b'')
def push(arr, target):
while len(blob) % 4: blob.append(0)
off = len(blob); data = arr.tobytes(); blob.extend(data)
g.bufferViews.append(BufferView(buffer=0, byteOffset=off, byteLength=len(data), target=target))
return len(g.bufferViews) - 1
bp = push(pos, pygltflib.ARRAY_BUFFER); bn = push(nor, pygltflib.ARRAY_BUFFER); bi = push(idx, pygltflib.ELEMENT_ARRAY_BUFFER)
g.accessors.append(Accessor(bufferView=bp, componentType=pygltflib.FLOAT, count=len(pos), type='VEC3', min=pos.min(0).tolist(), max=pos.max(0).tolist()))
ap = len(g.accessors) - 1
g.accessors.append(Accessor(bufferView=bn, componentType=pygltflib.FLOAT, count=len(nor), type='VEC3')); an = len(g.accessors) - 1
g.accessors.append(Accessor(bufferView=bi, componentType=pygltflib.UNSIGNED_INT, count=len(idx), type='SCALAR')); ai = len(g.accessors) - 1
g.materials.append(Material(name='Adom.HeatsinkVias.copper', alphaMode='BLEND', doubleSided=True,
pbrMetallicRoughness=PbrMetallicRoughness(baseColorFactor=[0.85, 0.52, 0.25, 0.35], metallicFactor=0.6, roughnessFactor=0.35)))
mat = len(g.materials) - 1
g.meshes.append(Mesh(name='Adom.HeatsinkVias', primitives=[Primitive(attributes=Attributes(POSITION=ap, NORMAL=an), indices=ai, material=mat)]))
node = Node(name='Adom.HeatsinkVias', mesh=len(g.meshes) - 1,
extras={'tooltip': 'Heatsink vias from the footprint: %d plated holes, %.2f mm drill, through a 1.6 mm board (reference artwork, not part of the chip)' % (len(vs), vs[0][4])})
g.nodes.append(node)
g.scenes[g.scene or 0].nodes.append(len(g.nodes) - 1)
g.buffers[0].byteLength = len(blob)
g.set_binary_blob(bytes(blob))
g.save(glb)
print('OK %s: %d heatsink vias added as Adom.HeatsinkVias' % (glb, len(vs)))
if __name__ == '__main__':
main(sys.argv[1], sys.argv[2])