#!/usr/bin/env python3 """ Genera el esquemático KiCad 9 — Sistema de Riego ESP8266. Símbolos propios correctos: NodeMCU ESP8266 v3, HC-SR04, SunFounder 8CH Relay. """ import uuid as _uuid, shutil from pathlib import Path def uid(): return str(_uuid.uuid4()) STEP = 2.54 def n_y(n_pins, idx): """Y del pin idx (0-based) para un símbolo con n_pins centrado en 0.""" return round(-(n_pins - 1) / 2.0 * STEP + idx * STEP, 2) # ── Helpers de símbolo ──────────────────────────────────────────────────────── def sym_pin(num, name, ptype, at_x, at_y, angle, length=2.54): return (f' (pin {ptype} line (at {at_x:.2f} {at_y:.2f} {angle}) (length {length:.2f})\n' f' (name "{name}" (effects (font (size 1.27 1.27))))\n' f' (number "{num}" (effects (font (size 1.27 1.27)))))') def sym_rect(x1, y1, x2, y2, w=0): return (f' (rectangle (start {x1:.2f} {y1:.2f}) (end {x2:.2f} {y2:.2f})\n' f' (stroke (width {w}) (type default)) (fill (type background)))') def prop(name, val, ax, ay, angle=0, hide=False): h = '\n (hide yes)' if hide else '' return (f' (property "{name}" "{val}" (at {ax:.2f} {ay:.2f} {angle})\n' f' (effects (font (size 1.27 1.27)){h}))') def symbol_block(lib_sym_name, ref_prefix, body, pins, ref_ax, ref_ay, val_ay, desc=""): """lib_sym_name = 'riego_lib:SymName'""" sym_short = lib_sym_name.split(":")[1] return (f' (symbol "{lib_sym_name}"\n' f' (pin_names (offset 1.016))\n' f' (exclude_from_sim no)\n' f' (in_bom yes)\n' f' (on_board yes)\n' + prop("Reference", ref_prefix, ref_ax, ref_ay) + "\n" + prop("Value", sym_short, ref_ax, val_ay) + "\n" + prop("Footprint", "", 0, 0, hide=True) + "\n" + prop("Datasheet", "", 0, 0, hide=True) + "\n" + prop("Description", desc, 0, 0, hide=True) + "\n" + f' (symbol "{sym_short}_0_1"\n' + sym_rect(*body) + "\n" + f' )\n' + f' (symbol "{sym_short}_1_1"\n' + "\n".join(pins) + "\n" + f' )\n' + f' )') # ── NodeMCU ESP8266 v3 ──────────────────────────────────────────────────────── NM_BW = 10.16 # body half-width NM_BH = 19.05 # body half-height NM_PX_L = -(NM_BW + STEP) # = -12.70 left pin connection X NM_PX_R = (NM_BW + STEP) # = +12.70 right pin connection X LEFT_PINS = [ ("1", "A0", "input"), ("2", "RSV", "passive"), ("3", "RSV", "passive"), ("4", "SD3", "bidirectional"), ("5", "SD2", "bidirectional"), ("6", "SD1/MOSI", "bidirectional"), ("7", "CMD/CS0", "bidirectional"), ("8", "SD0/MISO", "bidirectional"), ("9", "CLK/SCLK", "bidirectional"), ("10", "GND", "power_in"), ("11", "3V3", "power_out"), ("12", "EN", "input"), ("13", "RST", "input"), ("14", "GND", "power_in"), ("15", "VIN", "power_in"), ] RIGHT_PINS = [ ("16", "D0", "bidirectional"), ("17", "D1/SCL", "bidirectional"), ("18", "D2/SDA", "bidirectional"), ("19", "D3", "bidirectional"), ("20", "D4/LED", "bidirectional"), ("21", "3V3", "power_out"), ("22", "GND", "power_in"), ("23", "D5/SCLK", "bidirectional"), ("24", "D6/MISO", "bidirectional"), ("25", "D7/MOSI", "bidirectional"), ("26", "D8/CS", "bidirectional"), ("27", "RX", "input"), ("28", "TX", "output"), ("29", "GND", "power_in"), ("30", "RST", "input"), ] nm_pins = ( [sym_pin(n, name, pt, NM_PX_L, n_y(15, i), 0) for i,(n,name,pt) in enumerate(LEFT_PINS)] + [sym_pin(n, name, pt, NM_PX_R, n_y(15, i), 180) for i,(n,name,pt) in enumerate(RIGHT_PINS)] ) SYM_NODEMCU = symbol_block( "riego_lib:NodeMCU_ESP8266_v3", "U", (-NM_BW, -NM_BH, NM_BW, NM_BH), nm_pins, 0, NM_BH+1.27, NM_BH+3.81, "NodeMCU ESP8266 v3 module") # ── HC-SR04 ─────────────────────────────────────────────────────────────────── HC_PX = -6.35 HC_BODY = (-3.81, -5.08, 3.81, 5.08) HC_PINS_DEF = [ ("1", "VCC", "power_in", HC_PX, n_y(4,0), 0), ("2", "Trig", "input", HC_PX, n_y(4,1), 0), ("3", "Echo", "output", HC_PX, n_y(4,2), 0), ("4", "GND", "power_in", HC_PX, n_y(4,3), 0), ] SYM_HCSR04 = symbol_block( "riego_lib:HC_SR04", "US", HC_BODY, [sym_pin(*p) for p in HC_PINS_DEF], 0, HC_BODY[3]+1.27, HC_BODY[3]+3.81, "HC-SR04 ultrasonic distance sensor") # ── SunFounder 8-channel relay module ──────────────────────────────────────── RL_BW = 5.08 RL_BH = 12.70 RL_LEFT_PX = -(RL_BW + STEP) # = -7.62 RL_RIGHT_PX = (RL_BW + STEP) # = +7.62 rl_pins = ( [sym_pin(str(i+1), f"IN{i+1}", "input", RL_LEFT_PX, n_y(8,i), 0) for i in range(8)] + [sym_pin("9", "VCC", "power_in", RL_RIGHT_PX, n_y(2,0), 180), sym_pin("10", "GND", "power_in", RL_RIGHT_PX, n_y(2,1), 180)] ) SYM_RELAY = symbol_block( "riego_lib:Relay_SunFounder_8CH", "K", (-RL_BW, -RL_BH, RL_BW, RL_BH), rl_pins, 0, RL_BH+1.27, RL_BH+3.81, "SunFounder 8-channel relay module (active LOW)") # ── Device:R (resistencia estándar KiCad) ──────────────────────────────────── SYM_R = """ (symbol "Device:R" (pin_numbers hide) (pin_names (offset 0)) (exclude_from_sim no) (in_bom yes) (on_board yes) (property "Reference" "R" (at 2.032 0 90) (effects (font (size 1.27 1.27)))) (property "Value" "R" (at 0 0 90) (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))) (symbol "R_0_1" (rectangle (start -1.016 -2.032) (end 1.016 2.032) (stroke (width 0.254) (type default)) (fill (type none)))) (symbol "R_1_1" (pin passive line (at 0 3.81 270) (length 1.778) (name "~" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27))))) (pin passive line (at 0 -3.81 90) (length 1.778) (name "~" (effects (font (size 1.27 1.27)))) (number "2" (effects (font (size 1.27 1.27))))) ) )""" # ── Símbolos de alimentación ────────────────────────────────────────────────── SYM_GND = """ (symbol "power:GND" (power) (pin_names (offset 0)) (exclude_from_sim no) (in_bom no) (on_board no) (property "Reference" "#PWR" (at 0 -6.35 0) (effects (font (size 1.27 1.27)) (hide yes))) (property "Value" "GND" (at 0 -3.81 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))) (symbol "GND_0_1" (polyline (pts (xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27)) (stroke (width 0) (type default)) (fill (type none)))) (symbol "GND_1_1" (pin power_in line (at 0 0 270) (length 0) (name "~" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27)))))) )""" SYM_5V = """ (symbol "power:+5V" (power) (pin_names (offset 0)) (exclude_from_sim no) (in_bom no) (on_board no) (property "Reference" "#PWR" (at 0 3.81 0) (effects (font (size 1.27 1.27)) (hide yes))) (property "Value" "+5V" (at 0 3.81 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))) (symbol "+5V_0_1" (polyline (pts (xy -0.762 1.27) (xy 0 2.54) (xy 0.762 1.27)) (stroke (width 0) (type default)) (fill (type none))) (polyline (pts (xy 0 0) (xy 0 2.54)) (stroke (width 0) (type default)) (fill (type none)))) (symbol "+5V_1_1" (pin power_in line (at 0 0 270) (length 0) (name "~" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27)))))) )""" SYM_3V3 = """ (symbol "power:+3.3V" (power) (pin_names (offset 0)) (exclude_from_sim no) (in_bom no) (on_board no) (property "Reference" "#PWR" (at 0 3.81 0) (effects (font (size 1.27 1.27)) (hide yes))) (property "Value" "+3.3V" (at 0 3.81 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))) (symbol "+3.3V_0_1" (polyline (pts (xy -0.762 1.27) (xy 0 2.54) (xy 0.762 1.27)) (stroke (width 0) (type default)) (fill (type none))) (polyline (pts (xy 0 0) (xy 0 2.54)) (stroke (width 0) (type default)) (fill (type none)))) (symbol "+3.3V_1_1" (pin power_in line (at 0 0 270) (length 0) (name "~" (effects (font (size 1.27 1.27)))) (number "1" (effects (font (size 1.27 1.27)))))) )""" LIB_SYMBOLS = "\n".join([SYM_NODEMCU, SYM_HCSR04, SYM_RELAY, SYM_R, SYM_GND, SYM_5V, SYM_3V3]) # ── Helpers de elementos de esquemático ─────────────────────────────────────── def wire(x1, y1, x2, y2): return (f' (wire (pts (xy {x1:.2f} {y1:.2f}) (xy {x2:.2f} {y2:.2f}))\n' f' (stroke (width 0) (type default)) (uuid "{uid()}"))') def junction(x, y): return f' (junction (at {x:.2f} {y:.2f}) (diameter 0) (color 0 0 0 0) (uuid "{uid()}"))' def no_connect(x, y): return f' (no_connect (at {x:.2f} {y:.2f}) (uuid "{uid()}"))' def net_label(text, x, y, angle=0): return (f' (label "{text}" (at {x:.2f} {y:.2f} {angle}) (fields_autoplaced)\n' f' (effects (font (size 1.27 1.27)))\n' f' (uuid "{uid()}"))') _pwr_cnt = [1] def power_sym(sym, x, y): n = _pwr_cnt[0]; _pwr_cnt[0] += 1 vy = y - 3.81 if sym in ("+5V", "+3.3V") else y + 3.81 return (f' (symbol (lib_id "power:{sym}") (at {x:.2f} {y:.2f} 0) (unit 1)\n' f' (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n' f' (uuid "{uid()}")\n' f' (property "Reference" "#PWR{n:02d}" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (property "Value" "{sym}" (at {x:.2f} {vy:.2f} 0)\n' f' (effects (font (size 1.27 1.27))))\n' f' (property "Footprint" "" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (property "Datasheet" "" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (pin "1" (uuid "{uid()}")))') def place_comp(lib_id, ref, value, x, y, pin_nums, ref_dx=2, ref_dy=-2, val_dx=2, val_dy=2): pins = "\n".join(f' (pin "{n}" (uuid "{uid()}"))' for n in pin_nums) return (f' (symbol (lib_id "{lib_id}") (at {x:.2f} {y:.2f} 0) (unit 1)\n' f' (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n' f' (uuid "{uid()}")\n' f' (property "Reference" "{ref}" (at {x+ref_dx:.2f} {y+ref_dy:.2f} 0)\n' f' (effects (font (size 1.27 1.27))))\n' f' (property "Value" "{value}" (at {x+val_dx:.2f} {y+val_dy:.2f} 0)\n' f' (effects (font (size 1.27 1.27))))\n' f' (property "Footprint" "" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (property "Datasheet" "" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f'{pins}\n )') def place_resistor(ref, value, x, y, rotation=0): """rotation=0 → vertical; rotation=90 → horizontal.""" if rotation == 90: rx, ry, ra = x, y - 2.54, 0 else: rx, ry, ra = x + 2.032, y, 90 return (f' (symbol (lib_id "Device:R") (at {x:.2f} {y:.2f} {rotation}) (unit 1)\n' f' (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n' f' (uuid "{uid()}")\n' f' (property "Reference" "{ref}" (at {rx:.2f} {ry:.2f} {ra})\n' f' (effects (font (size 1.27 1.27))))\n' f' (property "Value" "{value}" (at {rx+2.54:.2f} {ry:.2f} {ra})\n' f' (effects (font (size 1.27 1.27))))\n' f' (property "Footprint" "" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (property "Datasheet" "~" (at {x:.2f} {y:.2f} 0)\n' f' (effects (font (size 1.27 1.27)) (hide yes)))\n' f' (pin "1" (uuid "{uid()}"))\n' f' (pin "2" (uuid "{uid()}"))\n )') # ── Posiciones y coordenadas ────────────────────────────────────────────────── # # U1 NodeMCU center (80, 100) # US1 HC-SR04 center (175, 80) # K1 Relay center (175, 145) # # NodeMCU right-side pin X = 80 + 12.70 = 92.70 # NodeMCU left-side pin X = 80 - 12.70 = 67.30 NM_CX, NM_CY = 80.0, 100.0 HC_CX, HC_CY = 175.0, 80.0 RL_CX, RL_CY = 175.0, 145.0 NM_RPX = NM_CX + NM_PX_R # 92.70 NM_LPX = NM_CX + NM_PX_L # 67.30 def nm_ry(idx): return round(NM_CY + n_y(15, idx), 2) # right side def nm_ly(idx): return round(NM_CY + n_y(15, idx), 2) # left side (same Y mapping) # HC-SR04: pins on left at X = 175 + HC_PX = 175 - 6.35 = 168.65 HC_PPX = HC_CX + HC_PX def hc_y(idx): return round(HC_CY + n_y(4, idx), 2) # Relay: left-side pins at X = 175 - 7.62 = 167.38 # right-side pins at X = 175 + 7.62 = 182.62 RL_LPX = RL_CX + RL_LEFT_PX RL_RPX = RL_CX + RL_RIGHT_PX def rl_ly(idx): return round(RL_CY + n_y(8, idx), 2) # IN1-IN8 def rl_ry(idx): return round(RL_CY + n_y(2, idx), 2) # VCC, GND # Device:R con rotation=90 (horizontal): # pin1 conecta en (X-3.81, Y) [izquierda] # pin2 conecta en (X+3.81, Y) [derecha] # Device:R con rotation=0 (vertical): # pin1 conecta en (X, Y+3.81) [abajo] # pin2 conecta en (X, Y-3.81) [arriba] # Divisor de tensión # R1A y R1B en paralelo (horizontal) entre D2/GPIO4 y ECHO_DIV # R3 vertical de ECHO_DIV a GND R1A_CX, R1A_CY = 125.0, 83.0 R1B_CX, R1B_CY = 125.0, 85.54 R1A_LP = (R1A_CX - 3.81, R1A_CY) # (121.19, 83.0) R1B_LP = (R1B_CX - 3.81, R1B_CY) # (121.19, 85.54) R1A_RP = (R1A_CX + 3.81, R1A_CY) # (128.81, 83.0) R1B_RP = (R1B_CX + 3.81, R1B_CY) # (128.81, 85.54) R3_CX, R3_CY = 128.81, 90.0 R3_TOP = (R3_CX, R3_CY - 3.81) # (128.81, 86.19) R3_BOT = (R3_CX, R3_CY + 3.81) # (128.81, 93.81) # ── Ensamblado de elementos ─────────────────────────────────────────────────── E = [] # U1 — NodeMCU E.append(place_comp( "riego_lib:NodeMCU_ESP8266_v3", "U1", "NodeMCU_ESP8266_v3", NM_CX, NM_CY, [str(i) for i in range(1, 31)], ref_dx=11.5, ref_dy=-21, val_dx=0, val_dy=21)) # No-connect pines no usados del NodeMCU # Izquierda: A0(0), RSV(1), RSV(2), SD3(3), SD2(4), SD1(5), CMD(6), SD0(7), CLK(8), EN(11) for idx in [0,1,2,3,4,5,6,7,8,11]: E.append(no_connect(NM_LPX, nm_ly(idx))) # Derecha: D0(0), D3(3), D4(4), D6(8), D7(9), D8(10), RX(11), TX(12) for idx in [0,3,4,8,9,10,11,12]: E.append(no_connect(NM_RPX, nm_ry(idx))) # Pines de potencia NodeMCU izquierda: GND(9), 3V3(10), RST(12→nc), GND(13), VIN(14→nc) E.append(power_sym("GND", NM_LPX, nm_ly(9))) E.append(power_sym("+3.3V", NM_LPX, nm_ly(10))) E.append(no_connect(NM_LPX, nm_ly(12))) # RST E.append(power_sym("GND", NM_LPX, nm_ly(13))) E.append(no_connect(NM_LPX, nm_ly(14))) # VIN # Pines de potencia NodeMCU derecha: 3V3(5), GND(6), GND(13), RST(14→nc) E.append(power_sym("+3.3V", NM_RPX, nm_ry(5))) E.append(power_sym("GND", NM_RPX, nm_ry(6))) E.append(power_sym("GND", NM_RPX, nm_ry(13))) E.append(no_connect(NM_RPX, nm_ry(14))) # RST # Etiquetas de señal en NodeMCU (pines derecha que usamos) # D1/SCL = idx 1, D2/SDA = idx 2, D5/SCLK = idx 7 E.append(net_label("D1/GPIO5", NM_RPX, nm_ry(1), angle=0)) E.append(net_label("D2_TRIG", NM_RPX, nm_ry(2), angle=0)) E.append(net_label("D5_ECHO", NM_RPX, nm_ry(7), angle=0)) # US1 — HC-SR04 E.append(place_comp( "riego_lib:HC_SR04", "US1", "HC_SR04", HC_CX, HC_CY, ["1","2","3","4"], ref_dx=5, ref_dy=-7, val_dx=5, val_dy=-4)) E.append(power_sym("+5V", HC_PPX, hc_y(0))) # VCC E.append(power_sym("GND", HC_PPX, hc_y(3))) # GND E.append(net_label("D2_TRIG", HC_PPX, hc_y(1), angle=180)) # Trig ← GPIO4/D2 E.append(net_label("ECHO_DIV", HC_PPX, hc_y(2), angle=180)) # Echo → divisor → D5/GPIO14 # K1 — Relay SunFounder 8CH E.append(place_comp( "riego_lib:Relay_SunFounder_8CH", "K1", "Relay_SunFounder_8CH", RL_CX, RL_CY, [str(i) for i in range(1, 11)], ref_dx=6, ref_dy=-14, val_dx=6, val_dy=-11)) E.append(power_sym("+5V", RL_RPX, rl_ry(0))) # VCC E.append(power_sym("GND", RL_RPX, rl_ry(1))) # GND E.append(net_label("D1/GPIO5", RL_LPX, rl_ly(0), angle=180)) # IN1 # No-connect IN2-IN8 for idx in range(1, 8): E.append(no_connect(RL_LPX, rl_ly(idx))) # ── Divisor de tensión ──────────────────────────────────────────────────────── E.append(place_resistor("R1A", "2.2k", R1A_CX, R1A_CY, rotation=90)) E.append(place_resistor("R1B", "2.2k", R1B_CX, R1B_CY, rotation=90)) E.append(place_resistor("R3", "2.2k", R3_CX, R3_CY, rotation=0)) # Lado izquierdo del paralelo — net D2/GPIO4 E.append(wire(R1A_LP[0], R1A_LP[1], R1B_LP[0], R1B_LP[1])) # vertical une R1A y R1B left E.append(net_label("ECHO_DIV", R1A_LP[0], R1A_LP[1], angle=180)) # Echo HC-SR04 → aquí # Lado derecho del paralelo — net D5_ECHO (sale hacia GPIO14/D5) E.append(wire(R1A_RP[0], R1A_RP[1], R1B_RP[0], R1B_RP[1])) # vertical une R1A y R1B right E.append(wire(R1B_RP[0], R1B_RP[1], R3_TOP[0], R3_TOP[1])) # baja hasta R3 top E.append(junction(R1B_RP[0], R1B_RP[1])) E.append(net_label("D5_ECHO", R1A_RP[0], R1A_RP[1], angle=0)) # → GPIO14/D5 # GND en R3 bottom E.append(power_sym("GND", R3_BOT[0], R3_BOT[1])) # ── Generar fichero ─────────────────────────────────────────────────────────── body = "\n".join(E) schematic = f"""(kicad_sch (version 20250114) (generator "eeschema") (generator_version "9.0") (uuid "{uid()}") (paper "A4") (title_block (title "Sistema de Riego — Diagrama de cableado") (date "2026-05-12") (rev "2") (company "Proyecto Riego ESP8266")) (lib_symbols {LIB_SYMBOLS} ) {body} (sheet_instances (path "/" (page "1"))) ) """ out = Path(__file__).parent / "riego.kicad_sch" out.write_text(schematic, encoding="utf-8") print(f"Generado: {out}") dst = Path(__file__).parent / "esp8266" / "esp8266.kicad_sch" shutil.copy(out, dst) print(f"Copiado a: {dst}")