From c160ed4aa6f9e40d038351caa1b76b14bbc43ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leopoldo=20Rodr=C3=ADguez?= Date: Fri, 25 Sep 2026 19:53:56 +0000 Subject: [PATCH] v6 T16 script + bump kicad (mounting holes 130 x 90) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YJ4eAB6zjQT49RVwpgfKdt --- 03-Architecture-Hardware/kicad | 2 +- scripts/2026-09-25_v6_T16_mounting_holes.py | 79 +++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 scripts/2026-09-25_v6_T16_mounting_holes.py diff --git a/03-Architecture-Hardware/kicad b/03-Architecture-Hardware/kicad index fdffd44..61652cc 160000 --- a/03-Architecture-Hardware/kicad +++ b/03-Architecture-Hardware/kicad @@ -1 +1 @@ -Subproject commit fdffd441d48a60d02cfbae93ecaae2246808e663 +Subproject commit 61652ccc3f92403e89a01f0ba88660caeaed6be3 diff --git a/scripts/2026-09-25_v6_T16_mounting_holes.py b/scripts/2026-09-25_v6_T16_mounting_holes.py new file mode 100644 index 0000000..37d2ef8 --- /dev/null +++ b/scripts/2026-09-25_v6_T16_mounting_holes.py @@ -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()