Ofensteuerung v1.0: Wemos-Platine 100x100 mit Pumpenrelais 230 V, Thermostat parallel, Strommessung ZMCT103C/ADS1115, MAX6675 Abgas, 3x DS18B20, Servo MG996R, PWM-Pumpe; 2-stufiges Freerouting, ESPHome-Konfig

This commit is contained in:
OpenClaw committed 2026-11-07 04:01:44 +01:00
commit 1301212f13
32 files changed
+33133

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Zweistufiges Routing mit Freerouting.
1) nur 230-V-Netze, eingesperrt in MAINS_AREA; CT-Sekundärpins/-Stummel mit 4 mm Abstandsklasse
2) Kleinspannung, MAINS_AREA gesperrt (230-V-Bahnen + LV-Stummel bleiben fest)
-> ofensteuerung.kicad_pcb"""
import os, sys, shutil, subprocess, pcbnew
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
from design import PROJECT, MAINS, MAINS_AREA, W, H
src = os.path.join(HERE, PROJECT + '_unrouted.kicad_pcb')
dst = os.path.join(HERE, PROJECT + '.kicad_pcb')
shutil.copy(os.path.join(HERE, PROJECT + '.kicad_pro'), src.replace('.kicad_pcb', '.kicad_pro'))
mm = pcbnew.FromMM
JAR = os.path.expanduser('~/tools/freerouting.jar')
passes = int(sys.argv[1]) if len(sys.argv) > 1 else 30
def freeroute(b, tag, mp):
dsn, ses = f'/tmp/ofen_{tag}.dsn', f'/tmp/ofen_{tag}.ses'
if os.path.exists(ses): os.remove(ses)
assert pcbnew.ExportSpecctraDSN(b, dsn)
for attempt in range(4):
if os.path.exists(ses): os.remove(ses)
r = subprocess.run(['java', '-jar', JAR, '-de', dsn, '-do', ses, '-mp', str(mp), '-l', 'en',
'--gui.enabled=false', '--usage_and_diagnostic_data.disable_analytics=true'],
capture_output=True, text=True, timeout=1800)
final = [l for l in r.stdout.splitlines() if 'Auto-routing stage completed' in l]
print(tag, attempt, final[-1][-120:] if final else r.stdout[-1500:])
if final and '(0 unrouted' in final[-1] and os.path.exists(ses):
return ses
mp += 20
raise SystemExit(tag + ': routing unvollständig\n' + r.stdout[-3000:] + r.stderr[-2000:])
def keepout(b, pts, name):
z = pcbnew.ZONE(b); z.SetIsRuleArea(True); z.SetDoNotAllowTracks(True); z.SetDoNotAllowVias(True)
z.SetDoNotAllowZoneFills(True); z.SetDoNotAllowPads(False); z.SetDoNotAllowFootprints(False); z.SetZoneName(name)
ls = pcbnew.LSET(); ls.AddLayer(pcbnew.F_Cu); ls.AddLayer(pcbnew.B_Cu); z.SetLayerSet(ls)
ol = z.Outline(); ol.NewOutline()
for x, y in pts: ol.Append(mm(x), mm(y))
b.Add(z); return z
def strip_zones(b):
zs = [z for z in b.Zones() if not z.GetIsRuleArea()]
for z in zs: b.Remove(z)
return zs
ismains = lambda n: n.lstrip('/') in MAINS or n.startswith('unconnected-(K1-')
# ---------- Stufe 1: 230 V ----------
import json
p1 = '/tmp/ofen_p1.kicad_pcb'
shutil.copy(src, p1)
pro = json.load(open(os.path.join(HERE, PROJECT + '.kicad_pro')))
nsj = pro['net_settings']
d = dict([c for c in nsj['classes'] if c['name'] == 'Default'][0]); d.update(name='Iso', clearance=4.0, priority=0)
nsj['classes'].append(d)
nsj['netclass_patterns'].append({'netclass': 'Iso', 'pattern': 'ISO_*'})
json.dump(pro, open(p1.replace('.kicad_pcb', '.kicad_pro'), 'w'), indent=2)
b1 = pcbnew.LoadBoard(p1)
_zs1 = strip_zones(b1)
_ra1 = [z for z in b1.Zones() if z.GetZoneName() == 'NETZ_230V']
for z in _ra1:
b1.Remove(z) # reine "keine Fläche"-Zone wird im DSN als Sperrfläche exportiert
newnets = {}
def mknet(name):
if name not in newnets:
n = pcbnew.NETINFO_ITEM(b1, name); b1.Add(n); newnets[name] = n
return newnets[name]
for nm in ('ISO_VMID', 'ISO_CT_A', 'COIL_P5V', 'COIL_RELAY_C'):
mknet(nm)
for fp in list(b1.GetFootprints()):
for pd in list(fp.Pads()):
n = pd.GetNetname()
if not n or ismains(n):
continue
if fp.GetReference() == 'T1':
pd.SetNet(mknet('ISO_' + n.lstrip('/')))
elif fp.GetReference() == 'K1':
pd.SetNet(mknet('COIL_' + n.lstrip('/').replace('+', 'P')))
else:
pd.SetNet(b1.FindNet('')) # Hindernis ohne Netz
_tr1 = list(b1.GetTracks())
for t in _tr1:
b1.Remove(t) # LV-Stummel in Stufe 1 weglassen (würden die Sperrflächen verletzen)
b1.SynchronizeNetsAndNetClasses(True)
x1, y1 = MAINS_AREA[2]
keepout(b1, [(x1, 0), (W, 0), (W, H), (x1, H)], 'NUR_LV_R')
keepout(b1, [(0, y1), (x1, y1), (x1, H), (0, H)], 'NUR_LV_U')
ses1 = freeroute(b1, 'netz', passes)
# ---------- Stufe 2: Kleinspannung ----------
b = pcbnew.LoadBoard(src)
zones = strip_zones(b)
assert pcbnew.ImportSpecctraSES(b, ses1)
for t in b.GetTracks():
if ismains(t.GetNetname()):
t.SetLocked(True)
ko = keepout(b, MAINS_AREA, 'ROUTE_KEEPOUT')
ses2 = freeroute(b, 'lv', passes)
assert pcbnew.ImportSpecctraSES(b, ses2)
b.Remove(ko)
for z in zones: b.Add(z)
pcbnew.ZONE_FILLER(b).Fill(b.Zones())
b.BuildConnectivity()
print('unconnected after fill:', b.GetConnectivity().GetUnconnectedCount(True))
pcbnew.SaveBoard(dst, b)
print('saved', dst, 'tracks', len(b.GetTracks()))