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:
commit
1301212f13
32 files changed
+33133
No files matched your search
+106
@@ -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()))
|
||||
Reference in new issue
Block a user