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
+178
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Platzierte Platine (ohne Leiterbahnen) aus design.py erzeugen -> ofensteuerung_unrouted.kicad_pcb"""
|
||||
import os, sys, json
|
||||
import pcbnew
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
from design import PARTS, PROJECT, W, H, MAINS, MAINS_AREA
|
||||
REFPOS = {'A1': (81.4, 20.0)}
|
||||
FPDIR = '/usr/share/kicad/footprints'
|
||||
mm = pcbnew.FromMM
|
||||
def V(x, y): return pcbnew.VECTOR2I(mm(x), mm(y))
|
||||
|
||||
symuuids = json.load(open(os.path.join(HERE, 'symuuids.json')))
|
||||
board = pcbnew.LoadBoard(os.path.join(HERE, PROJECT + '_template.kicad_pcb')) if False else pcbnew.CreateEmptyBoard()
|
||||
board.SetFileName(os.path.join(HERE, PROJECT + '.kicad_pcb'))
|
||||
|
||||
ds = board.GetDesignSettings()
|
||||
ds.SetCopperLayerCount(2)
|
||||
ns = ds.m_NetSettings
|
||||
dnc = ns.GetDefaultNetclass()
|
||||
dnc.SetTrackWidth(mm(0.4)); dnc.SetClearance(mm(0.25)); dnc.SetViaDiameter(mm(0.8)); dnc.SetViaDrill(mm(0.4))
|
||||
pnc = pcbnew.NETCLASS('Power')
|
||||
pnc.SetTrackWidth(mm(0.8)); pnc.SetClearance(mm(0.3)); pnc.SetViaDiameter(mm(1.0)); pnc.SetViaDrill(mm(0.5)); pnc.SetPriority(0)
|
||||
ns.SetNetclass('Power', pnc)
|
||||
pnc.SetTrackWidth(mm(1.0))
|
||||
mnc = pcbnew.NETCLASS('Mains')
|
||||
mnc.SetTrackWidth(mm(2.0)); mnc.SetClearance(mm(2.5)); mnc.SetViaDiameter(mm(2.2)); mnc.SetViaDrill(mm(1.0)); mnc.SetPriority(0)
|
||||
ns.SetNetclass('Mains', mnc)
|
||||
for pat in ('GND', '+5V', '+3V3'):
|
||||
ns.SetNetclassPatternAssignment(pat, 'Power')
|
||||
for n in MAINS:
|
||||
ns.SetNetclassPatternAssignment('/' + n, 'Mains')
|
||||
ns.SetNetclassPatternAssignment('unconnected-(K1-*', 'Mains')
|
||||
ds.m_TrackMinWidth = mm(0.25); ds.m_MinClearance = mm(0.2); ds.m_CopperEdgeClearance = mm(0.4)
|
||||
|
||||
nets = {}
|
||||
POWER = ('GND', '+5V', '+3V3')
|
||||
def net(name):
|
||||
name = name if (name in POWER or name.startswith('unconnected-')) else '/' + name
|
||||
if name not in nets:
|
||||
n = pcbnew.NETINFO_ITEM(board, name); board.Add(n); nets[name] = n
|
||||
return nets[name]
|
||||
|
||||
for p in PARTS:
|
||||
if p.get('board') is False or not p['fp']:
|
||||
continue
|
||||
lib, name = p['fp'].split(':')
|
||||
libpath = os.path.join(HERE, lib + '.pretty') if lib == PROJECT else os.path.join(FPDIR, lib + '.pretty')
|
||||
fp = pcbnew.FootprintLoad(libpath, name)
|
||||
if fp is None:
|
||||
raise SystemExit('fp missing ' + p['fp'])
|
||||
fp.SetFPID(pcbnew.LIB_ID(lib, name))
|
||||
fp.SetReference(p['ref']); fp.SetValue(p['val'])
|
||||
fp.SetPath(pcbnew.KIID_PATH('/' + symuuids[p['ref']]))
|
||||
x, y, rot = p['pcb']
|
||||
fp.SetPosition(V(x, y))
|
||||
fp.SetOrientationDegrees(rot)
|
||||
board.Add(fp)
|
||||
for pad in fp.Pads():
|
||||
n = p['pins'].get(pad.GetNumber())
|
||||
if n:
|
||||
pad.SetNet(net(n))
|
||||
elif pad.GetNumber() and pad.GetNumber() in p['pins']:
|
||||
nm = p.get('ncname', {}).get(pad.GetNumber(), '')
|
||||
pad.SetNet(net(f"unconnected-({p['ref']}-{nm + '-' if nm else ''}Pad{pad.GetNumber()})"))
|
||||
for k in ('Description', 'Bezugsquelle'):
|
||||
v = p.get({'Description': 'desc', 'Bezugsquelle': 'mpn'}[k], '')
|
||||
if k == 'Description' or v:
|
||||
try:
|
||||
fld = fp.GetField(k) if fp.HasField(k) else None
|
||||
if fld is None:
|
||||
fld = pcbnew.PCB_FIELD(fp, fp.GetNextFieldId() if hasattr(fp,'GetNextFieldId') else -1, k); fp.Add(fld)
|
||||
fld.SetText(v); fld.SetVisible(False); fld.SetLayer(pcbnew.F_Fab)
|
||||
except Exception as e:
|
||||
print('field', k, e)
|
||||
# Referenz klein, Value auf Fab
|
||||
fp.Reference().SetTextSize(V(0.9, 0.9)); fp.Reference().SetTextThickness(mm(0.14))
|
||||
fp.Value().SetLayer(pcbnew.F_Fab)
|
||||
if p['fp'].split(':')[0] in ('Resistor_THT', 'Diode_THT') or p['fp'].startswith('Capacitor_THT:C_Disc'):
|
||||
pads = list(fp.Pads())
|
||||
a, b2 = pads[0].GetPosition(), pads[1].GetPosition()
|
||||
fp.Reference().SetPosition(pcbnew.VECTOR2I((a.x + b2.x) // 2, (a.y + b2.y) // 2))
|
||||
fp.Reference().SetTextAngleDegrees(rot if rot in (0, 90) else rot - 180)
|
||||
fp.Reference().SetTextSize(V(0.8, 0.8)); fp.Reference().SetTextThickness(mm(0.13))
|
||||
if p['ref'] in REFPOS:
|
||||
fp.Reference().SetPosition(V(*REFPOS[p['ref']])); fp.Reference().SetTextAngleDegrees(0)
|
||||
if p['ref'] in ('J10',):
|
||||
fp.Reference().SetVisible(False)
|
||||
if p['fp'].startswith('Connector_Phoenix'):
|
||||
npins = len(p['pins']); sgn = 1 if rot == 0 else -1
|
||||
fp.Reference().SetPosition(V(x + sgn * 5.08 * (npins - 1) + sgn * 2.6, y + (-2.0 if rot == 180 else 2.0)))
|
||||
fp.Reference().SetTextAngleDegrees(0)
|
||||
|
||||
# Platinenumriss mit abgerundeten Ecken
|
||||
def seg(a, b):
|
||||
s = pcbnew.PCB_SHAPE(board); s.SetShape(pcbnew.SHAPE_T_SEGMENT); s.SetStart(V(*a)); s.SetEnd(V(*b))
|
||||
s.SetLayer(pcbnew.Edge_Cuts); s.SetWidth(mm(0.1)); board.Add(s)
|
||||
def arc(c, start, ang):
|
||||
s = pcbnew.PCB_SHAPE(board); s.SetShape(pcbnew.SHAPE_T_ARC); s.SetCenter(V(*c)); s.SetStart(V(*start))
|
||||
s.SetArcAngleAndEnd(pcbnew.EDA_ANGLE(ang, pcbnew.DEGREES_T)); s.SetLayer(pcbnew.Edge_Cuts); s.SetWidth(mm(0.1)); board.Add(s)
|
||||
r = 2.0
|
||||
seg((r, 0), (W - r, 0)); seg((W, r), (W, H - r)); seg((W - r, H), (r, H)); seg((0, H - r), (0, r))
|
||||
arc((W - r, r), (W - r, 0), 90); arc((W - r, H - r), (W, H - r), 90); arc((r, H - r), (r, H), 90); arc((r, r), (0, r), 90)
|
||||
|
||||
# GND-Flächen beidseitig
|
||||
def zone(layer, netname, pts, keepout=False):
|
||||
z = pcbnew.ZONE(board)
|
||||
z.SetLayer(layer)
|
||||
if keepout:
|
||||
z.SetIsRuleArea(True); z.SetDoNotAllowTracks(True); z.SetDoNotAllowVias(True); z.SetDoNotAllowZoneFills(True)
|
||||
z.SetDoNotAllowPads(False); z.SetDoNotAllowFootprints(False)
|
||||
ls = pcbnew.LSET(); ls.AddLayer(pcbnew.F_Cu); ls.AddLayer(pcbnew.B_Cu); z.SetLayerSet(ls)
|
||||
else:
|
||||
z.SetNet(net(netname)); z.SetMinThickness(mm(0.25)); z.SetLocalClearance(mm(0.4))
|
||||
z.SetPadConnection(pcbnew.ZONE_CONNECTION_THERMAL); z.SetThermalReliefGap(mm(0.5)); z.SetThermalReliefSpokeWidth(mm(0.6))
|
||||
z.SetAssignedPriority(0)
|
||||
ol = z.Outline(); ol.NewOutline()
|
||||
for (x, y) in pts:
|
||||
ol.Append(mm(x), mm(y))
|
||||
board.Add(z)
|
||||
return z
|
||||
for L in (pcbnew.F_Cu, pcbnew.B_Cu):
|
||||
zone(L, 'GND', [(0.5, 0.5), (W - 0.5, 0.5), (W - 0.5, H - 0.5), (0.5, H - 0.5)])
|
||||
# Antennen-Freifläche unter dem ESP8266-Modul (Antenne zeigt zum oberen Rand)
|
||||
zone(pcbnew.F_Cu, None, [(68.6, 1.0), (94.2, 1.0), (94.2, 13.0), (68.6, 13.0)], keepout=True)
|
||||
# 230-V-Bereich: keine GND-Fläche (beide Lagen)
|
||||
z = pcbnew.ZONE(board); z.SetIsRuleArea(True); z.SetDoNotAllowZoneFills(True); z.SetDoNotAllowTracks(False); z.SetDoNotAllowVias(False)
|
||||
z.SetDoNotAllowPads(False); z.SetDoNotAllowFootprints(False); z.SetZoneName('NETZ_230V')
|
||||
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 MAINS_AREA:
|
||||
ol.Append(mm(x), mm(y))
|
||||
board.Add(z)
|
||||
|
||||
# Kleinspannungs-Stummel aus dem 230-V-Bereich (CT-Sekundärseite, Relaisspule), gesperrt
|
||||
def padpos(ref, num):
|
||||
for fp in board.GetFootprints():
|
||||
if fp.GetReference() == ref:
|
||||
for pd in fp.Pads():
|
||||
if pd.GetNumber() == num:
|
||||
p = pd.GetPosition(); return (pcbnew.ToMM(p.x), pcbnew.ToMM(p.y))
|
||||
raise KeyError((ref, num))
|
||||
def track(netname, pts, width):
|
||||
for a, b in zip(pts, pts[1:]):
|
||||
t = pcbnew.PCB_TRACK(board); t.SetStart(V(*a)); t.SetEnd(V(*b)); t.SetWidth(mm(width)); t.SetLayer(pcbnew.F_Cu)
|
||||
t.SetNet(net(netname)); t.SetLocked(True); board.Add(t)
|
||||
YO = MAINS_AREA[2][1] + 1.0
|
||||
vm = padpos('T1', '2'); ca = padpos('T1', '1'); k5 = padpos('K1', '5'); k2 = padpos('K1', '2')
|
||||
track('VMID', [vm, (vm[0] - 2.0, vm[1] + 2.0), (vm[0] - 2.0, YO)], 0.4)
|
||||
track('CT_A', [ca, (ca[0] + 2.0, ca[1] + 2.0), (ca[0] + 2.0, YO)], 0.4)
|
||||
track('+5V', [k5, (k5[0] - 1.5, k5[1] + 1.5), (k5[0] - 1.5, YO)], 1.0)
|
||||
track('RELAY_C', [k2, (k2[0] + 1.5, k2[1] + 1.5), (k2[0] + 1.5, YO)], 0.6)
|
||||
# Trennlinie 230 V auf dem Bestückungsdruck
|
||||
for a, b in (((MAINS_AREA[1][0], 1.5), MAINS_AREA[2]), (MAINS_AREA[2], (1.5, MAINS_AREA[2][1]))):
|
||||
s = pcbnew.PCB_SHAPE(board); s.SetShape(pcbnew.SHAPE_T_SEGMENT); s.SetStart(V(*a)); s.SetEnd(V(*b))
|
||||
s.SetLayer(pcbnew.F_SilkS); s.SetWidth(mm(0.3)); board.Add(s)
|
||||
|
||||
# Randstreifen: keine Leiterbahnen näher als 0,8 mm am Rand
|
||||
for pts in ([(0, 0), (W, 0), (W, 0.8), (0, 0.8)], [(0, H - 0.8), (W, H - 0.8), (W, H), (0, H)],
|
||||
[(0, 0), (0.8, 0), (0.8, H), (0, H)], [(W - 0.8, 0), (W, 0), (W, H), (W - 0.8, H)]):
|
||||
z = zone(pcbnew.F_Cu, None, pts, keepout=True); z.SetDoNotAllowZoneFills(False)
|
||||
|
||||
# Beschriftung
|
||||
def text(t, x, y, size=1.2, layer='F.SilkS', rot=0, bold=False):
|
||||
layer = {'F.SilkS': pcbnew.F_SilkS, 'B.SilkS': pcbnew.B_SilkS}[layer]
|
||||
s = pcbnew.PCB_TEXT(board); s.SetText(t)
|
||||
if layer == pcbnew.B_SilkS: s.SetMirrored(True)
|
||||
s.SetPosition(V(x, y)); s.SetLayer(layer)
|
||||
s.SetTextSize(V(size, size)); s.SetTextThickness(mm(size * (0.2 if bold else 0.15))); s.SetTextAngleDegrees(rot)
|
||||
board.Add(s)
|
||||
from design import SILK
|
||||
for args in SILK:
|
||||
text(*args)
|
||||
|
||||
ns.RecomputeEffectiveNetclasses() if hasattr(ns,'RecomputeEffectiveNetclasses') else None
|
||||
board.SynchronizeNetsAndNetClasses(True)
|
||||
pcbnew.SaveBoard(os.path.join(HERE, PROJECT + '_unrouted.kicad_pcb'), board)
|
||||
print('saved', len(board.GetFootprints()), 'footprints')
|
||||
Reference in new issue
Block a user