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 @@
|
||||
"""Ofensteuerung (Wemos D1 mini) - einzige Quelle für Schaltplan + Platine.
|
||||
|
||||
Pins: Symbol-Pinnummer == Footprint-Padnummer.
|
||||
pcb: (x, y, rot) in mm, Platine 100 x 100, Ursprung oben links.
|
||||
230-V-Bereich oben links (Rahmen auf dem Bestückungsdruck), Kleinspannung rechts und unten.
|
||||
"""
|
||||
PROJECT = 'ofensteuerung'
|
||||
W, H = 100.0, 100.0
|
||||
|
||||
FP_R = 'Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal'
|
||||
FP_C = 'Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm'
|
||||
FP_CP10 = 'Capacitor_THT:CP_Radial_D10.0mm_P5.00mm'
|
||||
FP_CP8 = 'Capacitor_THT:CP_Radial_D8.0mm_P3.50mm'
|
||||
FP_CP5 = 'Capacitor_THT:CP_Radial_D5.0mm_P2.00mm'
|
||||
FP_Q = 'Package_TO_SOT_THT:TO-92_Inline_Wide'
|
||||
FP_LED = 'LED_THT:LED_D3.0mm'
|
||||
FP_D41 = 'Diode_THT:D_DO-41_SOD81_P10.16mm_Horizontal'
|
||||
FP_T3 = 'Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal'
|
||||
FP_T2 = 'Connector_Phoenix_MSTB:PhoenixContact_MSTBA_2,5_2-G-5,08_1x02_P5.08mm_Horizontal'
|
||||
FP_H2 = 'Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical'
|
||||
TERM = 'Steckklemme 5,08 mm (Phoenix MSTBA/MSTB oder 2EDGR/2EDGK kompatibel)'
|
||||
TERM230 = 'Steckklemme 5,08 mm, 250 V / 12 A (Phoenix MSTBA 2,5/x-G-5,08 + MSTB 2,5/x-ST-5,08)'
|
||||
|
||||
MAINS = ('L_IN', 'L_F', 'L_SW', 'L_PUMP', 'N', 'PE')
|
||||
|
||||
PARTS = []
|
||||
def P(ref, sym, val, fp, pins, pcb, **kw):
|
||||
d = dict(ref=ref, sym=sym, val=val, fp=fp, pins=pins, pcb=pcb); d.update(kw); PARTS.append(d)
|
||||
def R(ref, val, a, b, pcb, **kw):
|
||||
P(ref, 'Device:R', val, FP_R, {'1': a, '2': b}, pcb, **kw)
|
||||
def C(ref, val, a, b, pcb, fp=FP_C, sym='Device:C', **kw):
|
||||
P(ref, sym, val, fp, {'1': a, '2': b}, pcb, **kw)
|
||||
|
||||
# ================= 230 V =================
|
||||
P('J2', 'Connector:Screw_Terminal_01x03', 'NETZ 230V (PE/N/L)', FP_T3, {'1': 'L_IN', '2': 'N', '3': 'PE'}, (21.16, 10.5, 180), mpn=TERM230,
|
||||
desc='Netzzuleitung, vor der Platine abgesichert (LS 10 A)')
|
||||
P('J4', 'Connector:Screw_Terminal_01x02', 'THERMOSTAT (L/Ls)', FP_T2, {'1': 'L_SW', '2': 'L_F'}, (34.44, 10.5, 180), mpn=TERM230,
|
||||
desc='Potentialfreier Schließer (Anlegethermostat), parallel zum Relaiskontakt - schaltet die Pumpe auch ohne ESP')
|
||||
P('J3', 'Connector:Screw_Terminal_01x03', 'PUMPE 230V (L/N/PE)', FP_T3, {'1': 'PE', '2': 'N', '3': 'L_PUMP'}, (52.8, 10.5, 180), mpn=TERM230)
|
||||
P('F1', 'Device:Fuse', 'T 2A', 'Fuse:Fuseholder_Clip-5x20mm_Littelfuse_111_Inline_P20.00x5.00mm_D1.05mm_Horizontal',
|
||||
{'1': 'L_IN', '2': 'L_F'}, (9.36, 25.0, 0), desc='Feinsicherung 5x20 träge, Pumpe + Thermostatkreis', mpn='2x Sicherungsclip 5x20 (oder Halter aus B-12-2)')
|
||||
P('K1', 'Relay:SANYOU_SRD_Form_C', 'SRD-05VDC-SL-C', 'Relay_THT:Relay_SPDT_SANYOU_SRD_Series_Form_C',
|
||||
{'1': 'L_F', '2': 'RELAY_C', '3': 'L_SW', '4': None, '5': '+5V'}, (29.36, 48.0, 90),
|
||||
desc='Songle SRD-05VDC-SL-C 10 A/250 V, Schließer (NO) schaltet Pumpe', ncname={'4': ''})
|
||||
P('T1', PROJECT + ':ZMCT103C', 'ZMCT103C', PROJECT + ':ZMCT103C_Drahtbruecke',
|
||||
{'1': 'CT_A', '2': 'VMID', '3': 'L_SW', '4': 'L_PUMP'}, (50.0, 38.0, 90),
|
||||
desc='Stromwandler 5A/5mA (1000:1). Pumpen-L als isolierte Drahtbrücke (H07V-K 1,5 mm²) durchs Loch: Pad L_SW -> Loch -> Pad L_PUMP')
|
||||
|
||||
# ================= Versorgung 5 V =================
|
||||
P('J1', 'Connector:Screw_Terminal_01x02', 'VIN 5V/3A (+/-)', FP_T2, {'1': '+5V', '2': 'GND'}, (84.44, 89.5, 0), mpn=TERM,
|
||||
desc='5-V-Netzteil >= 3 A (Servo MG996R zieht bis 2,5 A)')
|
||||
C('C1', '1000u/10V', '+5V', 'GND', (90.0, 79.0, 0), fp=FP_CP10, sym='Device:C_Polarized', desc='Puffer für Servo-Anlaufstrom')
|
||||
C('C2', '100n', '+5V', 'GND', (85.5, 40.5, 0))
|
||||
R('R4', '2k2', '+5V', 'LED5_A', (77.0, 47.0, 0))
|
||||
P('D3', 'Device:LED', 'LED grün', FP_LED, {'1': 'GND', '2': 'LED5_A'}, (91.5, 47.0, 180))
|
||||
|
||||
# ================= Wemos =================
|
||||
P('A1', PROJECT + ':WEMOS_D1_mini', 'WEMOS D1 mini', PROJECT + ':WEMOS_D1_mini_Socket', {
|
||||
'1': None, '2': 'A0', '3': 'RELAY_G', '4': 'SCK', '5': 'MISO', '6': 'PWM_G', '7': 'TC_CS', '8': '+3V3',
|
||||
'9': 'TX', '10': 'FB', '11': 'SCL', '12': 'SDA', '13': 'SERVO_G', '14': 'OW', '15': 'GND', '16': '+5V'},
|
||||
(70.0, 16.0, 0), desc='auf 2x 1x8 Buchsenleiste 2,54 mm', ncname={'1': 'RST'})
|
||||
C('C3', '10u', '+3V3', 'GND', (77.0, 40.5, 0), fp=FP_CP5, sym='Device:C_Polarized')
|
||||
C('C4', '100n', '+3V3', 'GND', (69.0, 40.5, 0))
|
||||
|
||||
# ================= Relaistreiber =================
|
||||
R('R1', '1k', 'RELAY_G', 'RELAY_B', (15.0, 61.0, 0))
|
||||
R('R2', '10k', 'RELAY_B', 'GND', (15.0, 64.5, 0), desc='hält das Relais beim Booten aus')
|
||||
P('Q1', 'Transistor_BJT:Q_NPN_CBE', 'BC546B', FP_Q, {'1': 'RELAY_C', '2': 'RELAY_B', '3': 'GND'}, (26.5, 63.0, 0))
|
||||
P('D1', 'Device:D', '1N4007', FP_D41, {'1': '+5V', '2': 'RELAY_C'}, (23.0, 57.0, 0), desc='Freilaufdiode Relaisspule')
|
||||
R('R3', '1k', '+5V', 'LEDK_A', (34.0, 66.0, 0))
|
||||
P('D2', 'Device:LED', 'LED rot', FP_LED, {'1': 'RELAY_C', '2': 'LEDK_A'}, (37.0, 61.5, 0), desc='leuchtet, wenn das Relais anzieht')
|
||||
|
||||
# ================= Strommessung (ZMCT103C -> ADS1115 A0-A1 differenziell) =================
|
||||
R('R5', '100', 'CT_A', 'VMID', (44.0, 58.5, 0), desc='Bürde: 1 A Pumpenstrom = 100 mV eff.')
|
||||
R('R6', '10k', '+3V3', 'VMID', (44.0, 62.0, 0))
|
||||
R('R7', '10k', 'VMID', 'GND', (44.0, 65.5, 0))
|
||||
C('C5', '10u', 'VMID', 'GND', (56.0, 60.0, 0), fp=FP_CP5, sym='Device:C_Polarized')
|
||||
C('C6', '100n', 'VMID', 'GND', (55.0, 65.5, 0))
|
||||
P('A2', PROJECT + ':ADS1115_Modul', 'ADS1115 Modul', PROJECT + ':ADS1115_Module_1x10', {
|
||||
'1': '+3V3', '2': 'GND', '3': 'SCL', '4': 'SDA', '5': 'GND', '6': None, '7': 'CT_A', '8': 'VMID', '9': 'ADS_A2', '10': 'ADS_A3'},
|
||||
(16.0, 81.5, 0), desc='I2C 0x48 (ADDR=GND), auf 1x10 Buchsenleiste', ncname={'6': 'ALRT'})
|
||||
|
||||
# ================= Abgastemperatur (MAX6675-Modul, Typ K) =================
|
||||
P('A3', PROJECT + ':MAX6675_Modul', 'MAX6675 Modul', PROJECT + ':MAX6675_Module_1x5',
|
||||
{'1': 'GND', '2': '+3V3', '3': 'SCK', '4': 'TC_CS', '5': 'MISO'}, (76.5, 58.5, 0),
|
||||
desc='Thermoelement Typ K direkt an die Schraubklemme des Moduls (Plattenrand). Pinfolge GND VCC SCK CS SO vor dem Einlöten prüfen')
|
||||
R('R9', '10k', 'TC_CS', 'GND', (72.5, 59.0, 90), desc='GPIO15 muss beim Booten low sein')
|
||||
C('C8', '100n', '+3V3', 'GND', (68.5, 64.0, 90))
|
||||
|
||||
# ================= 1-Wire DS18B20 (3 Klemmen, ein Bus) =================
|
||||
for i, xyr in enumerate(((11.0, 89.5, 0), (29.36, 89.5, 0), (10.5, 58.0, 270))):
|
||||
P(f'J{5+i}', 'Connector:Screw_Terminal_01x03', 'DS18B20 (GND/DQ/3V3)', FP_T3, {'1': 'GND', '2': 'OW', '3': '+3V3'}, xyr, mpn=TERM,
|
||||
desc='DS18B20: schwarz GND, gelb DQ, rot VDD')
|
||||
R('R8', '4k7', '+3V3', 'OW', (3.5, 76.0, 0))
|
||||
C('C7', '100n', '+3V3', 'GND', (3.5, 80.0, 0))
|
||||
|
||||
# ================= Luftklappe: Servo MG996R =================
|
||||
P('J8', 'Connector:Screw_Terminal_01x03', 'KLAPPE SERVO (GND/5V/SIG)', FP_T3, {'1': 'GND', '2': '+5V', '3': 'SERVO'}, (47.72, 89.5, 0), mpn=TERM,
|
||||
desc='MG996R: braun GND, rot +5V, orange Signal')
|
||||
R('R10', '330', 'SERVO_G', 'SERVO', (47.0, 83.0, 0), desc='Schutzwiderstand Signal (3,3-V-Pegel reicht für MG996R)')
|
||||
C('C9', '470u/10V', '+5V', 'GND', (51.0, 76.5, 0), fp=FP_CP8, sym='Device:C_Polarized', desc='Puffer direkt an der Servoklemme')
|
||||
C('C10', '100n', '+5V', 'GND', (44.0, 76.5, 90))
|
||||
|
||||
# ================= PWM-Pumpe (optional, wie Umwälzpumpe) =================
|
||||
P('J9', 'Connector:Screw_Terminal_01x03', 'PWM-PUMPE (GND/FB/PWM)', FP_T3, {'1': 'GND', '2': 'FB_IN', '3': 'PWM_OUT'}, (66.08, 89.5, 0), mpn=TERM,
|
||||
desc='Signalkabel PWM-Hocheffizienzpumpe (Profil H/S), wie Umwälzpumpe')
|
||||
R('R11', '1k', 'PWM_G', 'PWM_B', (73.0, 84.0, 90))
|
||||
R('R12', '10k', 'PWM_B', 'GND', (76.5, 84.0, 90))
|
||||
P('Q2', 'Transistor_BJT:Q_NPN_CBE', 'BC546B', FP_Q, {'1': 'PWM_OUT', '2': 'PWM_B', '3': 'GND'}, (80.0, 82.0, 0))
|
||||
R('R13', '470', 'PWM_PU', 'PWM_OUT', (69.5, 84.0, 90))
|
||||
P('JP1', 'Jumper:Jumper_2_Open', 'Pull-up 5V', FP_H2, {'1': '+5V', '2': 'PWM_PU'}, (66.0, 78.5, 0),
|
||||
desc='gesteckt: Ausgang 0/5 V (VTS/AMG), offen: Open-Collector -> ESP aus = Pumpe nach Bedienfeld')
|
||||
R('R14', '220', 'FB_IN', 'FB', (62.5, 84.0, 90))
|
||||
R('R15', '4k7', '+3V3', 'FB', (59.0, 84.0, 90))
|
||||
|
||||
# ================= Erweiterung =================
|
||||
P('J10', 'Connector_Generic:Conn_01x09', 'ERWEITERUNG', 'Connector_PinHeader_2.54mm:PinHeader_1x09_P2.54mm_Vertical', {
|
||||
'1': '+5V', '2': '+3V3', '3': 'GND', '4': 'SDA', '5': 'SCL', '6': 'A0', '7': 'TX', '8': 'ADS_A2', '9': 'ADS_A3'},
|
||||
(97.5, 32.0, 0), desc='I2C, ESP A0, TX (GPIO1), ADS1115 A2/A3 für spätere Sensoren')
|
||||
|
||||
for i, (x, y) in enumerate([(3.5, 3.5), (96.5, 3.5), (3.5, 96.5), (97.1, 97.1)]):
|
||||
P(f'H{i+1}', 'Mechanical:MountingHole', 'M3', 'MountingHole:MountingHole_3.2mm_M3', {}, (x, y, 0))
|
||||
# PWR_FLAGs (nur Schaltplan)
|
||||
for i, net in enumerate(('+5V', 'GND')):
|
||||
P(f'#FLG0{i+1}', 'power:PWR_FLAG', 'PWR_FLAG', '', {'1': net}, None, board=False)
|
||||
|
||||
NOCONNECT = {('A1', '1'), ('A2', '6'), ('K1', '4')}
|
||||
|
||||
# ---------- 230-V-Bereich ----------
|
||||
# Polygon des Netzspannungsbereichs (keine GND-Fläche; beim Routen der Kleinspannung gesperrt)
|
||||
MAINS_AREA = [(0.6, 0.6), (66.3, 0.6), (66.3, 53.5), (0.6, 53.5)]
|
||||
# Kleinspannungs-Stummel, die aus dem 230-V-Bereich herausführen (vorverlegt): (net, [(x,y),...])
|
||||
LV_STUBS = [] # in gen_pcb aus den Padpositionen berechnet
|
||||
|
||||
# ---------- Bestückungsdruck ----------
|
||||
TERMLBL = {
|
||||
'J1': ('5V 3A', ['+', '-']), 'J2': ('NETZ 230V', ['L', 'N', 'PE']), 'J3': ('PUMPE', ['PE', 'N', 'L']),
|
||||
'J4': ('THERMOSTAT', ['Ls', 'L']),
|
||||
'J5': ('DS18B20', ['GND', 'DQ', '3V3']), 'J6': ('DS18B20', ['GND', 'DQ', '3V3']), 'J7': ('DS18B20', ['GND', 'DQ', '3V3']),
|
||||
'J8': ('KLAPPE', ['GND', '5V', 'SIG']), 'J9': ('PWM-PUMPE', ['GND', 'FB', 'PWM']),
|
||||
}
|
||||
SILK = []
|
||||
for p in PARTS:
|
||||
if p['ref'] in TERMLBL:
|
||||
name, pins = TERMLBL[p['ref']]
|
||||
x0, y0, rot = p['pcb']
|
||||
if rot == 270: # linke Kante, Pins nach unten
|
||||
ys = [y0 + 5.08 * i for i in range(len(pins))]
|
||||
for y, t in zip(ys, pins):
|
||||
SILK.append((t, x0 + 3.6, y, 0.8))
|
||||
SILK.append((name, x0 - 5.5, sum(ys) / len(ys), 1.1, 'F.SilkS', 90, True))
|
||||
continue
|
||||
if rot == 90: # rechte Kante, Pin 1 unten, Pins nach oben
|
||||
ys = [y0 - 5.08 * i for i in range(len(pins))]
|
||||
for y, t in zip(ys, pins):
|
||||
SILK.append((t, x0 - 6.0, y, 0.9))
|
||||
SILK.append((name, x0 - 1.0, ys[-1] - 4.8, 1.1, 'F.SilkS', 0, True))
|
||||
continue
|
||||
sgn = 1 if rot == 0 else -1
|
||||
xs = [x0 + sgn * 5.08 * i for i in range(len(pins))]
|
||||
ly = y0 + (4.05 if rot == 180 else -3.0)
|
||||
gy = y0 + (-6.0 if rot == 180 else -5.0)
|
||||
for x, t in zip(xs, pins):
|
||||
SILK.append((t, x, ly, 0.9))
|
||||
if rot == 180:
|
||||
SILK.append((name, sum(xs) / len(xs), ly + 2.0, 1.1, 'F.SilkS', 0, True))
|
||||
else:
|
||||
SILK.append((name, sum(xs) / len(xs), gy, 1.1, 'F.SilkS', 0, True))
|
||||
for i, t in enumerate(['5V', '3V3', 'GND', 'SDA', 'SCL', 'A0', 'TX', 'AD2', 'AD3']):
|
||||
SILK.append((t, 94.4, 32.0 + 2.54 * i, 0.8, 'F.SilkS', 0))
|
||||
SILK += [
|
||||
('OFENSTEUERUNG v1.0', 50, 97.0, 1.2, 'B.SilkS', 0, True),
|
||||
('!! 230 V !!', 9.0, 44.0, 1.5, 'F.SilkS', 0, True),
|
||||
('Draht L durchs Loch', 49.5, 27.5, 0.8),
|
||||
('T2A', 19.2, 28.6, 0.9),
|
||||
('PU', 66.0, 77.4, 0.8),
|
||||
('Typ K', 98.6, 63.5, 0.9, 'F.SilkS', 90),
|
||||
]
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
# Doku-Bilder: Bestückung, Platine (Kupfer), Schaltplan
|
||||
cd "$(dirname "$0")"; t=/tmp/docs_$$; mkdir -p $t
|
||||
kicad-cli pcb export svg --mode-single -l F.Silkscreen,F.Fab,Edge.Cuts --page-size-mode 2 --exclude-drawing-sheet -o $t/b.svg ofensteuerung.kicad_pcb >/dev/null 2>&1
|
||||
rsvg-convert -w 1400 -b white $t/b.svg -o docs_bestueckung.png
|
||||
kicad-cli pcb export svg --mode-single -l F.Cu,B.Cu,Edge.Cuts,F.Silkscreen --page-size-mode 2 --exclude-drawing-sheet -o $t/c.svg ofensteuerung.kicad_pcb >/dev/null 2>&1
|
||||
rsvg-convert -w 1400 -b white $t/c.svg -o docs_platine.png
|
||||
kicad-cli sch export svg -o $t ofensteuerung.kicad_sch >/dev/null 2>&1
|
||||
rsvg-convert -w 2400 -b white $t/ofensteuerung.svg -o docs_schaltplan.png
|
||||
kicad-cli pcb render --side top --width 1400 --height 1400 --quality basic -o docs_3d.png ofensteuerung.kicad_pcb >/dev/null 2>&1 || true
|
||||
rm -rf $t; ls -la docs_*.png
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 247 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 385 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fertigungsdaten: Gerber+Bohrdaten (zip), Stückliste mit Inventar-Fächern, Schaltplan-PDF, 1:1-Druck, Doku-Bilder."""
|
||||
import os, sys, csv, shutil, subprocess, zipfile, collections
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
from design import PARTS, PROJECT
|
||||
FAB = os.path.join(HERE, 'fab'); G = os.path.join(FAB, 'gerber')
|
||||
shutil.rmtree(G, ignore_errors=True); os.makedirs(G)
|
||||
pcb = os.path.join(HERE, PROJECT + '.kicad_pcb'); sch = os.path.join(HERE, PROJECT + '.kicad_sch')
|
||||
def run(*a):
|
||||
subprocess.run(a, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
run('kicad-cli', 'pcb', 'export', 'gerbers', '-o', G + '/', '--layers',
|
||||
'F.Cu,B.Cu,F.Silkscreen,B.Silkscreen,F.Mask,B.Mask,Edge.Cuts', '--subtract-soldermask', pcb)
|
||||
run('kicad-cli', 'pcb', 'export', 'drill', '-o', G + '/', '--format', 'excellon', '--excellon-separate-th', pcb)
|
||||
zp = os.path.join(FAB, PROJECT + '_gerber.zip')
|
||||
with zipfile.ZipFile(zp, 'w', zipfile.ZIP_DEFLATED) as z:
|
||||
for f in sorted(os.listdir(G)):
|
||||
z.write(os.path.join(G, f), f)
|
||||
run('kicad-cli', 'sch', 'export', 'pdf', '-o', os.path.join(FAB, PROJECT + '_schaltplan.pdf'), sch)
|
||||
run('kicad-cli', 'pcb', 'export', 'pdf', '--mode-single', '-l', 'F.Silkscreen,F.Fab,Edge.Cuts,F.Courtyard',
|
||||
'--include-border-title', '-o', os.path.join(FAB, PROJECT + '_1zu1_druck.pdf'), pcb)
|
||||
|
||||
# ---------- Stückliste ----------
|
||||
INV = { # (Wert, Footprint-Teil) -> Inventar-Fach (Parts_Inventory.xlsx)
|
||||
'100': 'E-01-3', '220': 'E-05-3', '330': 'E-07-3', '470': 'E-09-3', '1k': 'E-01-4', '2k2': 'E-05-4',
|
||||
'4k7': 'E-09-4', '10k': 'E-01-5', '100n': 'C-07-1', '10u': 'C-10-2 / D-04-3', '470u/10V': 'C-04-3',
|
||||
'1000u/10V': 'C-05-3', 'BC546B': 'B-01-4', '1N4007': 'B-02-1', 'LED grün': 'B-07-5', 'LED rot': 'B-08-5',
|
||||
'Pull-up 5V': 'A-07-2 + Jumper A-11-3', 'ERWEITERUNG': 'A-07-2',
|
||||
'SRD-05VDC-SL-C': 'A-07-1 prüfen (sonst kaufen)', 'T 2A': 'Clips/Halter B-12-2, Sicherung B-11-2',
|
||||
'WEMOS D1 mini': 'Buchsenleisten A-06-5 / A-07-3', 'ADS1115 Modul': 'Buchsenleiste A-06-5; Modul ggf. B-11-5',
|
||||
'MAX6675 Modul': 'Buchsenleiste A-06-5; Modul ggf. B-11-5',
|
||||
}
|
||||
BUY = {'ZMCT103C': 'kaufen (Stromwandler 5A/5mA)', 'MAX6675 Modul': 'kaufen inkl. Thermoelement Typ K (Abgasfühler)',
|
||||
'ADS1115 Modul': 'kaufen, falls kein Rest von der Umwälzpumpe', 'WEMOS D1 mini': 'vorhanden/kaufen'}
|
||||
rows = collections.OrderedDict()
|
||||
for p in PARTS:
|
||||
if p.get('board') is False or p['ref'].startswith('H'):
|
||||
continue
|
||||
key = (p['val'], p['fp'].split(':')[-1])
|
||||
rows.setdefault(key, []).append(p)
|
||||
with open(os.path.join(FAB, PROJECT + '_bom.csv'), 'w', newline='') as f:
|
||||
w = csv.writer(f, delimiter=';')
|
||||
w.writerow(['Anzahl', 'Referenzen', 'Wert', 'Footprint', 'Inventar-Fach', 'Beschaffung / Hinweis'])
|
||||
for (val, fp), ps in rows.items():
|
||||
refs = ' '.join(sorted((q['ref'] for q in ps), key=lambda r: (r.rstrip('0123456789'), int(''.join(c for c in r if c.isdigit()) or 0))))
|
||||
term = ps[0]['fp'].startswith('Connector_Phoenix')
|
||||
inv = '' if term else INV.get(val, '')
|
||||
hint = ps[0].get('mpn') or BUY.get(val, '')
|
||||
w.writerow([len(ps), refs, val, fp, inv, hint])
|
||||
print('fab ok:', sorted(os.listdir(FAB)))
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
Anzahl;Referenzen;Wert;Footprint;Inventar-Fach;Beschaffung / Hinweis
|
||||
1;J2;NETZ 230V (PE/N/L);PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal;;Steckklemme 5,08 mm, 250 V / 12 A (Phoenix MSTBA 2,5/x-G-5,08 + MSTB 2,5/x-ST-5,08)
|
||||
1;J4;THERMOSTAT (L/Ls);PhoenixContact_MSTBA_2,5_2-G-5,08_1x02_P5.08mm_Horizontal;;Steckklemme 5,08 mm, 250 V / 12 A (Phoenix MSTBA 2,5/x-G-5,08 + MSTB 2,5/x-ST-5,08)
|
||||
1;J3;PUMPE 230V (L/N/PE);PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal;;Steckklemme 5,08 mm, 250 V / 12 A (Phoenix MSTBA 2,5/x-G-5,08 + MSTB 2,5/x-ST-5,08)
|
||||
1;F1;T 2A;Fuseholder_Clip-5x20mm_Littelfuse_111_Inline_P20.00x5.00mm_D1.05mm_Horizontal;Clips/Halter B-12-2, Sicherung B-11-2;2x Sicherungsclip 5x20 (oder Halter aus B-12-2)
|
||||
1;K1;SRD-05VDC-SL-C;Relay_SPDT_SANYOU_SRD_Series_Form_C;A-07-1 prüfen (sonst kaufen);
|
||||
1;T1;ZMCT103C;ZMCT103C_Drahtbruecke;;kaufen (Stromwandler 5A/5mA)
|
||||
1;J1;VIN 5V/3A (+/-);PhoenixContact_MSTBA_2,5_2-G-5,08_1x02_P5.08mm_Horizontal;;Steckklemme 5,08 mm (Phoenix MSTBA/MSTB oder 2EDGR/2EDGK kompatibel)
|
||||
1;C1;1000u/10V;CP_Radial_D10.0mm_P5.00mm;C-05-3;
|
||||
6;C2 C4 C6 C7 C8 C10;100n;C_Disc_D5.0mm_W2.5mm_P5.00mm;C-07-1;
|
||||
1;R4;2k2;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-05-4;
|
||||
1;D3;LED grün;LED_D3.0mm;B-07-5;
|
||||
1;A1;WEMOS D1 mini;WEMOS_D1_mini_Socket;Buchsenleisten A-06-5 / A-07-3;vorhanden/kaufen
|
||||
2;C3 C5;10u;CP_Radial_D5.0mm_P2.00mm;C-10-2 / D-04-3;
|
||||
3;R1 R3 R11;1k;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-01-4;
|
||||
5;R2 R6 R7 R9 R12;10k;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-01-5;
|
||||
2;Q1 Q2;BC546B;TO-92_Inline_Wide;B-01-4;
|
||||
1;D1;1N4007;D_DO-41_SOD81_P10.16mm_Horizontal;B-02-1;
|
||||
1;D2;LED rot;LED_D3.0mm;B-08-5;
|
||||
1;R5;100;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-01-3;
|
||||
1;A2;ADS1115 Modul;ADS1115_Module_1x10;"Buchsenleiste A-06-5; Modul ggf. B-11-5";kaufen, falls kein Rest von der Umwälzpumpe
|
||||
1;A3;MAX6675 Modul;MAX6675_Module_1x5;"Buchsenleiste A-06-5; Modul ggf. B-11-5";kaufen inkl. Thermoelement Typ K (Abgasfühler)
|
||||
3;J5 J6 J7;DS18B20 (GND/DQ/3V3);PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal;;Steckklemme 5,08 mm (Phoenix MSTBA/MSTB oder 2EDGR/2EDGK kompatibel)
|
||||
2;R8 R15;4k7;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-09-4;
|
||||
1;J8;KLAPPE SERVO (GND/5V/SIG);PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal;;Steckklemme 5,08 mm (Phoenix MSTBA/MSTB oder 2EDGR/2EDGK kompatibel)
|
||||
1;R10;330;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-07-3;
|
||||
1;C9;470u/10V;CP_Radial_D8.0mm_P3.50mm;C-04-3;
|
||||
1;J9;PWM-PUMPE (GND/FB/PWM);PhoenixContact_MSTBA_2,5_3-G-5,08_1x03_P5.08mm_Horizontal;;Steckklemme 5,08 mm (Phoenix MSTBA/MSTB oder 2EDGR/2EDGK kompatibel)
|
||||
1;R13;470;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-09-3;
|
||||
1;JP1;Pull-up 5V;PinHeader_1x02_P2.54mm_Vertical;A-07-2 + Jumper A-11-3;
|
||||
1;R14;220;R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal;E-05-3;
|
||||
1;J10;ERWEITERUNG;PinHeader_1x09_P2.54mm_Vertical;A-07-2;
|
||||
|
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
(fp_lib_table (version 7)
|
||||
(lib (name "ofensteuerung")(type "KiCad")(uri "${KIPRJMOD}/ofensteuerung.pretty")(options "")(descr ""))
|
||||
)
|
||||
+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')
|
||||
+581
@@ -0,0 +1,581 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Erzeugt Schaltplan (.kicad_sch) + Projekt-Libs aus design.py (eine Quelle für Schaltplan und Platine)."""
|
||||
import re, uuid, os, sys, json
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from design import PARTS, PROJECT, NOCONNECT
|
||||
|
||||
SYMDIR = '/usr/share/kicad/symbols'
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
U = lambda: str(uuid.uuid5(uuid.NAMESPACE_URL, PROJECT + ':' + '|'.join(map(str, _ctr.__next__()))))
|
||||
def _gen():
|
||||
i = 0
|
||||
while True:
|
||||
i += 1; yield (i,)
|
||||
_ctr = _gen()
|
||||
|
||||
# ---------- minimal s-expression helpers ----------
|
||||
def tokenize(s):
|
||||
return re.findall(r'\(|\)|"(?:\\.|[^"\\])*"|[^\s()"]+', s)
|
||||
|
||||
def parse(tokens, i=0):
|
||||
out = []
|
||||
while i < len(tokens):
|
||||
t = tokens[i]
|
||||
if t == '(':
|
||||
sub, i = parse(tokens, i + 1); out.append(sub)
|
||||
elif t == ')':
|
||||
return out, i + 1
|
||||
else:
|
||||
out.append(t); i += 1
|
||||
return out, i
|
||||
|
||||
def dump(x, ind=0):
|
||||
if not isinstance(x, list):
|
||||
return x
|
||||
if all(not isinstance(e, list) for e in x):
|
||||
return '(' + ' '.join(x) + ')'
|
||||
s = '(' + ' '.join(e for e in x[:2] if not isinstance(e, list))
|
||||
rest = x[2:] if len(x) > 1 and not isinstance(x[1], list) else x[1:]
|
||||
if isinstance(x[1], list):
|
||||
s = '(' + x[0]; rest = x[1:]
|
||||
for e in rest:
|
||||
s += '\n' + ' ' * (ind + 1) + (dump(e, ind + 1) if isinstance(e, list) else e)
|
||||
return s + ')'
|
||||
|
||||
def q(s):
|
||||
return '"' + str(s).replace('\\', '\\\\').replace('"', '\\"') + '"'
|
||||
|
||||
_libcache = {}
|
||||
def lib_symbol(libid):
|
||||
lib, name = libid.split(':')
|
||||
if lib == PROJECT:
|
||||
path = os.path.join(HERE, PROJECT + '.kicad_sym')
|
||||
else:
|
||||
path = os.path.join(SYMDIR, lib + '.kicad_sym')
|
||||
if path not in _libcache:
|
||||
_libcache[path] = parse(tokenize(open(path).read()))[0][0]
|
||||
for e in _libcache[path]:
|
||||
if isinstance(e, list) and e[0] == 'symbol' and e[1] == q(name):
|
||||
if any(isinstance(k, list) and k[0] == 'extends' for k in e):
|
||||
raise SystemExit('extends not supported: ' + libid)
|
||||
import copy
|
||||
s = copy.deepcopy(e); s[1] = q(libid)
|
||||
return s
|
||||
raise SystemExit('symbol not found ' + libid)
|
||||
|
||||
def pins_of(sym):
|
||||
res = {}
|
||||
def walk(x):
|
||||
for e in x:
|
||||
if isinstance(e, list):
|
||||
if e[0] == 'pin':
|
||||
at = next(k for k in e if isinstance(k, list) and k[0] == 'at')
|
||||
num = next(k for k in e if isinstance(k, list) and k[0] == 'number')[1].strip('"')
|
||||
res[num] = (float(at[1]), float(at[2]), int(float(at[3])) if len(at) > 3 else 0)
|
||||
else:
|
||||
walk(e)
|
||||
walk(sym)
|
||||
return res
|
||||
|
||||
# ---------- project symbol library (Wemos, ADS1115 module) ----------
|
||||
def rect_symbol(name, ref, left, right, w=15.24, desc=''):
|
||||
"""left/right: list of (number, name, type). pins 2.54 apart."""
|
||||
n = max(len(left), len(right))
|
||||
h = (n + 1) * 2.54
|
||||
top = h / 2
|
||||
pins = []
|
||||
for i, (num, nm, tp) in enumerate(left):
|
||||
y = top - 2.54 * (i + 1)
|
||||
pins.append(f'(pin {tp} line (at {-w/2-2.54:.2f} {y:.2f} 0) (length 2.54) (name {q(nm)} (effects (font (size 1.27 1.27)))) (number {q(num)} (effects (font (size 1.27 1.27)))))')
|
||||
for i, (num, nm, tp) in enumerate(right):
|
||||
y = top - 2.54 * (i + 1)
|
||||
pins.append(f'(pin {tp} line (at {w/2+2.54:.2f} {y:.2f} 180) (length 2.54) (name {q(nm)} (effects (font (size 1.27 1.27)))) (number {q(num)} (effects (font (size 1.27 1.27)))))')
|
||||
return f'''(symbol {q(name)} (pin_names (offset 1.016)) (exclude_from_sim no) (in_bom yes) (on_board yes)
|
||||
(property "Reference" {q(ref)} (at 0 {top+1.27:.2f} 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Value" {q(name)} (at 0 {-top-1.27:.2f} 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Description" {q(desc)} (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(symbol {q(name + '_0_1')} (rectangle (start {-w/2:.2f} {top:.2f}) (end {w/2:.2f} {-top:.2f}) (stroke (width 0.254) (type default)) (fill (type background))))
|
||||
(symbol {q(name + '_1_1')} {' '.join(pins)}))'''
|
||||
|
||||
def write_projlib():
|
||||
wemos = rect_symbol('WEMOS_D1_mini', 'A',
|
||||
[('1', 'RST', 'input'), ('2', 'A0', 'input'), ('3', 'D0/GPIO16', 'bidirectional'), ('4', 'D5/GPIO14', 'bidirectional'),
|
||||
('5', 'D6/GPIO12', 'bidirectional'), ('6', 'D7/GPIO13', 'bidirectional'), ('7', 'D8/GPIO15', 'bidirectional'), ('8', '3V3', 'power_out')],
|
||||
[('9', 'TX/GPIO1', 'bidirectional'), ('10', 'RX/GPIO3', 'bidirectional'), ('11', 'D1/GPIO5', 'bidirectional'), ('12', 'D2/GPIO4', 'bidirectional'),
|
||||
('13', 'D3/GPIO0', 'bidirectional'), ('14', 'D4/GPIO2', 'bidirectional'), ('15', 'GND', 'power_in'), ('16', '5V', 'power_in')],
|
||||
w=30.48, desc='Wemos D1 mini (ESP8266) auf 2x 1x8 Buchsenleisten')
|
||||
ads = rect_symbol('ADS1115_Modul', 'A',
|
||||
[('1', 'VDD', 'power_in'), ('2', 'GND', 'power_in'), ('3', 'SCL', 'input'), ('4', 'SDA', 'bidirectional'), ('5', 'ADDR', 'input')],
|
||||
[('6', 'ALRT', 'output'), ('7', 'A0', 'input'), ('8', 'A1', 'input'), ('9', 'A2', 'input'), ('10', 'A3', 'input')],
|
||||
w=12.7, desc='ADS1115 16-bit ADC Breakout (HW-ADS1115/CJMCU, 1x10 Pin)')
|
||||
ct = rect_symbol('ZMCT103C', 'T',
|
||||
[('3', 'P1 (L_SW)', 'passive'), ('4', 'P2 (L_PUMP)', 'passive')],
|
||||
[('1', 'S1', 'passive'), ('2', 'S2', 'passive')],
|
||||
w=15.24, desc='Stromwandler ZMCT103C 1000:1, Primär = Drahtbrücke durchs Loch')
|
||||
mx = rect_symbol('MAX6675_Modul', 'A',
|
||||
[('1', 'GND', 'power_in'), ('2', 'VCC', 'power_in'), ('3', 'SCK', 'input'), ('4', 'CS', 'input'), ('5', 'SO', 'output')],
|
||||
[], w=12.7, desc='MAX6675 Thermoelement-Modul (Typ K), 1x5 Pin')
|
||||
open(os.path.join(HERE, PROJECT + '.kicad_sym'), 'w').write(
|
||||
'(kicad_symbol_lib (version 20231120) (generator "gen_sch.py")\n' + wemos + '\n' + ads + '\n' + ct + '\n' + mx + '\n)\n')
|
||||
|
||||
# ---------- geometry ----------
|
||||
import math, heapq
|
||||
from sch_layout import BLOCKS, LABELS, PSYM, NOTES as LNOTES
|
||||
|
||||
GRID = 1.27
|
||||
POWERNETS = ('GND', '+5V', '+3V3')
|
||||
|
||||
def pin_lengths(sym):
|
||||
res = {}
|
||||
def walk(x):
|
||||
for e in x:
|
||||
if isinstance(e, list):
|
||||
if e[0] == 'pin':
|
||||
num = next(k for k in e if isinstance(k, list) and k[0] == 'number')[1].strip('"')
|
||||
ln = next((k for k in e if isinstance(k, list) and k[0] == 'length'), ['length', '2.54'])
|
||||
res[num] = float(ln[1])
|
||||
else:
|
||||
walk(e)
|
||||
walk(sym)
|
||||
return res
|
||||
|
||||
def graphic_points(sym):
|
||||
pts = []
|
||||
def walk(x):
|
||||
for e in x:
|
||||
if isinstance(e, list):
|
||||
if e[0] in ('rectangle',):
|
||||
for k in e:
|
||||
if isinstance(k, list) and k[0] in ('start', 'end'):
|
||||
pts.append((float(k[1]), float(k[2])))
|
||||
elif e[0] in ('polyline', 'bezier'):
|
||||
for k in e:
|
||||
if isinstance(k, list) and k[0] == 'pts':
|
||||
pts.extend((float(p[1]), float(p[2])) for p in k[1:])
|
||||
elif e[0] == 'circle':
|
||||
c = next(k for k in e if isinstance(k, list) and k[0] == 'center')
|
||||
r = float(next(k for k in e if isinstance(k, list) and k[0] == 'radius')[1])
|
||||
pts.extend([(float(c[1]) - r, float(c[2]) - r), (float(c[1]) + r, float(c[2]) + r)])
|
||||
elif e[0] == 'arc':
|
||||
for k in e:
|
||||
if isinstance(k, list) and k[0] in ('start', 'mid', 'end'):
|
||||
pts.append((float(k[1]), float(k[2])))
|
||||
elif e[0] in ('pin', 'property'):
|
||||
pass
|
||||
else:
|
||||
walk(e)
|
||||
walk(sym)
|
||||
return pts
|
||||
|
||||
def xf(px, py, rot, mirror):
|
||||
"""lib point (y up) -> screen offset (y down)"""
|
||||
x, y = px, -py
|
||||
if mirror == 'y':
|
||||
x = -x
|
||||
elif mirror == 'x':
|
||||
y = -y
|
||||
r = math.radians(rot)
|
||||
c, s = round(math.cos(r)), round(math.sin(r))
|
||||
return (x * c + y * s, -x * s + y * c)
|
||||
|
||||
DIRV = {0: (1, 0), 90: (0, -1), 180: (-1, 0), 270: (0, 1)} # screen
|
||||
def vdir(v):
|
||||
return {(1, 0): 0, (0, -1): 90, (-1, 0): 180, (0, 1): 270}[(int(round(v[0])), int(round(v[1])))]
|
||||
|
||||
def G(v):
|
||||
return int(round(v / GRID))
|
||||
|
||||
# ---------- router ----------
|
||||
class Grid:
|
||||
def __init__(self):
|
||||
self.block = {} # cell -> 'hard'
|
||||
self.soft = set()
|
||||
self.occ = {} # cell -> {net: set(dirs 'h'/'v')}
|
||||
self.nodes = {} # cell -> net (pins, label anchors, wire corners/ends: no foreign wire may pass)
|
||||
self.edges = {} # net -> set of (cellA, cellB)
|
||||
|
||||
def add_edge(self, net, a, b):
|
||||
e = (min(a, b), max(a, b))
|
||||
self.edges.setdefault(net, set()).add(e)
|
||||
d = 'h' if a[1] == b[1] else 'v'
|
||||
for c in (a, b):
|
||||
self.occ.setdefault(c, {}).setdefault(net, set()).add(d)
|
||||
|
||||
def tree(self, net):
|
||||
cells = set()
|
||||
for a, b in self.edges.get(net, ()):
|
||||
cells.add(a); cells.add(b)
|
||||
cells |= {c for c, n in self.nodes.items() if n == net}
|
||||
return cells
|
||||
|
||||
def passable(self, c, net, d):
|
||||
"""may net pass straight through c in direction d ('h'/'v')?"""
|
||||
if c in self.block:
|
||||
return False
|
||||
n = self.nodes.get(c)
|
||||
if n is not None and n != net:
|
||||
return False
|
||||
o = self.occ.get(c, {})
|
||||
for other, dirs in o.items():
|
||||
if other == net:
|
||||
continue
|
||||
if d in dirs or len(dirs) != 1:
|
||||
return False
|
||||
# other must go straight through (2 edges same axis)
|
||||
deg = sum(1 for (a, b) in self.edges[other] if a == c or b == c)
|
||||
if deg != 2:
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_turn_or_end(self, c, net):
|
||||
o = self.occ.get(c, {})
|
||||
return all(other == net for other in o) and self.nodes.get(c, net) == net and c not in self.block
|
||||
|
||||
def route(self, net, src, targets, bounds):
|
||||
"""A* from src to any target cell; returns list of cells"""
|
||||
(x0, y0, x1, y1) = bounds
|
||||
def h(c):
|
||||
return min(abs(c[0] - t[0]) + abs(c[1] - t[1]) for t in targets) if len(targets) < 40 else 0
|
||||
start = (src, None)
|
||||
pq = [(h(src), 0, src, None)]
|
||||
best = {start: 0}
|
||||
prev = {}
|
||||
while pq:
|
||||
f, g, c, d = heapq.heappop(pq)
|
||||
if g > best.get((c, d), 1e9):
|
||||
continue
|
||||
if c in targets and c != src:
|
||||
if self.can_turn_or_end(c, net) or (d is not None and self._t_ok(c, net)):
|
||||
path = [c]; k = (c, d)
|
||||
while k in prev:
|
||||
k = prev[k]; path.append(k[0])
|
||||
return path[::-1]
|
||||
for nd, (dx, dy) in DIRV.items():
|
||||
n = (c[0] + dx, c[1] + dy)
|
||||
if not (x0 <= n[0] <= x1 and y0 <= n[1] <= y1):
|
||||
continue
|
||||
if d is not None and nd != d and (nd - d) % 180 == 0:
|
||||
continue # no reversing
|
||||
turning = d is not None and nd != d
|
||||
if turning and c != src and not self.can_turn_or_end(c, net):
|
||||
continue
|
||||
if c == src and d is None:
|
||||
pass
|
||||
ax = 'h' if dx else 'v'
|
||||
if n in targets:
|
||||
cost = 1
|
||||
else:
|
||||
if not self.passable(n, net, ax):
|
||||
continue
|
||||
cost = 1 + (6 if n in self.soft else 0) + (3 if self.occ.get(n) else 0)
|
||||
cost += 4 if turning else 0
|
||||
ng = g + cost
|
||||
if ng < best.get((n, nd), 1e9):
|
||||
best[(n, nd)] = ng; prev[(n, nd)] = (c, d)
|
||||
heapq.heappush(pq, (ng + h(n), ng, n, nd))
|
||||
return None
|
||||
|
||||
def _t_ok(self, c, net):
|
||||
return self.can_turn_or_end(c, net)
|
||||
|
||||
# ---------- schematic ----------
|
||||
def main():
|
||||
write_projlib()
|
||||
root = str(uuid.uuid5(uuid.NAMESPACE_URL, PROJECT + ':root'))
|
||||
parts = {p['ref']: p for p in PARTS}
|
||||
place = {}
|
||||
blockof = {}
|
||||
for b, d in BLOCKS.items():
|
||||
for ref, v in d.items():
|
||||
place[ref] = v; blockof[ref] = b
|
||||
missing = set(parts) - set(place)
|
||||
if missing:
|
||||
raise SystemExit('nicht platziert: %s' % sorted(missing))
|
||||
|
||||
libsyms = {}
|
||||
grid = Grid()
|
||||
items = []
|
||||
symuuids = {}
|
||||
pinpos = {} # (ref, num) -> (cell, outward dir)
|
||||
textboxes = []
|
||||
|
||||
for ref, p in parts.items():
|
||||
lid = p['sym']
|
||||
if lid not in libsyms:
|
||||
libsyms[lid] = lib_symbol(lid)
|
||||
sym = libsyms[lid]
|
||||
x0, y0, rot, mir = place[ref]
|
||||
pins = pins_of(sym); plen = pin_lengths(sym)
|
||||
# body
|
||||
gp = [xf(px, py, rot, mir) for px, py in graphic_points(sym)]
|
||||
if gp:
|
||||
bx0 = min(v[0] for v in gp) + x0; bx1 = max(v[0] for v in gp) + x0
|
||||
by0 = min(v[1] for v in gp) + y0; by1 = max(v[1] for v in gp) + y0
|
||||
for i in range(G(bx0), G(bx1) + 1):
|
||||
for j in range(G(by0), G(by1) + 1):
|
||||
grid.block[(i, j)] = ref
|
||||
else:
|
||||
bx0 = bx1 = x0; by0 = by1 = y0
|
||||
for num, (px, py, ang) in pins.items():
|
||||
ox, oy = xf(px, py, rot, mir)
|
||||
cx, cy = x0 + ox, y0 + oy
|
||||
ldx, ldy = xf(math.cos(math.radians(ang)), math.sin(math.radians(ang)), rot, mir) # toward body
|
||||
out = vdir((-ldx, -ldy))
|
||||
cell = (G(cx), G(cy))
|
||||
pinpos[(ref, num)] = (cell, out)
|
||||
# pin line cells (between endpoint and body) are blocked
|
||||
L = int(round(plen.get(num, 2.54) / GRID))
|
||||
for k in range(1, L + 1):
|
||||
grid.block[(cell[0] + int(round(ldx)) * k, cell[1] + int(round(ldy)) * k)] = ref
|
||||
grid.nodes[cell] = None # filled with net below
|
||||
su = str(uuid.uuid5(uuid.NAMESPACE_URL, PROJECT + ':sym:' + ref))
|
||||
symuuids[ref] = su
|
||||
# fields: right of body for vertical/narrow parts, above for wide parts
|
||||
pc = [xf(px, py, rot, mir) for px, py, a in pins.values()]
|
||||
wide = len(pc) == 2 and abs(pc[0][1] - pc[1][1]) < 0.1 and not lid.startswith(PROJECT)
|
||||
big = lid.startswith(PROJECT) or lid.startswith('Regulator') or lid.startswith('Connector') or lid.startswith('Jumper') or ref.startswith('H')
|
||||
if ref.startswith('#'):
|
||||
rpos = vpos = None
|
||||
elif big:
|
||||
rpos = (round((bx0 + bx1) / 2, 2), round(by0 - 3.81, 2), 'center'); vpos = (round((bx0 + bx1) / 2, 2), round(by0 - 1.6, 2), 'center')
|
||||
if ref.startswith('H'):
|
||||
rpos = (x0 + 3.81, y0 - 1.27, 'left'); vpos = (x0 + 3.81, y0 + 1.27, 'left')
|
||||
elif wide:
|
||||
rpos = (round((bx0 + bx1) / 2, 2), round(by0 - 1.9, 2), 'center'); vpos = (round((bx0 + bx1) / 2, 2), round(by1 + 2.0, 2), 'center')
|
||||
else:
|
||||
rpos = (round(bx1 + 1.6, 2), round((by0 + by1) / 2 - 0.8, 2), 'left'); vpos = (round(bx1 + 1.6, 2), round((by0 + by1) / 2 + 1.6, 2), 'left')
|
||||
for pos in (rpos, vpos):
|
||||
if pos:
|
||||
for i in range(G(pos[0]) - (3 if pos[2] == 'center' else 0), G(pos[0]) + 5):
|
||||
grid.soft.add((i, G(pos[1]))); grid.soft.add((i, G(pos[1]) - 1))
|
||||
props = [('Reference', ref, rpos), ('Value', p['val'], vpos), ('Footprint', p['fp'], None), ('Datasheet', '', None),
|
||||
('Description', p.get('desc', ''), None)]
|
||||
if p.get('mpn'):
|
||||
props.append(('Bezugsquelle', p['mpn'], None))
|
||||
pr = ''
|
||||
for k, v, pos in props:
|
||||
if pos is None:
|
||||
pr += f' (property {q(k)} {q(v)} (at {x0} {y0} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||
else:
|
||||
just = '' if pos[2] == 'center' else ' (justify left)'
|
||||
fa = 90 if rot in (90, 270) else 0
|
||||
pr += f' (property {q(k)} {q(v)} (at {pos[0]:.2f} {pos[1]:.2f} {fa}) (effects (font (size 1.27 1.27)){just}))\n'
|
||||
pinstr = ''.join(f' (pin {q(n)} (uuid {U()}))\n' for n in pins)
|
||||
dnp = 'yes' if p.get('dnp') else 'no'
|
||||
onb = 'no' if p.get('board') is False else 'yes'
|
||||
inbom = 'no' if ref.startswith('H') or ref.startswith('#') else 'yes'
|
||||
mirs = f' (mirror {mir})' if mir else ''
|
||||
items.append(f'''(symbol (lib_id {q(lid)}) (at {x0} {y0} {rot}){mirs} (unit 1) (exclude_from_sim no) (in_bom {inbom}) (on_board {onb}) (dnp {dnp}) (uuid {su})
|
||||
{pr}{pinstr} (instances (project {q(PROJECT)} (path {q('/' + root)} (reference {q(ref)}) (unit 1)))))''')
|
||||
|
||||
for (ref, num), (cell, _o) in pinpos.items():
|
||||
grid.block.pop(cell, None)
|
||||
dx, dy = DIRV[_o]
|
||||
for k in range(1, 4): # Grafik (z.B. LED-Pfeile) darf den Pinausgang nicht zustellen
|
||||
c2 = (cell[0] + dx * k, cell[1] + dy * k)
|
||||
if grid.block.get(c2) == ref:
|
||||
grid.block.pop(c2)
|
||||
if parts[ref]['sym'].startswith('power:'):
|
||||
for dx in (-1, 0, 1):
|
||||
grid.block.pop((cell[0] + dx, cell[1]), None)
|
||||
# nets -> pins grouped per block
|
||||
netpins = {}
|
||||
for ref, p in parts.items():
|
||||
for num, net in p['pins'].items():
|
||||
cell, out = pinpos[(ref, num)]
|
||||
if net is None:
|
||||
grid.nodes[cell] = '__nc__'
|
||||
cx, cy = cell[0] * GRID, cell[1] * GRID
|
||||
items.append(f'(no_connect (at {cx:.2f} {cy:.2f}) (uuid {U()}))')
|
||||
continue
|
||||
grid.nodes[cell] = net
|
||||
netpins.setdefault((net, blockof[ref] if net in POWERNETS else '*'), []).append((ref, num))
|
||||
for ref, p in parts.items(): # pins without a net entry (shouldn't exist)
|
||||
pass
|
||||
|
||||
def W(a, b):
|
||||
items.append(f'(wire (pts (xy {a[0]*GRID:.2f} {a[1]*GRID:.2f}) (xy {b[0]*GRID:.2f} {b[1]*GRID:.2f})) (stroke (width 0) (type default)) (uuid {U()}))')
|
||||
|
||||
pwr_n = [0]
|
||||
def power_symbol(net, cell, out=None):
|
||||
"""power symbol with its pin at cell. out = direction of the stub that leads to it (None = free)."""
|
||||
plid = 'power:' + net
|
||||
if plid not in libsyms:
|
||||
libsyms[plid] = lib_symbol(plid)
|
||||
if net == 'GND':
|
||||
rot = 180 if out == 90 else ((out - 270) % 360 if out in (0, 180) else 0)
|
||||
else:
|
||||
rot = 180 if out == 270 else ((out - 90) % 360 if out in (0, 180) else 0)
|
||||
pwr_n[0] += 1
|
||||
ref = '#PWR' + str(pwr_n[0]).zfill(3)
|
||||
ex, ey = cell[0] * GRID, cell[1] * GRID
|
||||
bodyd = ({0: 270, 90: 0, 180: 90, 270: 180} if net == 'GND' else {0: 90, 90: 180, 180: 270, 270: 0})[rot]
|
||||
bdx, bdy = DIRV[bodyd]
|
||||
tdx, tdy = bdx * (3.3 + 0.45 * len(net)), bdy * 4.0
|
||||
items.append(f'''(symbol (lib_id {q(plid)}) (at {ex:.2f} {ey:.2f} {rot}) (unit 1) (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid {U()})
|
||||
(property "Reference" {q(ref)} (at {ex:.2f} {ey:.2f} 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Value" {q(net)} (at {ex + tdx:.2f} {ey + tdy:.2f} {90 if rot in (90, 270) else 0}) (effects (font (size 1.0 1.0))))
|
||||
(property "Footprint" "" (at {ex:.2f} {ey:.2f} 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at {ex:.2f} {ey:.2f} 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid {U()}))
|
||||
(instances (project {q(PROJECT)} (path {q('/' + root)} (reference {q(ref)}) (unit 1)))))''')
|
||||
for k in range(1, 3):
|
||||
for w in (-1, 0, 1):
|
||||
c2 = (cell[0] + bdx * k + (w if bdy else 0), cell[1] + bdy * k + (w if bdx else 0))
|
||||
grid.block.setdefault(c2, 'pwr')
|
||||
|
||||
def label(net, cell, d):
|
||||
ang, just = {'right': (0, 'left'), 'left': (180, 'right'), 'up': (90, 'left'), 'down': (270, 'right')}[d]
|
||||
items.append(f'(label {q(net)} (at {cell[0]*GRID:.2f} {cell[1]*GRID:.2f} {ang}) (effects (font (size 1.27 1.27)) (justify {just} bottom)) (uuid {U()}))')
|
||||
grid.nodes[cell] = net
|
||||
dx, dy = DIRV[{'right': 0, 'left': 180, 'up': 90, 'down': 270}[d]]
|
||||
for k in range(1, len(net) + 2):
|
||||
grid.block.setdefault((cell[0] + dx * k, cell[1] + dy * k), 'lbl')
|
||||
if dx:
|
||||
grid.block.setdefault((cell[0] + dx * k, cell[1] - 1), 'lbl')
|
||||
|
||||
# explicit labels + power symbols become nodes of their (net, block)
|
||||
extra = {}
|
||||
for b, net, x, y, d in LABELS:
|
||||
c = (G(x), G(y)); label(net, c, d); extra.setdefault((net, b), []).append(c)
|
||||
for b, net, x, y in PSYM:
|
||||
c = (G(x), G(y)); grid.nodes[c] = net; power_symbol(net, c, None); extra.setdefault((net, b), []).append(c)
|
||||
|
||||
for (nx, ny, size, text) in LNOTES: # Überschriften nicht überfahren
|
||||
for i in range(G(nx) - 1, G(nx + len(text) * size * 0.95) + 2):
|
||||
for j in range(G(ny - size * 1.3), G(ny) + 2):
|
||||
grid.block.setdefault((i, j), 'note')
|
||||
|
||||
# 1) stubs for everything that is not wired inside its block
|
||||
wired = []
|
||||
for (net, b), pl in sorted(netpins.items()):
|
||||
nodes = [pinpos[x][0] for x in pl] + extra.get((net, b), [])
|
||||
if net in POWERNETS and (net, b) not in extra:
|
||||
for x in pl:
|
||||
cell, out = pinpos[x]
|
||||
dx, dy = DIRV[out]; e = (cell[0] + 2 * dx, cell[1] + 2 * dy)
|
||||
grid.add_edge(net, cell, (cell[0] + dx, cell[1] + dy)); grid.add_edge(net, (cell[0] + dx, cell[1] + dy), e)
|
||||
grid.nodes[e] = net
|
||||
W(cell, e); _emitted.add((net, cell, e)); power_symbol(net, e, out)
|
||||
elif len(nodes) == 1:
|
||||
cell, out = pinpos[pl[0]]
|
||||
dx, dy = DIRV[out]; e = (cell[0] + 2 * dx, cell[1] + 2 * dy)
|
||||
grid.add_edge(net, cell, (cell[0] + dx, cell[1] + dy)); grid.add_edge(net, (cell[0] + dx, cell[1] + dy), e)
|
||||
W(cell, e); _emitted.add((net, cell, e))
|
||||
label(net, e, {0: 'right', 180: 'left', 90: 'up', 270: 'down'}[out])
|
||||
else:
|
||||
wired.append((net, b, nodes, pl))
|
||||
|
||||
# 2) route wired nets: shortest nets first
|
||||
def span(n):
|
||||
xs = [c[0] for c in n[2]]; ys = [c[1] for c in n[2]]
|
||||
return (max(xs) - min(xs)) + (max(ys) - min(ys))
|
||||
failed = []
|
||||
for net, b, nodes, pl in sorted(wired, key=span):
|
||||
xs = [c[0] for c in nodes]; ys = [c[1] for c in nodes]
|
||||
bounds = (min(xs) - 40, min(ys) - 40, max(xs) + 40, max(ys) + 40)
|
||||
# start from the node nearest to the centroid
|
||||
cx, cy = sum(xs) / len(xs), sum(ys) / len(ys)
|
||||
order = sorted(nodes, key=lambda c: abs(c[0] - cx) + abs(c[1] - cy))
|
||||
connected = {order[0]}
|
||||
for c in order[1:]:
|
||||
if c in grid.tree(net) & connected or c in connected:
|
||||
continue
|
||||
tgt = (grid.tree(net) & (connected | {cc for e in grid.edges.get(net, ()) for cc in e})) | connected
|
||||
# pins must leave in their outward direction: start one step out if it is a pin
|
||||
path = grid.route(net, c, tgt, bounds)
|
||||
if path is None:
|
||||
if os.environ.get('DBG'):
|
||||
print('DBG', net, 'src', c, 'block', grid.block.get(c), 'node', grid.nodes.get(c), 'occ', grid.occ.get(c), 'ntgt', len(tgt))
|
||||
for dx,dy in ((1,0),(-1,0),(0,1),(0,-1)):
|
||||
n=(c[0]+dx,c[1]+dy); print(' nb', n, grid.block.get(n), grid.nodes.get(n), grid.occ.get(n))
|
||||
failed.append((net, b, c)); continue
|
||||
for a, bb in zip(path, path[1:]):
|
||||
grid.add_edge(net, a, bb)
|
||||
connected |= set(path)
|
||||
if failed:
|
||||
print('ROUTING FAILED:', failed)
|
||||
|
||||
# 2b) name every wired signal net with a small label on a straight wire run (keeps PCB net names)
|
||||
labelled = {(net, b) for b, net, x, y, d in LABELS} | {(net, b) for b, net, x, y in PSYM}
|
||||
for net, b, nodes, pl in wired:
|
||||
if net in POWERNETS or (net, b) in labelled:
|
||||
continue
|
||||
es = grid.edges.get(net, set())
|
||||
hcells = {}
|
||||
for a, c in es:
|
||||
if a[1] == c[1]:
|
||||
hcells.setdefault(a, set()).add(c); hcells.setdefault(c, set()).add(a)
|
||||
need = int(len(net) * 1.0) + 1
|
||||
best = None
|
||||
for c in sorted(hcells, key=lambda c: (c[1], c[0])):
|
||||
run = [c]
|
||||
while (run[-1][0] + 1, run[-1][1]) in hcells.get(run[-1], ()) and len(run) <= need:
|
||||
run.append((run[-1][0] + 1, run[-1][1]))
|
||||
if len(run) > need and all((r[0], r[1] - 1) not in grid.block and (r[0], r[1] - 1) not in grid.soft for r in run):
|
||||
best = c; break
|
||||
if best is None:
|
||||
# fallback: first pin of the net
|
||||
best = nodes[0]
|
||||
print('label on pin for', net)
|
||||
cx, cy = best[0] * GRID, best[1] * GRID
|
||||
items.append(f'(label {q(net)} (at {cx:.2f} {cy:.2f} 0) (effects (font (size 1.0 1.0)) (justify left bottom)) (uuid {U()}))')
|
||||
for k in range(need + 1):
|
||||
grid.block.setdefault((best[0] + k, best[1] - 1), 'lbl')
|
||||
|
||||
# 3) emit wires from edge sets (split at corners, junctions, nodes)
|
||||
for net, es in grid.edges.items():
|
||||
adj = {}
|
||||
for a, b in es:
|
||||
adj.setdefault(a, set()).add(b); adj.setdefault(b, set()).add(a)
|
||||
def is_break(c):
|
||||
nb = adj[c]
|
||||
if len(nb) != 2 or grid.nodes.get(c) == net:
|
||||
return True
|
||||
a, b = list(nb)
|
||||
return not (a[0] == b[0] or a[1] == b[1])
|
||||
done = set()
|
||||
stub_set = set()
|
||||
for start in adj:
|
||||
if not is_break(start):
|
||||
continue
|
||||
for n in adj[start]:
|
||||
e = (min(start, n), max(start, n))
|
||||
if e in done:
|
||||
continue
|
||||
path = [start, n]; done.add(e)
|
||||
while not is_break(path[-1]):
|
||||
nxt = [m for m in adj[path[-1]] if m != path[-2]][0]
|
||||
done.add((min(path[-1], nxt), max(path[-1], nxt))); path.append(nxt)
|
||||
# stubs already emitted for power/single labels: skip if both ends are stub cells
|
||||
if (net, path[0], path[-1]) in _emitted or (net, path[-1], path[0]) in _emitted:
|
||||
continue
|
||||
W(path[0], path[-1])
|
||||
for c, nb in adj.items():
|
||||
if len(nb) >= 3:
|
||||
items.append(f'(junction (at {c[0]*GRID:.2f} {c[1]*GRID:.2f}) (diameter 0) (color 0 0 0 0) (uuid {U()}))')
|
||||
|
||||
for (x, y, size, text) in LNOTES:
|
||||
items.append(f'(text {q(text)} (exclude_from_sim no) (at {x} {y} 0) (effects (font (size {size} {size}) (bold yes)) (justify left bottom)) (uuid {U()}))')
|
||||
|
||||
libtxt = '\n'.join(dump(v, 1) for v in libsyms.values())
|
||||
sch = f'''(kicad_sch (version 20231120) (generator "eeschema") (generator_version "8.0") (uuid {root}) (paper "A3")
|
||||
(title_block (title "Ofensteuerung (Wemos D1 mini)") (date "2026-11-07") (rev "1.0") (company "Ramiro / OpenClaw")
|
||||
(comment 1 "Pumpenrelais 230 V + Thermostat parallel + Strommessung, Abgas Typ K, 3x DS18B20, Servo-Luftklappe, PWM-Pumpe")
|
||||
(comment 2 "Erzeugt mit pcb/gen_sch.py aus design.py + sch_layout.py - Änderungen dort, dann neu erzeugen"))
|
||||
(lib_symbols
|
||||
{libtxt})
|
||||
{chr(10).join(items)}
|
||||
(sheet_instances (path "/" (page "1")))
|
||||
)
|
||||
'''
|
||||
open(os.path.join(HERE, PROJECT + '.kicad_sch'), 'w').write(sch)
|
||||
json.dump(symuuids, open(os.path.join(HERE, 'symuuids.json'), 'w'), indent=1)
|
||||
print('ok', len(PARTS), 'parts', 'failed' if failed else '')
|
||||
|
||||
_emitted = set()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
(version 1)
|
||||
(rule "GND-Waermefallen min. 1 Steg"
|
||||
(condition "A.NetName == 'GND'")
|
||||
(constraint min_resolved_spokes 1))
|
||||
(rule "230 V zu Kleinspannung min. 4 mm"
|
||||
(condition "A.NetClass == 'Mains' && B.NetClass != 'Mains'")
|
||||
(constraint clearance (min 4mm)))
|
||||
(rule "230 V zu Relaisspule (Relais-intern) min. 3 mm"
|
||||
(condition "A.NetClass == 'Mains' && B.NetClass != 'Mains' && (A.memberOfFootprint('K1') || B.memberOfFootprint('K1'))")
|
||||
(constraint clearance (min 3mm)))
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,771 @@
|
||||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.381,
|
||||
"copper_line_width": 0.381,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.524,
|
||||
"copper_text_size_v": 2.032,
|
||||
"copper_text_thickness": 0.3048,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": false,
|
||||
"text_position": 0,
|
||||
"units_format": 1
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.381,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.762,
|
||||
"height": 1.524,
|
||||
"width": 1.524
|
||||
},
|
||||
"silk_line_width": 0.381,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.524,
|
||||
"silk_text_size_v": 1.524,
|
||||
"silk_text_thickness": 0.3048,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"45_degree_only": false,
|
||||
"border_display_style": 2,
|
||||
"border_hatch_pitch": 0.5,
|
||||
"corner_radius": 0.0,
|
||||
"corner_smoothing": 0,
|
||||
"fill_mode": 0,
|
||||
"hatch_gap": 1.5,
|
||||
"hatch_orientation": 0.0,
|
||||
"hatch_smoothing_level": 0,
|
||||
"hatch_smoothing_value": 0.1,
|
||||
"hatch_thickness": 1.0,
|
||||
"min_clearance": 0.508,
|
||||
"min_island_area": 10.0,
|
||||
"min_thickness": 0.25,
|
||||
"pad_connection": 1,
|
||||
"remove_islands": 0,
|
||||
"thermal_relief_gap": 0.5,
|
||||
"thermal_relief_spoke_width": 0.5
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [
|
||||
{
|
||||
"gap": 0.0,
|
||||
"via_gap": 0.0,
|
||||
"width": 0.0
|
||||
}
|
||||
],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "warning",
|
||||
"hole_clearance": "error",
|
||||
"hole_near_hole": "error",
|
||||
"hole_to_hole": "error",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "warning",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "warning",
|
||||
"overlapping_pads": "warning",
|
||||
"padstack": "error",
|
||||
"pth_inside_courtyard": "warning",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "error",
|
||||
"silk_overlap": "error",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"allow_blind_buried_vias": false,
|
||||
"allow_microvias": false,
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.2,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.4,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.0,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.12,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.2,
|
||||
"min_via_annular_width": 0.05,
|
||||
"min_via_diameter": 0.6,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_enabled": false,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_enabled": false,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_enabled": false,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [
|
||||
0.0,
|
||||
0.4,
|
||||
0.8
|
||||
],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 100,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.1,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 100,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.1,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 100,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.1,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [
|
||||
{
|
||||
"diameter": 0.0,
|
||||
"drill": 0.0
|
||||
}
|
||||
],
|
||||
"zones_allow_external_fillets": true,
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_label_syntax": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"conflicting_netclasses": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"legacy": {
|
||||
"common": {
|
||||
"NetDir": ""
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "ofensteuerung.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.25,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.178,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.4,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 2.5,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_width": 0.13,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Mains",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 1,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 2.0,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 2.2,
|
||||
"via_drill": 1.0,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.3,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_width": 0.13,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Power",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 0,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 1.0,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 1.0,
|
||||
"via_drill": 0.5,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": {},
|
||||
"netclass_assignments": {},
|
||||
"netclass_patterns": [
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "GND"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "+5V"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "+3V3"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/L_IN"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/L_F"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/L_SW"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/L_PUMP"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/N"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "/PE"
|
||||
},
|
||||
{
|
||||
"netclass": "Mains",
|
||||
"pattern": "unconnected-(K1-*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"pos_files": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"svg": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": false,
|
||||
"name": "Grouped By Value",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Référence"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_bus_thickness": 12.0,
|
||||
"default_junction_size": 40.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"default_wire_thickness": 6.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.3,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.3
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"ngspice": {
|
||||
"fix_include_paths": true,
|
||||
"fix_passive_vals": false,
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"model_mode": 0,
|
||||
"workbook_filename": ""
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"space_save_all_events": true,
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"spice_model_current_sheet_as_root": true,
|
||||
"spice_save_all_currents": false,
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "ofensteuerung.kicad_sch",
|
||||
"name": "ofensteuerung",
|
||||
"uuid": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheets": [],
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,34 @@
|
||||
(kicad_symbol_lib (version 20231120) (generator "gen_sch.py")
|
||||
(symbol "WEMOS_D1_mini" (pin_names (offset 1.016)) (exclude_from_sim no) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "A" (at 0 12.70 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Value" "WEMOS_D1_mini" (at 0 -12.70 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Description" "Wemos D1 mini (ESP8266) auf 2x 1x8 Buchsenleisten" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(symbol "WEMOS_D1_mini_0_1" (rectangle (start -15.24 11.43) (end 15.24 -11.43) (stroke (width 0.254) (type default)) (fill (type background))))
|
||||
(symbol "WEMOS_D1_mini_1_1" (pin input line (at -17.78 8.89 0) (length 2.54) (name "RST" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27))))) (pin input line (at -17.78 6.35 0) (length 2.54) (name "A0" (effects (font (size 1.27 1.27)))) (number "2" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -17.78 3.81 0) (length 2.54) (name "D0/GPIO16" (effects (font (size 1.27 1.27)))) (number "3" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -17.78 1.27 0) (length 2.54) (name "D5/GPIO14" (effects (font (size 1.27 1.27)))) (number "4" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -17.78 -1.27 0) (length 2.54) (name "D6/GPIO12" (effects (font (size 1.27 1.27)))) (number "5" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -17.78 -3.81 0) (length 2.54) (name "D7/GPIO13" (effects (font (size 1.27 1.27)))) (number "6" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -17.78 -6.35 0) (length 2.54) (name "D8/GPIO15" (effects (font (size 1.27 1.27)))) (number "7" (effects (font (size 1.27 1.27))))) (pin power_out line (at -17.78 -8.89 0) (length 2.54) (name "3V3" (effects (font (size 1.27 1.27)))) (number "8" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 8.89 180) (length 2.54) (name "TX/GPIO1" (effects (font (size 1.27 1.27)))) (number "9" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 6.35 180) (length 2.54) (name "RX/GPIO3" (effects (font (size 1.27 1.27)))) (number "10" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 3.81 180) (length 2.54) (name "D1/GPIO5" (effects (font (size 1.27 1.27)))) (number "11" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 1.27 180) (length 2.54) (name "D2/GPIO4" (effects (font (size 1.27 1.27)))) (number "12" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 -1.27 180) (length 2.54) (name "D3/GPIO0" (effects (font (size 1.27 1.27)))) (number "13" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at 17.78 -3.81 180) (length 2.54) (name "D4/GPIO2" (effects (font (size 1.27 1.27)))) (number "14" (effects (font (size 1.27 1.27))))) (pin power_in line (at 17.78 -6.35 180) (length 2.54) (name "GND" (effects (font (size 1.27 1.27)))) (number "15" (effects (font (size 1.27 1.27))))) (pin power_in line (at 17.78 -8.89 180) (length 2.54) (name "5V" (effects (font (size 1.27 1.27)))) (number "16" (effects (font (size 1.27 1.27)))))))
|
||||
(symbol "ADS1115_Modul" (pin_names (offset 1.016)) (exclude_from_sim no) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "A" (at 0 8.89 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Value" "ADS1115_Modul" (at 0 -8.89 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Description" "ADS1115 16-bit ADC Breakout (HW-ADS1115/CJMCU, 1x10 Pin)" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(symbol "ADS1115_Modul_0_1" (rectangle (start -6.35 7.62) (end 6.35 -7.62) (stroke (width 0.254) (type default)) (fill (type background))))
|
||||
(symbol "ADS1115_Modul_1_1" (pin power_in line (at -8.89 5.08 0) (length 2.54) (name "VDD" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27))))) (pin power_in line (at -8.89 2.54 0) (length 2.54) (name "GND" (effects (font (size 1.27 1.27)))) (number "2" (effects (font (size 1.27 1.27))))) (pin input line (at -8.89 0.00 0) (length 2.54) (name "SCL" (effects (font (size 1.27 1.27)))) (number "3" (effects (font (size 1.27 1.27))))) (pin bidirectional line (at -8.89 -2.54 0) (length 2.54) (name "SDA" (effects (font (size 1.27 1.27)))) (number "4" (effects (font (size 1.27 1.27))))) (pin input line (at -8.89 -5.08 0) (length 2.54) (name "ADDR" (effects (font (size 1.27 1.27)))) (number "5" (effects (font (size 1.27 1.27))))) (pin output line (at 8.89 5.08 180) (length 2.54) (name "ALRT" (effects (font (size 1.27 1.27)))) (number "6" (effects (font (size 1.27 1.27))))) (pin input line (at 8.89 2.54 180) (length 2.54) (name "A0" (effects (font (size 1.27 1.27)))) (number "7" (effects (font (size 1.27 1.27))))) (pin input line (at 8.89 0.00 180) (length 2.54) (name "A1" (effects (font (size 1.27 1.27)))) (number "8" (effects (font (size 1.27 1.27))))) (pin input line (at 8.89 -2.54 180) (length 2.54) (name "A2" (effects (font (size 1.27 1.27)))) (number "9" (effects (font (size 1.27 1.27))))) (pin input line (at 8.89 -5.08 180) (length 2.54) (name "A3" (effects (font (size 1.27 1.27)))) (number "10" (effects (font (size 1.27 1.27)))))))
|
||||
(symbol "ZMCT103C" (pin_names (offset 1.016)) (exclude_from_sim no) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "T" (at 0 5.08 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Value" "ZMCT103C" (at 0 -5.08 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Description" "Stromwandler ZMCT103C 1000:1, Primär = Drahtbrücke durchs Loch" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(symbol "ZMCT103C_0_1" (rectangle (start -7.62 3.81) (end 7.62 -3.81) (stroke (width 0.254) (type default)) (fill (type background))))
|
||||
(symbol "ZMCT103C_1_1" (pin passive line (at -10.16 1.27 0) (length 2.54) (name "P1 (L_SW)" (effects (font (size 1.27 1.27)))) (number "3" (effects (font (size 1.27 1.27))))) (pin passive line (at -10.16 -1.27 0) (length 2.54) (name "P2 (L_PUMP)" (effects (font (size 1.27 1.27)))) (number "4" (effects (font (size 1.27 1.27))))) (pin passive line (at 10.16 1.27 180) (length 2.54) (name "S1" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27))))) (pin passive line (at 10.16 -1.27 180) (length 2.54) (name "S2" (effects (font (size 1.27 1.27)))) (number "2" (effects (font (size 1.27 1.27)))))))
|
||||
(symbol "MAX6675_Modul" (pin_names (offset 1.016)) (exclude_from_sim no) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "A" (at 0 8.89 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Value" "MAX6675_Modul" (at 0 -8.89 0) (effects (font (size 1.27 1.27))))
|
||||
(property "Footprint" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Description" "MAX6675 Thermoelement-Modul (Typ K), 1x5 Pin" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(symbol "MAX6675_Modul_0_1" (rectangle (start -6.35 7.62) (end 6.35 -7.62) (stroke (width 0.254) (type default)) (fill (type background))))
|
||||
(symbol "MAX6675_Modul_1_1" (pin power_in line (at -8.89 5.08 0) (length 2.54) (name "GND" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27))))) (pin power_in line (at -8.89 2.54 0) (length 2.54) (name "VCC" (effects (font (size 1.27 1.27)))) (number "2" (effects (font (size 1.27 1.27))))) (pin input line (at -8.89 0.00 0) (length 2.54) (name "SCK" (effects (font (size 1.27 1.27)))) (number "3" (effects (font (size 1.27 1.27))))) (pin input line (at -8.89 -2.54 0) (length 2.54) (name "CS" (effects (font (size 1.27 1.27)))) (number "4" (effects (font (size 1.27 1.27))))) (pin output line (at -8.89 -5.08 0) (length 2.54) (name "SO" (effects (font (size 1.27 1.27)))) (number "5" (effects (font (size 1.27 1.27)))))))
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
(footprint "ADS1115_Module_1x10" (version 20240108) (generator "gen") (layer "F.Cu")
|
||||
(descr "ADS1115 Breakout (HW-ADS1115), 1x10 Stiftleiste 2,54 mm, Platine ca. 28 x 17,5 mm")
|
||||
(property "Reference" "A2" (at 11.43 -17.50 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(property "Value" "ADS1115" (at 11.43 -8.00 0) (layer "F.Fab") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(pad "1" thru_hole rect (at 0.00 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "VDD" (at 0.00 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "2" thru_hole circle (at 2.54 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "GND" (at 2.54 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "3" thru_hole circle (at 5.08 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "SCL" (at 5.08 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "4" thru_hole circle (at 7.62 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "SDA" (at 7.62 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "5" thru_hole circle (at 10.16 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "ADDR" (at 10.16 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "6" thru_hole circle (at 12.70 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "ALRT" (at 12.70 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "7" thru_hole circle (at 15.24 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "A0" (at 15.24 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "8" thru_hole circle (at 17.78 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "A1" (at 17.78 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "9" thru_hole circle (at 20.32 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "A2" (at 20.32 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "10" thru_hole circle (at 22.86 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "A3" (at 22.86 -2.60 90) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(fp_line (start -2.57 -16.00) (end 25.43 -16.00) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 25.43 -16.00) (end 25.43 1.50) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 25.43 1.50) (end -2.57 1.50) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start -2.57 1.50) (end -2.57 -16.00) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start -2.57 -16.00) (end 25.43 -16.00) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start 25.43 -16.00) (end 25.43 1.50) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start 25.43 1.50) (end -2.57 1.50) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start -2.57 1.50) (end -2.57 -16.00) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_text user "ADS1115" (at 11.43 -10.00 0) (layer "F.SilkS") (effects (font (size 1.2 1.2) (thickness 0.12))))
|
||||
(fp_rect (start -1.9 -1.9) (end 24.76 1.9) (stroke (width 0.05) (type solid)) (fill no) (layer "F.CrtYd"))
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
(footprint "MAX6675_Module_1x5" (version 20240108) (generator "gen") (layer "F.Cu")
|
||||
(descr "MAX6675 Thermoelement-Modul (ca. 25 x 15 mm), 1x5 Stiftleiste 2,54 mm (GND VCC SCK CS SO), Schraubklemme Typ K am anderen Ende")
|
||||
(property "Reference" "A3" (at 12 -4.2 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(property "Value" "MAX6675" (at 12 5 0) (layer "F.Fab") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(pad "1" thru_hole rect (at 0 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "GND" (at 2.9 0.00 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13)) (justify left)))
|
||||
(pad "2" thru_hole circle (at 0 2.54) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "VCC" (at 2.9 2.54 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13)) (justify left)))
|
||||
(pad "3" thru_hole circle (at 0 5.08) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "SCK" (at 2.9 5.08 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13)) (justify left)))
|
||||
(pad "4" thru_hole circle (at 0 7.62) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "CS" (at 2.9 7.62 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13)) (justify left)))
|
||||
(pad "5" thru_hole circle (at 0 10.16) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "SO" (at 2.9 10.16 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13)) (justify left)))
|
||||
(fp_rect (start -1.6 -2.4) (end 23.4 12.56) (stroke (width 0.12) (type solid)) (fill no) (layer "F.SilkS"))
|
||||
(fp_rect (start -1.6 -2.4) (end 23.4 12.56) (stroke (width 0.1) (type solid)) (fill no) (layer "F.Fab"))
|
||||
(fp_rect (start -1.9 -1.9) (end 1.9 12.06) (stroke (width 0.05) (type solid)) (fill no) (layer "F.CrtYd"))
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
(footprint "WEMOS_D1_mini_Socket" (version 20240108) (generator "gen") (layer "F.Cu")
|
||||
(descr "Wemos D1 mini auf 2x 1x8 Buchsenleiste, Reihenabstand 22,86 mm; USB an +Y")
|
||||
(property "Reference" "A1" (at 11.43 -15.50 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(property "Value" "WEMOS_D1_mini" (at 11.43 8.90 0) (layer "F.Fab") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(pad "1" thru_hole rect (at 0.00 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "RST" (at 2.40 0.00 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "2" thru_hole circle (at 0.00 2.54) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "A0" (at 2.40 2.54 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "3" thru_hole circle (at 0.00 5.08) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D0" (at 2.40 5.08 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "4" thru_hole circle (at 0.00 7.62) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D5" (at 2.40 7.62 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "5" thru_hole circle (at 0.00 10.16) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D6" (at 2.40 10.16 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "6" thru_hole circle (at 0.00 12.70) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D7" (at 2.40 12.70 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "7" thru_hole circle (at 0.00 15.24) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D8" (at 2.40 15.24 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "8" thru_hole circle (at 0.00 17.78) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "3V3" (at 2.40 17.78 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "9" thru_hole circle (at 22.86 0.00) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "TX" (at 20.40 0.00 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "10" thru_hole circle (at 22.86 2.54) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "RX" (at 20.40 2.54 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "11" thru_hole circle (at 22.86 5.08) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D1" (at 20.40 5.08 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "12" thru_hole circle (at 22.86 7.62) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D2" (at 20.40 7.62 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "13" thru_hole circle (at 22.86 10.16) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D3" (at 20.40 10.16 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "14" thru_hole circle (at 22.86 12.70) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "D4" (at 20.40 12.70 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "15" thru_hole circle (at 22.86 15.24) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "G" (at 20.40 15.24 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(pad "16" thru_hole circle (at 22.86 17.78) (size 1.7 1.7) (drill 1.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_text user "5V" (at 20.40 17.78 0) (layer "F.SilkS") (effects (font (size 0.8 0.8) (thickness 0.13))))
|
||||
(fp_line (start -1.37 -13.22) (end 24.23 -13.22) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 24.23 -13.22) (end 24.23 20.98) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 24.23 20.98) (end -1.37 20.98) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start -1.37 20.98) (end -1.37 -13.22) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start -1.37 -13.22) (end 24.23 -13.22) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start 24.23 -13.22) (end 24.23 20.98) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start 24.23 20.98) (end -1.37 20.98) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start -1.37 20.98) (end -1.37 -13.22) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
||||
(fp_line (start 7.43 15.48) (end 15.43 15.48) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 15.43 15.48) (end 15.43 20.78) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 15.43 20.78) (end 7.43 20.78) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_line (start 7.43 20.78) (end 7.43 15.48) (stroke (width 0.12) (type solid)) (layer "F.SilkS"))
|
||||
(fp_text user "USB" (at 11.43 18.48 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.12))))
|
||||
(fp_text user "ANTENNE" (at 11.43 -7.22 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.12))))
|
||||
(fp_line (start -1.80 -1.80) (end 1.80 -1.80) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 1.80 -1.80) (end 1.80 19.58) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 1.80 19.58) (end -1.80 19.58) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start -1.80 19.58) (end -1.80 -1.80) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 21.06 -1.80) (end 24.66 -1.80) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 24.66 -1.80) (end 24.66 19.58) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 24.66 19.58) (end 21.06 19.58) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
(fp_line (start 21.06 19.58) (end 21.06 -1.80) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
(footprint "ZMCT103C_Drahtbruecke" (version 20240108) (generator "gen") (layer "F.Cu")
|
||||
(descr "Stromwandler ZMCT103C (18,2 x 10,2 mm, Pins 11,8 mm, Loch 5 mm in 10 mm Höhe). Pads 3/4 = Primärleiter als isolierte Drahtbrücke durchs Loch")
|
||||
(property "Reference" "T1" (at 0 -7.2 0) (layer "F.SilkS") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(property "Value" "ZMCT103C" (at 0 7.2 0) (layer "F.Fab") (effects (font (size 1.0 1.0) (thickness 0.15))))
|
||||
(pad "1" thru_hole rect (at -5.9 0) (size 2.2 2.2) (drill 1.2) (layers "*.Cu" "*.Mask"))
|
||||
(pad "2" thru_hole circle (at 5.9 0) (size 2.2 2.2) (drill 1.2) (layers "*.Cu" "*.Mask"))
|
||||
(pad "3" thru_hole circle (at 0 -10) (size 4.0 4.0) (drill 2.0) (layers "*.Cu" "*.Mask"))
|
||||
(pad "4" thru_hole circle (at 0 10) (size 4.0 4.0) (drill 2.0) (layers "*.Cu" "*.Mask"))
|
||||
(fp_rect (start -9.1 -5.1) (end 9.1 5.1) (stroke (width 0.12) (type solid)) (fill no) (layer "F.SilkS"))
|
||||
(fp_line (start 0 -7.6) (end 0 7.6) (stroke (width 0.3) (type dash)) (layer "F.SilkS"))
|
||||
(fp_circle (center 0 0) (end 2.5 0) (stroke (width 0.12) (type solid)) (fill no) (layer "F.Fab"))
|
||||
(fp_rect (start -9.1 -5.1) (end 9.1 5.1) (stroke (width 0.1) (type solid)) (fill no) (layer "F.Fab"))
|
||||
(fp_rect (start -9.35 -5.35) (end 9.35 5.35) (stroke (width 0.05) (type solid)) (fill no) (layer "F.CrtYd"))
|
||||
(fp_circle (center 0 -10) (end 2.4 -10) (stroke (width 0.05) (type solid)) (fill no) (layer "F.CrtYd"))
|
||||
(fp_circle (center 0 10) (end 2.4 10) (stroke (width 0.05) (type solid)) (fill no) (layer "F.CrtYd"))
|
||||
)
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# render.sh <board> <out.png> : Bestückungsansicht (Kupfer beidseitig + Druck + Courtyard)
|
||||
b=$1; o=$2; t=/tmp/render_$$; mkdir -p $t
|
||||
kicad-cli pcb export svg --mode-single -l F.Cu,B.Cu,F.Silkscreen,Edge.Cuts,F.Courtyard,F.Fab --page-size-mode 2 --exclude-drawing-sheet -o $t/b.svg "$b" >/dev/null 2>&1
|
||||
rsvg-convert -w 1600 -b white $t/b.svg -o "$o"; rm -rf $t
|
||||
+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()))
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Schaltplan-Anordnung (A3, mm, 1,27-Raster). gen_sch.py verlegt alle Signalleitungen automatisch.
|
||||
BLOCKS: block -> {ref: (x, y, rot, mirror)}; PSYM: (block, net, x, y)."""
|
||||
def g(v):
|
||||
return round(round(v / 1.27) * 1.27, 2)
|
||||
def B(d):
|
||||
return {k: (g(x), g(y), r, m) for k, (x, y, r, m) in d.items()}
|
||||
|
||||
BLOCKS = {
|
||||
'power': B({'J1': (25.4, 40.64, 0, 'y'), '#FLG01': (40.64, 30.48, 0, None), '#FLG02': (40.64, 53.34, 180, None),
|
||||
'C1': (48.26, 43.18, 0, None), 'C2': (58.42, 43.18, 0, None), 'R4': (68.58, 40.64, 0, None), 'D3': (68.58, 53.34, 90, None)}),
|
||||
'mains': B({'J2': (25.4, 101.6, 0, 'y'), 'F1': (50.8, 96.52, 90, None), 'J4': (63.5, 76.2, 90, None),
|
||||
'K1': (88.9, 101.6, 0, None), 'T1': (125.73, 111.76, 0, None), 'J3': (160.02, 88.9, 0, None)}),
|
||||
'relay': B({'R1': (63.5, 146.05, 90, None), 'R2': (72.39, 152.4, 0, None), 'Q1': (81.28, 146.05, 0, None),
|
||||
'D1': (104.14, 128.27, 90, None), 'D2': (116.84, 140.97, 90, None), 'R3': (116.84, 128.27, 0, None)}),
|
||||
'ct': B({'R5': (144.78, 129.54, 0, None), 'R6': (157.48, 125.73, 0, None), 'R7': (157.48, 138.43, 0, None),
|
||||
'C5': (167.64, 138.43, 0, None), 'C6': (177.8, 138.43, 0, None)}),
|
||||
'wemos': B({'A1': (228.6, 151.13, 0, None), 'C3': (215.9, 187.96, 0, None), 'C4': (226.06, 187.96, 0, None)}),
|
||||
'ads': B({'A2': (309.88, 165.1, 0, None)}),
|
||||
'max': B({'A3': (309.88, 104.14, 0, None), 'R9': (292.1, 116.84, 0, None), 'C8': (281.94, 109.22, 0, None)}),
|
||||
'onewire': B({'J5': (391.16, 40.64, 0, None), 'J6': (391.16, 55.88, 0, None), 'J7': (391.16, 71.12, 0, None),
|
||||
'R8': (372.11, 38.1, 0, None), 'C7': (360.68, 55.88, 0, None)}),
|
||||
'servo': B({'J8': (391.16, 210.82, 0, None), 'R10': (370.84, 213.36, 90, None), 'C9': (358.14, 220.98, 0, None), 'C10': (347.98, 220.98, 0, None)}),
|
||||
'pwm': B({'J9': (301.16, 242.00, 0, None), 'R14': (280.84, 239.46, 90, None), 'R15': (270.68, 231.84, 0, None),
|
||||
'R11': (232.58, 248.35, 90, None), 'R12': (240.20, 254.70, 0, None), 'Q2': (247.82, 248.35, 0, None),
|
||||
'R13': (250.36, 234.38, 0, None), 'JP1': (250.36, 222.95, 270, None)}),
|
||||
'erw': B({'J10': (391.16, 152.4, 0, None)}),
|
||||
'holes': B({'H1': (30.48, 270.0, 0, None), 'H2': (45.72, 270.0, 0, None), 'H3': (60.96, 270.0, 0, None), 'H4': (76.2, 270.0, 0, None)}),
|
||||
}
|
||||
LABELS = []
|
||||
PSYM = []
|
||||
NOTES = [
|
||||
(20.32, 25.4, 2.0, 'Versorgung 5 V / 3 A (Servo)'),
|
||||
(20.32, 66.04, 2.0, '230 V: Netz -> T2A -> Relais || Thermostat -> Stromwandler -> Pumpe'),
|
||||
(20.32, 71.12, 1.27, 'Thermostat (Schließer) parallel zum Relaiskontakt: Pumpe läuft auch bei ESP-Ausfall'),
|
||||
(55.88, 119.38, 2.0, 'Relaistreiber (D0/GPIO16)'),
|
||||
(137.16, 116.84, 2.0, 'Strommessung: ZMCT103C 1000:1, Bürde 100 R, Mitte 1,65 V -> ADS1115 A0-A1'),
|
||||
(205.74, 124.46, 2.0, 'Wemos D1 mini (ESP8266)'),
|
||||
(289.56, 144.78, 2.0, 'ADS1115-Modul (I2C 0x48)'),
|
||||
(276.86, 86.36, 2.0, 'Abgas: MAX6675 + Thermoelement Typ K'),
|
||||
(345.44, 25.4, 2.0, '1-Wire DS18B20 (3 Klemmen, ein Bus)'),
|
||||
(330.2, 195.58, 2.0, 'Luftklappe: Servo MG996R'),
|
||||
(214.63, 215.9, 2.0, 'PWM-Pumpe (optional)'),
|
||||
(370.84, 134.62, 2.0, 'Erweiterung'),
|
||||
(20.32, 259.08, 2.0, 'Befestigung M3'),
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
(sym_lib_table (version 7)
|
||||
(lib (name "ofensteuerung")(type "KiCad")(uri "${KIPRJMOD}/ofensteuerung.kicad_sym")(options "")(descr ""))
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"J2": "1caec72d-30f2-543f-8a7b-8d15757bd46e",
|
||||
"J4": "94cdcc2e-15f3-5b84-8c13-0a414ebd9a4a",
|
||||
"J3": "874983a2-1f04-5d18-95d9-813e3c95ca4f",
|
||||
"F1": "28f7fb03-2c32-5a68-ac1a-0dfa9004e102",
|
||||
"K1": "b0833d88-5b13-575a-8c39-0712ded4a388",
|
||||
"T1": "a0ec19ea-1bc4-5564-b075-8880441d714c",
|
||||
"J1": "14db5e1a-5c8a-5b29-9ff4-6cea8c813b20",
|
||||
"C1": "8873537e-6111-5211-a1b2-eda456206996",
|
||||
"C2": "a1383de9-f1c7-58fc-bd00-4af2368d5ea7",
|
||||
"R4": "896b2495-0e93-5444-a79e-a31c87b32f07",
|
||||
"D3": "853a00a6-f61e-5058-a424-a4f2aaf75a73",
|
||||
"A1": "170186fe-6865-5841-bb94-24c9647a65b6",
|
||||
"C3": "a70b1b63-ea4e-5029-ae98-9accd895772e",
|
||||
"C4": "588b9e19-ad03-5ef3-937e-5b008b57a2fd",
|
||||
"R1": "7e46d0e7-0adb-52d2-aeff-817c993a8df2",
|
||||
"R2": "9472fb12-f09d-5118-99fd-0499f1fb8701",
|
||||
"Q1": "42346c05-a39a-5aad-8ea7-aaecf47bfc30",
|
||||
"D1": "67f6d85f-ced2-5061-9e14-0e0aa85deebb",
|
||||
"R3": "3619ba94-600c-5b94-9d4a-2f991be3aa84",
|
||||
"D2": "729b0b41-1e4f-5358-8c03-ace35885839e",
|
||||
"R5": "fa666bc4-d233-56e8-9645-58881b4862dc",
|
||||
"R6": "4e26925e-c36d-5140-810f-d14aa2903b4f",
|
||||
"R7": "8403744e-6b6d-5cd0-9da0-472a4075b143",
|
||||
"C5": "01cc4214-37e7-5df8-9da2-18eff3632cf7",
|
||||
"C6": "8735fdbe-202c-50da-b085-89bbe420f20b",
|
||||
"A2": "fa890770-a976-50d2-b400-a3575b41e470",
|
||||
"A3": "8ce1bbb2-48c4-5067-9815-8598faa918f7",
|
||||
"R9": "78bdcbe4-f5c1-5989-a736-70f5cc57aa27",
|
||||
"C8": "36dd4acf-4241-5813-bc9e-db8f49c6c62f",
|
||||
"J5": "7b024d22-065d-5532-92c8-e638eec4001c",
|
||||
"J6": "cfb39cbc-0c41-5eb9-aaa7-3d48c8772625",
|
||||
"J7": "c98e076d-86d4-5812-96a1-1cc2cc7579f6",
|
||||
"R8": "1cac538f-cc88-58af-9dde-4692fa44b808",
|
||||
"C7": "cca9da88-aa3f-5dea-95c5-b24dc12337c5",
|
||||
"J8": "f100476c-4cf2-5231-9151-2ae902343366",
|
||||
"R10": "908bd703-3f78-5fd5-9c5a-3e06598b398c",
|
||||
"C9": "384fcc73-6639-5801-9c53-652a1213eef3",
|
||||
"C10": "58a30551-3cb9-5345-8176-c53f55beb9ca",
|
||||
"J9": "8f4f8411-8abd-5397-90c6-3a189502d35f",
|
||||
"R11": "0d9a11dc-9d23-561e-bf96-503fb30d7239",
|
||||
"R12": "8ae27c8e-3f79-57d5-8199-f6bcc519bba5",
|
||||
"Q2": "b09b2680-f862-5961-ba64-6482bf8aa60d",
|
||||
"R13": "7d4fbf9b-8f88-59f2-9f2d-c254bd8a0261",
|
||||
"JP1": "7a1a010a-2d32-58cb-a143-60bd6157c63e",
|
||||
"R14": "b63b2fdc-a5d6-5577-b327-8780907c1eb5",
|
||||
"R15": "9c96f871-bb18-53b7-8388-645a29539aa4",
|
||||
"J10": "e0637304-7b57-5d35-a747-6c92b98d3964",
|
||||
"H1": "f0799bec-e8cc-52af-ae12-b708a3c4ecea",
|
||||
"H2": "000a138c-3bf8-52fd-b988-ac152c47ba28",
|
||||
"H3": "502d18ea-1f8a-5b7e-88c4-56af34d05c92",
|
||||
"H4": "fb455633-728c-5335-a15a-009097dcce65",
|
||||
"#FLG01": "1d52e2bb-2e6a-522a-b9d7-7e85b16195c8",
|
||||
"#FLG02": "4ded5458-e727-5aa5-b2ce-d80a75a4fca3"
|
||||
}
|
||||
Reference in new issue
Block a user