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
+11
@@ -0,0 +1,11 @@
|
||||
/.esphome/
|
||||
/esphome/.esphome/
|
||||
secrets.yaml
|
||||
*.bin
|
||||
__pycache__/
|
||||
pcb/en/
|
||||
pcb/*_unrouted.kicad_p*
|
||||
pcb/*-backups/
|
||||
pcb/fp-info-cache
|
||||
*.kicad_prl
|
||||
pcb/fab/gerber/
|
||||
@@ -0,0 +1,88 @@
|
||||
# Ofensteuerung (Wemos D1 mini)
|
||||
|
||||
Trägerplatine 100 × 100 mm für den Ofen, gleicher Aufbau wie die Umwälzpumpen-Platine:
|
||||
Wemos D1 mini auf Buchsenleisten, alle Außenanschlüsse als **Steckklemmen 5,08 mm** (Stiftleiste + abziehbarer Schraubstecker).
|
||||
|
||||

|
||||
|
||||
## Funktionen
|
||||
|
||||
| Klemme | Pins | Funktion | ESP |
|
||||
|---|---|---|---|
|
||||
| J2 NETZ 230V | PE / N / L | Netzzuleitung (vorher LS-Schalter) | – |
|
||||
| J3 PUMPE | L / N / PE | Pumpe 230 V über Relais K1 (Schließer), abgesichert mit F1 T2A | D0 |
|
||||
| J4 THERMOSTAT | L / Ls | **Externer Thermostat parallel zum Relaiskontakt**: potentialfreier Schließer, schaltet die Pumpe auch ohne ESP | – |
|
||||
| T1 | Drahtbrücke | Stromwandler ZMCT103C misst den Pumpenstrom, egal ob Relais oder Thermostat schaltet → ADS1115 A0-A1 | I²C |
|
||||
| A3 (Modul) | Typ K | Abgastemperatur: MAX6675-Modul, Thermoelement direkt in die Klemme des Moduls am Plattenrand | D5/D6/D8 |
|
||||
| J5, J6, J7 | GND / DQ / 3V3 | 3× DS18B20 am selben 1-Wire-Bus (4k7) | D4 |
|
||||
| J8 KLAPPE | GND / 5V / SIG | Luftklappe: Servo **MG996R** (braun/rot/orange), 470 µF direkt an der Klemme | D3 |
|
||||
| J9 PWM-PUMPE | GND / FB / PWM | Vorbereitung PWM-Pumpe, gleiche Stufe wie Umwälzpumpe (BC546B, 470 Ω, JP1 Pull-up) + Rückmeldung | D7, RX |
|
||||
| J1 5V 3A | + / − | Versorgung 5 V, **mindestens 3 A** (MG996R zieht beim Anlaufen/Blockieren bis 2,5 A) | – |
|
||||
| J10 | 5V 3V3 GND SDA SCL A0 TX AD2 AD3 | Erweiterung (Stiftleiste 2,54) | – |
|
||||
|
||||
Pinbelegung jeder Klemme steht auf dem Bestückungsdruck, Pin 1 ist mit ▽ markiert.
|
||||
|
||||
## 230-V-Teil
|
||||
|
||||
- Der Netzspannungsbereich (oben links) ist auf dem Bestückungsdruck umrandet, dort gibt es keine GND-Fläche.
|
||||
- Abstand 230 V ↔ Kleinspannung auf der Platine **≥ 4 mm** (eigene DRC-Regel). Ausnahme ist das Relais selbst: Spule ↔ Kontakt ≈ 3,3 mm (SRD-Bauform, Basisisolierung).
|
||||
Die Kleinspannungsseite ist damit **nicht berührsicher getrennt** → Platine nur in geschlossenem Gehäuse betreiben, vor dem Öffnen Netz trennen.
|
||||
Über die Fühler (Thermoelement am Abgasrohr, DS18B20 an Rohren) liegt GND in der Praxis ohnehin auf Schutzleiterpotential.
|
||||
- **Stromwandler T1:** Die Pumpen-Phase läuft als isolierte Drahtbrücke (H07V-K 1,5 mm², ca. 4 cm, vorgebogen) vom Pad links durch das Loch des ZMCT103C zum Pad rechts.
|
||||
Ohne diese Brücke läuft die Pumpe nicht.
|
||||
- Leiterbahnen 230 V: 2 mm breit, für die Pumpe (< 1 A) reichlich. F1 = Feinsicherung 5×20 träge (T2A, je nach Pumpe auch T1A).
|
||||
- **Ausfallverhalten:** ESP aus/bootet → Relais aus (10k am Basiswiderstand). Die Pumpe läuft dann nur über den Thermostat an J4.
|
||||
Deshalb den Thermostat (z.B. Anlegethermostat am Ofenvorlauf, ca. 60–70 °C, Schließer) unbedingt anschließen.
|
||||
|
||||
## Strommessung
|
||||
|
||||
ZMCT103C (1000:1) mit 100 Ω Bürde: 1 A Pumpenstrom = 100 mV eff. Die Bürde liegt auf 1,65 V (Teiler 2×10k), der ADS1115 misst differenziell A0-A1 (±256 mV → bis ca. 1,8 A).
|
||||
ESPHome (`ct_clamp`) rechnet daraus den Strom und meldet zusätzlich:
|
||||
- **Pumpe läuft** (Strom > 30 mA)
|
||||
- **Pumpe läuft über Thermostat** (Strom, aber Relais aus)
|
||||
- **Pumpe Störung** (Relais an, 20 s kein Strom → Sicherung, Pumpe oder Kabel)
|
||||
|
||||
Nach dem Aufbau einmal mit Zangenamperemeter oder Shelly vergleichen und ggf. den Faktor `multiply: 10.0` anpassen.
|
||||
|
||||
## Teile
|
||||
|
||||
`pcb/fab/ofensteuerung_bom.csv` enthält die Stückliste mit Fach aus deinem Inventar (`Parts_Inventory.xlsx`).
|
||||
|
||||
**Aus dem Inventar:** Widerstände 100/220/330/470/1k/2k2/4k7/10k (Schrank E), 100n (C-07-1), 10 µ (C-10-2), 470 µ (C-04-3), 1000 µ (C-05-3),
|
||||
BC546B (B-01-4), 1N4007 (B-02-1), LEDs (B-07-5, B-08-5), Stift-/Buchsenleisten (A-07-2, A-06-5), Jumper (A-11-3), Sicherungshalter/-sicherung (B-12-2, B-11-2).
|
||||
Relais: im Fach A-07-1 nachsehen, ob ein SRD-05VDC-SL-C (oder pinkompatibel: Songle SRD, HKE HRS4H, Ningbo …) dabei ist.
|
||||
|
||||
**Zukaufen:**
|
||||
- Steckklemmen 5,08 mm, jeweils Stiftleiste abgewinkelt + Schraubstecker: **7× 3-polig, 2× 2-polig** (J2, J3, J4 sind 230 V: Phoenix MSTB 2,5 oder 2EDG mit 300 V Nennspannung)
|
||||
- ZMCT103C Stromwandler (Einzelbauteil, nicht das Modul mit OPV)
|
||||
- MAX6675-Modul + Thermoelement Typ K als Abgasfühler (Edelstahl-Hülse, bis ca. 600 °C)
|
||||
- ADS1115-Modul (HW-ADS1115, Pinfolge VDD GND SCL SDA ADDR ALRT A0 A1 A2 A3 prüfen)
|
||||
- Servo MG996R, 3× DS18B20 (wasserdicht), Wemos D1 mini
|
||||
- 5-V-Netzteil ≥ 3 A (Hutschiene z.B. Mean Well HDR-30-5)
|
||||
- Anlegethermostat mit Schließer
|
||||
- ggf. Relais SRD-05VDC-SL-C, 2 Sicherungsclips 5×20 + T2A
|
||||
|
||||
Hinweise:
|
||||
- MAX6675-Modul: Pinfolge GND VCC SCK CS SO ist die übliche (rote/blaue Platine). Vor dem Einlöten der Buchsenleiste am Modul prüfen.
|
||||
- Wemos, ADS1115 und MAX6675 auf Buchsenleisten stecken. Der USB-Stecker des Wemos ist auf der Platine zugebaut: erstes Flashen vor dem Einstecken, danach OTA.
|
||||
- D3/GPIO0 (Servo) und D8/GPIO15 (MAX CS, 10k nach GND) sind Boot-Pins und passend beschaltet.
|
||||
|
||||
## Firmware
|
||||
|
||||
`esphome/ofensteuerung.yaml` (geprüft mit `esphome config`): Relais, Strom, Abgas, 3× DS18B20, Luftklappe als Zahl 0–100 %, PWM-Pumpe.
|
||||
MQTT unter `gunter/ofensteuerung/…`, Secrets wie bei der Umwälzpumpe. Die Servo-Endlagen (`min_level`/`max_level`) an die Klappe anpassen.
|
||||
|
||||
## Dateien neu erzeugen
|
||||
|
||||
Alles kommt aus `pcb/design.py` (Netzliste, Footprints, Positionen) und `pcb/sch_layout.py` (Schaltplan-Anordnung).
|
||||
|
||||
```
|
||||
cd pcb
|
||||
python3 gen_sch.py # Schaltplan + Projekt-Libs (kicad-cli sch erc ofensteuerung.kicad_sch)
|
||||
python3 gen_pcb.py # platzierte Platine
|
||||
python3 route.py 30 # Freerouting in 2 Stufen: erst 230 V, dann Kleinspannung (230-V-Bereich gesperrt)
|
||||
kicad-cli pcb drc --schematic-parity ofensteuerung.kicad_pcb
|
||||
python3 fab.py && ./docs.sh # Gerber-zip, Stückliste, PDFs, Bilder
|
||||
```
|
||||
|
||||
Bestellen: `pcb/fab/ofensteuerung_gerber.zip` (2 Lagen, 1,6 mm, HASL, 100 × 100 mm).
|
||||
@@ -0,0 +1,216 @@
|
||||
# Ofensteuerung auf der Platine pcb/ (v1.0)
|
||||
# D0/GPIO16 Relais K1 (230-V-Pumpe, Schließer) - Thermostat an J4 liegt parallel zum Kontakt
|
||||
# D1/GPIO5 SCL, D2/GPIO4 SDA -> ADS1115 (0x48): A0-A1 = Stromwandler ZMCT103C (Bürde 100 R, Mitte 1,65 V)
|
||||
# D5/GPIO14 SCK, D6/GPIO12 SO, D8/GPIO15 CS -> MAX6675 (Abgas, Thermoelement Typ K)
|
||||
# D4/GPIO2 1-Wire, 3x DS18B20 (J5-J7, 4k7 Pull-up)
|
||||
# D3/GPIO0 Servo MG996R (Luftklappe, 330 R in Serie)
|
||||
# D7/GPIO13 PWM-Pumpe (Transistorstufe wie Umwälzpumpe), RX/GPIO3 Rückmeldung
|
||||
# frei auf J10: A0, TX/GPIO1, ADS1115 A2/A3
|
||||
substitutions:
|
||||
name: ofensteuerung
|
||||
|
||||
esphome:
|
||||
name: ${name}
|
||||
friendly_name: Ofensteuerung
|
||||
|
||||
esp8266:
|
||||
board: d1_mini
|
||||
restore_from_flash: true
|
||||
|
||||
logger:
|
||||
level: INFO
|
||||
baud_rate: 0 # RX/GPIO3 = Rückmeldung PWM-Pumpe
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
reboot_timeout: 15min
|
||||
ap:
|
||||
ssid: "Ofensteuerung Fallback"
|
||||
|
||||
captive_portal:
|
||||
|
||||
api:
|
||||
reboot_timeout: 0s
|
||||
encryption:
|
||||
key: !secret api_key
|
||||
|
||||
mqtt:
|
||||
broker: 192.168.179.24
|
||||
port: 1883
|
||||
username: ramiro
|
||||
password: !secret mqtt_password
|
||||
topic_prefix: gunter/ofensteuerung
|
||||
reboot_timeout: 0s
|
||||
discovery: true
|
||||
log_topic: null
|
||||
birth_message:
|
||||
topic: gunter/ofensteuerung/status
|
||||
payload: online
|
||||
will_message:
|
||||
topic: gunter/ofensteuerung/status
|
||||
payload: offline
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
i2c:
|
||||
sda: GPIO4 # D2
|
||||
scl: GPIO5 # D1
|
||||
scan: true
|
||||
frequency: 400kHz
|
||||
|
||||
spi:
|
||||
clk_pin: GPIO14 # D5
|
||||
miso_pin: GPIO12 # D6
|
||||
|
||||
one_wire:
|
||||
- platform: gpio
|
||||
pin: GPIO2 # D4
|
||||
|
||||
ads1115:
|
||||
- address: 0x48
|
||||
continuous_mode: true
|
||||
|
||||
# ---------------- Pumpe 230 V ----------------
|
||||
switch:
|
||||
- platform: gpio
|
||||
pin: GPIO16 # D0 -> BC546B -> K1
|
||||
name: "Pumpe Relais"
|
||||
id: pumpe_relais
|
||||
restore_mode: RESTORE_DEFAULT_OFF
|
||||
icon: mdi:pump
|
||||
|
||||
# ---------------- Luftklappe (Servo MG996R) ----------------
|
||||
output:
|
||||
- platform: esp8266_pwm
|
||||
id: klappe_pwm
|
||||
pin: GPIO0 # D3 (Boot-Pin, Servo-Eingang ist hochohmig)
|
||||
frequency: 50 Hz
|
||||
- platform: esp8266_pwm
|
||||
id: pump_pwm
|
||||
pin: GPIO13 # D7
|
||||
frequency: 1000 Hz
|
||||
inverted: false # Stufe invertiert: Klemmen-Duty = 1 - power (VTS/AMG Profil H)
|
||||
min_power: 0.16
|
||||
max_power: 0.93
|
||||
zero_means_zero: true
|
||||
|
||||
servo:
|
||||
- id: klappe_servo
|
||||
output: klappe_pwm
|
||||
min_level: 3% # ~0,6 ms -> ggf. an Klappenanschlag anpassen
|
||||
max_level: 12% # ~2,4 ms
|
||||
transition_length: 4s
|
||||
auto_detach_time: 3s # danach kein Haltestrom/Brummen; Klappe hält per Getriebe
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
name: "Luftklappe"
|
||||
id: luftklappe
|
||||
unit_of_measurement: "%"
|
||||
min_value: 0
|
||||
max_value: 100
|
||||
step: 1
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: 50
|
||||
icon: mdi:valve
|
||||
set_action:
|
||||
- servo.write:
|
||||
id: klappe_servo
|
||||
level: !lambda 'return x / 50.0f - 1.0f;' # 0..100 % -> -1..+1
|
||||
|
||||
# ---------------- PWM-Pumpe (optional) ----------------
|
||||
fan:
|
||||
- platform: speed
|
||||
output: pump_pwm
|
||||
name: "PWM-Pumpe"
|
||||
id: pwm_pumpe
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
|
||||
sensor:
|
||||
# Pumpenstrom über ZMCT103C: 1 A primär = 1 mA sekundär = 100 mV an der Bürde
|
||||
- platform: ads1115
|
||||
multiplexer: A0_A1
|
||||
gain: 0.256 # +-256 mV -> bis ca. 1,8 A eff.
|
||||
id: ct_spannung
|
||||
sample_rate: 860
|
||||
update_interval: never
|
||||
internal: true
|
||||
- platform: ct_clamp
|
||||
sensor: ct_spannung
|
||||
name: "Pumpe Strom"
|
||||
id: pumpe_strom
|
||||
sample_duration: 400ms
|
||||
update_interval: 5s
|
||||
unit_of_measurement: A
|
||||
accuracy_decimals: 3
|
||||
filters:
|
||||
- multiply: 10.0 # V an 100 R -> A primär
|
||||
- lambda: 'return x < 0.015f ? 0.0f : x;'
|
||||
- platform: template
|
||||
name: "Pumpe Leistung (ca.)"
|
||||
unit_of_measurement: W
|
||||
device_class: power
|
||||
accuracy_decimals: 0
|
||||
update_interval: 5s
|
||||
lambda: 'return id(pumpe_strom).state * 230.0f * 0.9f;' # Schein -> grob Wirkleistung (cos phi ~0,9 schätzen)
|
||||
|
||||
- platform: max6675
|
||||
cs_pin: GPIO15 # D8 (10k Pull-down auf der Platine)
|
||||
name: "Abgastemperatur"
|
||||
id: abgas
|
||||
update_interval: 5s
|
||||
filters:
|
||||
- sliding_window_moving_average: {window_size: 3, send_every: 1}
|
||||
|
||||
# DS18B20 an J5/J6/J7 (ein Bus). Nach dem ersten Start die Adressen aus dem Log eintragen
|
||||
# und index durch address: 0x... ersetzen, sonst können die Fühler die Reihenfolge tauschen.
|
||||
- platform: dallas_temp
|
||||
name: "Temperatur 1"
|
||||
index: 0
|
||||
update_interval: 10s
|
||||
- platform: dallas_temp
|
||||
name: "Temperatur 2"
|
||||
index: 1
|
||||
update_interval: 10s
|
||||
- platform: dallas_temp
|
||||
name: "Temperatur 3"
|
||||
index: 2
|
||||
update_interval: 10s
|
||||
|
||||
- platform: duty_cycle
|
||||
pin:
|
||||
number: GPIO3 # RX
|
||||
mode: INPUT
|
||||
name: "PWM-Pumpe Rückmeldung"
|
||||
update_interval: 5s
|
||||
accuracy_decimals: 0
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "WLAN Signal"
|
||||
update_interval: 60s
|
||||
- platform: uptime
|
||||
name: "Uptime"
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "Pumpe läuft"
|
||||
device_class: running
|
||||
lambda: 'return id(pumpe_strom).state > 0.03f;'
|
||||
- platform: template
|
||||
name: "Pumpe läuft über Thermostat"
|
||||
lambda: 'return id(pumpe_strom).state > 0.03f && !id(pumpe_relais).state;'
|
||||
- platform: template
|
||||
name: "Pumpe Störung" # Relais an, aber kein Strom (Sicherung, Pumpe, Kabel)
|
||||
device_class: problem
|
||||
lambda: 'return id(pumpe_relais).state && id(pumpe_strom).state < 0.03f;'
|
||||
filters:
|
||||
- delayed_on: 20s
|
||||
- platform: status
|
||||
name: "Status"
|
||||
@@ -0,0 +1,5 @@
|
||||
wifi_ssid: "IOT"
|
||||
wifi_password: "..."
|
||||
api_key: "..." # 32 Byte base64: head -c32 /dev/urandom | base64
|
||||
ota_password: "..."
|
||||
mqtt_password: "..."
|
||||
+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