v6 T16 script + bump kicad (mounting holes 130 x 90)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ4eAB6zjQT49RVwpgfKdt
This commit is contained in:
Leopoldo Rodríguez 2026-09-25 19:53:56 +00:00
parent 2b03a7ee02
commit c160ed4aa6
2 changed files with 80 additions and 1 deletions

@ -1 +1 @@
Subproject commit fdffd441d48a60d02cfbae93ecaae2246808e663
Subproject commit 61652ccc3f92403e89a01f0ba88660caeaed6be3

View File

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""v6 T16 — square up the 4 mounting holes and give them unique references.
Found 25-Sep: the board (140 x 100 mm, Edge.Cuts x 60.19–200.19, y 40.32–140.32) had
MH5 (65.2, 45.3) MH6 (65.2, 135.3) <- west pair, same position since v4/v5 (proven)
MH6 (195.5, 45.0) MH6 (195.5, 135.5) <- east pair off by 0.3 / 0.2 / 0.5 mm
so the pattern was 130.3 x 90.0 on the west and 90.5 on the east, and three holes shared "MH6".
Fix: keep the proven west pair and mirror it -> a clean 130.0 x 90.0 mm rectangle, every hole
~5.0 mm from its two edges, centred on the board:
MH5 (65.2, 45.3) MH6 (65.2, 135.3) MH7 (195.2, 45.3) MH8 (195.2, 135.3)
Only the footprint (at …) line and the Reference value change; the holes keep their UUIDs.
python3 T16.py hbox-watchdog-v6.kicad_pcb # dry run
python3 T16.py hbox-watchdog-v6.kicad_pcb --apply # writes .T16bak
"""
import re, sys, shutil
TARGET = [ # (current ref, current x, y) -> (new ref, x, y)
(('MH5', 65.2, 45.3), ('MH5', 65.2, 45.3)),
(('MH6', 65.2, 135.3), ('MH6', 65.2, 135.3)),
(('MH6', 195.5, 45.0), ('MH7', 195.2, 45.3)),
(('MH6', 195.5, 135.5), ('MH8', 195.2, 135.3)),
]
def span(s, i):
d = 0; instr = False; j = i
while True:
c = s[j]
if c == '"' and s[j-1] != '\\': instr = not instr
elif not instr:
if c == '(': d += 1
elif c == ')':
d -= 1
if d == 0: return j + 1
j += 1
def holes(s):
out = []
for m in re.finditer(r'\n\t\(footprint "MountingHole:[^"]*"', s):
a = m.start() + 1; e = span(s, a); b = s[a:e]
at = re.search(r'\n\t\t\(at (-?[\d.]+) (-?[\d.]+)([^)]*)\)', b)
ref = re.search(r'\(property "Reference" "([^"]*)"', b).group(1)
out.append(dict(a=a, e=e, ref=ref, x=float(at.group(1)), y=float(at.group(2)), rot=at.group(3)))
return out
def main():
path = sys.argv[1]; apply = '--apply' in sys.argv
s0 = open(path).read(); s = s0; ch = []
hs = holes(s0)
assert len(hs) == 4, f'expected 4 mounting holes, found {len(hs)}'
edits = []
for (r0, x0, y0), (r1, x1, y1) in TARGET:
h = [h for h in hs if abs(h['x'] - x1) < 1e-3 and abs(h['y'] - y1) < 1e-3 and h['ref'] == r1]
if h: continue # already in place
h = [h for h in hs if abs(h['x'] - x0) < 1e-3 and abs(h['y'] - y0) < 1e-3 and h['ref'] == r0]
if len(h) != 1: raise SystemExit(f'cannot identify hole {r0} at ({x0},{y0})')
edits.append((h[0], r1, x1, y1))
for h, r1, x1, y1 in sorted(edits, key=lambda t: -t[0]['a']):
b = s[h['a']:h['e']]
b = re.sub(r'\n\t\t\(at (-?[\d.]+) (-?[\d.]+)([^)]*)\)', f'\n\t\t(at {x1:g} {y1:g}{h["rot"]})', b, count=1)
b = b.replace(f'(property "Reference" "{h["ref"]}"', f'(property "Reference" "{r1}"', 1)
s = s[:h['a']] + b + s[h['e']:]
ch.append(f'{h["ref"]} ({h["x"]:g},{h["y"]:g}) -> {r1} ({x1:g},{y1:g})')
hs1 = holes(s)
refs = [h['ref'] for h in hs1]
assert sorted(refs) == ['MH5', 'MH6', 'MH7', 'MH8'], refs
xs = sorted({h['x'] for h in hs1}); ys = sorted({h['y'] for h in hs1})
assert len(xs) == 2 and len(ys) == 2, 'holes not on a rectangle'
print('\n'.join(ch) or 'nothing to do (already applied)')
print(f'pattern {xs[1]-xs[0]:.2f} x {ys[1]-ys[0]:.2f} mm')
if ch and apply:
shutil.copy2(path, path + '.T16bak'); open(path, 'w').write(s); print('APPLIED (backup .T16bak)')
elif ch:
print('DRY RUN — re-run with --apply')
if __name__ == '__main__':
main()