e1a116d0f5
Custom ESP32-WROOM-32E + Si3210 SLIC board replacing the ESP32-A1S dev kit (final PLIP target, RJ9 handset). Generated via tools/ gen_si3210_lib.py (symbol + QFN-38 footprint), gen_schematic.py (parses standard KiCad libs, 45 instances, 157 global labels on pin ends) and gen_pcb.py (58x38mm outline, 41 footprints placed per the plan, nets assigned, unrouted). ERC: 0 violations. Schematic follows the spec GPIO map and the plan's passive network. PCB placed not routed (Freerouting + DRC + gerbers remain — see README). BOM in JLCPCB format; CP2102N/transformer/RJ9 LCSC parts marked TBD. WARNINGS (README): the Si3210 -72V DC-DC stage is simplified and must be checked against the Skyworks datasheet/AN35 before fab; line protection is minimal; human review required before ordering.
452 lines
19 KiB
Python
452 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
"""Génère plip-telephone.kicad_sch (format 20231120).
|
|
|
|
Pipeline :
|
|
1. extrait les blocs (symbol "NOM" ...) des libs standard KiCad + lib locale Si3210 ;
|
|
2. les embarque dans (lib_symbols ...) sous l'id "Lib:Nom" (aplatissement des extends) ;
|
|
3. place les instances en rotation 0 et connecte par labels globaux posés
|
|
exactement à l'extrémité des broches (positions absolues calculées depuis
|
|
les (pin ... (at x y angle)) du symbole) ;
|
|
4. pose des (no_connect) sur les broches libres ;
|
|
5. exporte tools/netmap.json (ref -> pad -> net) réutilisé par gen_pcb.py.
|
|
"""
|
|
import copy
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import uuid
|
|
|
|
KICAD_SYMS = "/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols"
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
PROJ = os.path.dirname(HERE)
|
|
|
|
ROOT_UUID = "5a3c9d2e-1f47-4b8a-9c61-7e2d84f0b3a1"
|
|
PROJECT_NAME = "plip-telephone"
|
|
|
|
NC = "NC" # marqueur broche libre -> (no_connect)
|
|
|
|
|
|
# --------------------------------------------------------------------------- sexpr
|
|
def tokenize(text):
|
|
return re.findall(r'"(?:[^"\\]|\\.)*"|\(|\)|[^\s()"]+', text)
|
|
|
|
|
|
def parse(tokens):
|
|
pos = [0]
|
|
|
|
def rec():
|
|
assert tokens[pos[0]] == "("
|
|
pos[0] += 1
|
|
out = []
|
|
while tokens[pos[0]] != ")":
|
|
if tokens[pos[0]] == "(":
|
|
out.append(rec())
|
|
else:
|
|
out.append(tokens[pos[0]])
|
|
pos[0] += 1
|
|
pos[0] += 1
|
|
return out
|
|
|
|
return rec()
|
|
|
|
|
|
def serialize(node, indent=0):
|
|
if isinstance(node, str):
|
|
return node
|
|
inner = [serialize(c, indent + 1) for c in node]
|
|
one = "(" + " ".join(inner) + ")"
|
|
if len(one) <= 110 and "\n" not in one:
|
|
return one
|
|
pad = " " * (indent + 1)
|
|
head = []
|
|
i = 0
|
|
while i < len(node) and isinstance(node[i], str):
|
|
head.append(inner[i])
|
|
i += 1
|
|
out = "(" + " ".join(head)
|
|
for j in range(i, len(inner)):
|
|
out += "\n" + pad + inner[j].replace("\n", "\n")
|
|
out += ")"
|
|
return out
|
|
|
|
|
|
def q(s):
|
|
return '"%s"' % s
|
|
|
|
|
|
def unq(s):
|
|
return s[1:-1] if isinstance(s, str) and s.startswith('"') else s
|
|
|
|
|
|
def fnum(v):
|
|
s = ("%.4f" % float(v)).rstrip("0").rstrip(".")
|
|
return s if s not in ("-0",) else "0"
|
|
|
|
|
|
def new_uuid():
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
# --------------------------------------------------------------------------- libs
|
|
_lib_cache = {}
|
|
|
|
|
|
def load_lib(path):
|
|
if path not in _lib_cache:
|
|
_lib_cache[path] = parse(tokenize(open(path).read()))
|
|
return _lib_cache[path]
|
|
|
|
|
|
def lib_path(libname):
|
|
if libname == "Si3210":
|
|
return os.path.join(PROJ, "libs", "Si3210.kicad_sym")
|
|
return os.path.join(KICAD_SYMS, libname + ".kicad_sym")
|
|
|
|
|
|
def find_symbol(tree, name):
|
|
for n in tree:
|
|
if isinstance(n, list) and n and n[0] == "symbol" and unq(n[1]) == name:
|
|
return n
|
|
return None
|
|
|
|
|
|
def strip_nodes(node, names):
|
|
if not isinstance(node, list):
|
|
return node
|
|
return [strip_nodes(c, names) for c in node
|
|
if not (isinstance(c, list) and c and c[0] in names)]
|
|
|
|
|
|
def get_prop(sym, pname):
|
|
for n in sym:
|
|
if isinstance(n, list) and n and n[0] == "property" and unq(n[1]) == pname:
|
|
return n
|
|
return None
|
|
|
|
|
|
def set_prop_value(sym, pname, value):
|
|
p = get_prop(sym, pname)
|
|
if p is not None:
|
|
p[2] = q(value)
|
|
else:
|
|
sym.append(["property", q(pname), q(value),
|
|
["at", "0", "0", "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]], "hide"]])
|
|
|
|
|
|
def extract_symbol(libname, symname):
|
|
"""Retourne le bloc symbole aplati (extends résolu), renommé Lib:Nom."""
|
|
tree = load_lib(lib_path(libname))
|
|
sym = find_symbol(tree, symname)
|
|
if sym is None:
|
|
raise SystemExit("symbole introuvable: %s:%s" % (libname, symname))
|
|
sym = copy.deepcopy(sym)
|
|
|
|
ext = [n for n in sym if isinstance(n, list) and n and n[0] == "extends"]
|
|
if ext:
|
|
base_name = unq(ext[0][1])
|
|
base = copy.deepcopy(find_symbol(tree, base_name))
|
|
if base is None:
|
|
raise SystemExit("base extends introuvable: %s" % base_name)
|
|
# part des graphismes/broches/attributs de la base, propriétés du dérivé
|
|
merged = [n for n in base if not (isinstance(n, list) and n and n[0] == "property")]
|
|
merged[1] = q(symname)
|
|
# renomme les sous-symboles BASE_x_y -> DERIVE_x_y
|
|
for n in merged:
|
|
if isinstance(n, list) and n and n[0] == "symbol":
|
|
n[1] = q(unq(n[1]).replace(base_name, symname))
|
|
props = [n for n in sym if isinstance(n, list) and n and n[0] == "property"]
|
|
# insère les propriétés après le nom
|
|
merged[2:2] = props
|
|
sym = merged
|
|
|
|
sym[1] = q("%s:%s" % (libname, symname))
|
|
sym = strip_nodes(sym, {"embedded_fonts"})
|
|
return sym
|
|
|
|
|
|
def expand_numbers(numstr):
|
|
if numstr.startswith("[") and numstr.endswith("]"):
|
|
return [x.strip() for x in numstr[1:-1].split(",")]
|
|
return [numstr]
|
|
|
|
|
|
def symbol_pins(sym):
|
|
"""[(numbers[], name, etype, x, y, angle)] sur tous les sous-symboles."""
|
|
pins = []
|
|
for sub in sym:
|
|
if not (isinstance(sub, list) and sub and sub[0] == "symbol"):
|
|
continue
|
|
for p in sub:
|
|
if not (isinstance(p, list) and p and p[0] == "pin"):
|
|
continue
|
|
at = next(x for x in p if isinstance(x, list) and x[0] == "at")
|
|
name = next(unq(x[1]) for x in p if isinstance(x, list) and x[0] == "name")
|
|
nums = next(unq(x[1]) for x in p if isinstance(x, list) and x[0] == "number")
|
|
pins.append((expand_numbers(nums), name, p[1],
|
|
float(at[1]), float(at[2]), float(at[3])))
|
|
return pins
|
|
|
|
|
|
# --------------------------------------------------------------------------- composants
|
|
# (ref, lib, symbole, valeur, footprint, LCSC, (x, y) schéma, {pad: net|NC})
|
|
COMPONENTS = [
|
|
("U1", "RF_Module", "ESP32-WROOM-32E", "ESP32-WROOM-32E-N16",
|
|
"RF_Module:ESP32-WROOM-32E", "C701341", (170, 160), {
|
|
"1": "GND", "15": "GND", "38": "GND", "39": "GND",
|
|
"2": "3V3", "3": "ESP_EN", "25": "ESP_IO0",
|
|
"30": "SPI_SCLK", "37": "SPI_MOSI", "31": "SPI_MISO",
|
|
"29": "SLIC_CS", "23": "SD_CS", "26": "SLIC_INT", "24": "SLIC_RST",
|
|
"11": "PCM_PCLK", "10": "PCM_FSYNC", "36": "PCM_DRX", "7": "PCM_DTX",
|
|
"28": "UART_UI_TX", "27": "UART_UI_RX",
|
|
"35": "ESP_TXD0", "34": "ESP_RXD0", "16": "LED_STATUS",
|
|
"4": NC, "5": NC, "6": NC, "8": NC, "9": NC, "12": NC, "13": NC,
|
|
"14": NC, "17": NC, "18": NC, "19": NC, "20": NC, "21": NC,
|
|
"22": NC, "32": NC, "33": NC,
|
|
}),
|
|
("U2", "Si3210", "Si3210", "Si3210-E-FM",
|
|
"Si3210:Si3210_QFN38", "C6295850", (260, 200), {
|
|
"34": "SPI_SCLK", "33": "SPI_MOSI", "32": "SPI_MISO", "35": "SLIC_CS",
|
|
"37": "PCM_PCLK", "2": "PCM_FSYNC", "1": "PCM_DTX", "38": "PCM_DRX",
|
|
"3": "SLIC_RST", "36": "SLIC_INT", "28": "GND",
|
|
"6": "3V3", "23": "3V3", "26": "3V3",
|
|
"19": "GND", "27": "GND", "9": "GND", "39": "GND",
|
|
"16": "TIP_AC", "17": "RING_AC",
|
|
"30": "DCDRV", "14": "SVBAT", "29": "DCFF",
|
|
"7": "IREF", "8": "CAPP", "10": "CAPM",
|
|
"11": NC, "12": NC, "13": NC, "15": NC, "18": NC, "20": NC,
|
|
"21": NC, "22": NC, "24": NC, "25": NC, "4": NC, "5": NC, "31": NC,
|
|
}),
|
|
("U3", "Interface_USB", "CP2102N-Axx-xQFN20", "CP2102N-A02-GQFN20",
|
|
"Package_DFN_QFN:QFN-20-1EP_3x3mm_P0.4mm_EP1.65x1.65mm", "TBD", (90, 110), {
|
|
"3": "GND", "12": "GND", "21": "GND",
|
|
"6": "CP_VDD", "7": "5V", "8": "VBUS",
|
|
"4": "CP_DP", "5": "CP_DM",
|
|
"17": "ESP_TXD0", "18": "ESP_RXD0", "16": "CP_RTS",
|
|
"1": NC, "2": NC, "9": NC, "10": NC, "11": NC, "13": NC,
|
|
"14": NC, "15": NC, "19": NC, "20": NC,
|
|
}),
|
|
("U4", "Power_Protection", "USBLC6-2SC6", "USBLC6-2SC6",
|
|
"Package_TO_SOT_SMD:SOT-23-6", "C7519", (90, 55), {
|
|
"1": "USB_DP", "3": "USB_DM", "6": "USB_DP_R", "4": "USB_DM_R",
|
|
"5": "VBUS", "2": "GND",
|
|
}),
|
|
("U5", "Regulator_Linear", "ME6211C33M5", "ME6211C33M5G-N",
|
|
"Package_TO_SOT_SMD:SOT-23-5", "C82942", (140, 50), {
|
|
"1": "5V", "2": "GND", "3": "5V", "5": "3V3", "4": NC,
|
|
}),
|
|
("J1", "Connector", "USB_C_Receptacle_USB2.0_16P", "USB-C 16P",
|
|
"Connector_USB:USB_C_Receptacle_HRO_TYPE-C-31-M-12", "C2765186", (45, 60), {
|
|
"A4": "VBUS", "A9": "VBUS", "B4": "VBUS", "B9": "VBUS",
|
|
"A1": "GND", "B1": "GND", "A12": "GND", "B12": "GND", "SH": "GND",
|
|
"A5": "CC1", "B5": "CC2",
|
|
"A6": "USB_DP", "B6": "USB_DP", "A7": "USB_DM", "B7": "USB_DM",
|
|
"A8": NC, "B8": NC,
|
|
}),
|
|
("J2", "Connector", "4P4C", "RJ9_4P4C",
|
|
"Si3210:RJ9_4P4C", "TBD", (375, 245), {
|
|
"1": NC, "2": "PHONE_TIP", "3": "PHONE_RING", "4": NC,
|
|
}),
|
|
("J3", "Connector_Generic", "Conn_01x04", "JST_SH_UI_LINK",
|
|
"Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical", "C265021", (375, 200), {
|
|
"1": "3V3", "2": "UART_UI_TX", "3": "UART_UI_RX", "4": "GND",
|
|
}),
|
|
("SD1", "Connector", "Micro_SD_Card", "Micro_SD_SPI",
|
|
"Connector_Card:microSD_HC_Hirose_DM3AT-SF-PEJM5", "C585353", (260, 90), {
|
|
"1": NC, "2": "SD_CS", "3": "SPI_MOSI", "4": "3V3",
|
|
"5": "SPI_SCLK", "6": "GND", "7": "SPI_MISO", "8": NC, "SH": "GND",
|
|
}),
|
|
("TR1", "Device", "Transformer_1P_1S", "600R:600R",
|
|
"Si3210:Transformer_600_600", "TBD", (340, 245), {
|
|
"1": "TIP_AC", "2": "RING_AC", "3": "PHONE_RING", "4": "PHONE_TIP",
|
|
}),
|
|
("FB1", "Device", "FerriteBead", "FB_600R@100MHz",
|
|
"Inductor_SMD:L_0805_2012Metric", "TBD", (120, 45), {
|
|
"1": "VBUS", "2": "5V",
|
|
}),
|
|
("L1", "Device", "L", "100uH",
|
|
"Inductor_SMD:L_1210_3225Metric", "TBD", (310, 185), {
|
|
"1": "DCDRV", "2": "SVBAT",
|
|
}),
|
|
# --- résistances
|
|
("R1", "Device", "R", "5.1k", "Resistor_SMD:R_0402_1005Metric", "TBD", (105, 75),
|
|
{"1": "CC1", "2": "GND"}),
|
|
("R2", "Device", "R", "5.1k", "Resistor_SMD:R_0402_1005Metric", "TBD", (112, 75),
|
|
{"1": "CC2", "2": "GND"}),
|
|
("R3", "Device", "R", "22R", "Resistor_SMD:R_0402_1005Metric", "TBD", (115, 55),
|
|
{"1": "USB_DP_R", "2": "CP_DP"}),
|
|
("R4", "Device", "R", "22R", "Resistor_SMD:R_0402_1005Metric", "TBD", (122, 55),
|
|
{"1": "USB_DM_R", "2": "CP_DM"}),
|
|
("R5", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (140, 120),
|
|
{"1": "3V3", "2": "ESP_EN"}),
|
|
("R6", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (147, 120),
|
|
{"1": "3V3", "2": "ESP_IO0"}),
|
|
("R7", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (154, 120),
|
|
{"1": "3V3", "2": "SPI_MOSI"}),
|
|
("R8", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (161, 120),
|
|
{"1": "3V3", "2": "SPI_MISO"}),
|
|
("R9", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (168, 120),
|
|
{"1": "3V3", "2": "SD_CS"}),
|
|
("R15", "Device", "R", "10k", "Resistor_SMD:R_0402_1005Metric", "C25744", (175, 120),
|
|
{"1": "3V3", "2": "SLIC_INT"}),
|
|
("R10", "Device", "R", "20k", "Resistor_SMD:R_0402_1005Metric", "TBD", (300, 235),
|
|
{"1": "IREF", "2": "GND"}),
|
|
("R11", "Device", "R", "100k", "Resistor_SMD:R_0402_1005Metric", "TBD", (320, 185),
|
|
{"1": "SVBAT", "2": "DCFF"}),
|
|
("R12", "Device", "R", "4.7k", "Resistor_SMD:R_0402_1005Metric", "TBD", (330, 185),
|
|
{"1": "DCFF", "2": "GND"}),
|
|
("R13", "Device", "R", "1k", "Resistor_SMD:R_0402_1005Metric", "TBD", (210, 170),
|
|
{"1": "3V3", "2": "LED_PWR_A"}),
|
|
("R14", "Device", "R", "1k", "Resistor_SMD:R_0402_1005Metric", "TBD", (210, 160),
|
|
{"1": "LED_STATUS", "2": "LED_ST_A"}),
|
|
# --- condensateurs
|
|
("C1", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (200, 120),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C2", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (340, 120),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C3", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (347, 120),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C4", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (354, 120),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C5", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (110, 125),
|
|
{"1": "CP_VDD", "2": "GND"}),
|
|
("C6", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (290, 90),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C7", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (120, 125),
|
|
{"1": "CP_RTS", "2": "ESP_EN"}),
|
|
("C8", "Device", "C", "100nF", "Capacitor_SMD:C_0402_1005Metric", "C307331", (310, 225),
|
|
{"1": "CAPP", "2": "CAPM"}),
|
|
("C9", "Device", "C", "1uF", "Capacitor_SMD:C_0402_1005Metric", "TBD", (155, 50),
|
|
{"1": "5V", "2": "GND"}),
|
|
("C10", "Device", "C", "1uF", "Capacitor_SMD:C_0402_1005Metric", "TBD", (165, 50),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C11", "Device", "C", "10uF", "Capacitor_SMD:C_0805_2012Metric", "C19702", (175, 50),
|
|
{"1": "3V3", "2": "GND"}),
|
|
("C12", "Device", "C", "10uF", "Capacitor_SMD:C_0805_2012Metric", "C19702", (185, 50),
|
|
{"1": "3V3", "2": "GND"}),
|
|
# --- LEDs
|
|
("D1", "Device", "LED", "LED_PWR", "LED_SMD:LED_0402_1005Metric", "C2286", (220, 170),
|
|
{"2": "LED_PWR_A", "1": "GND"}),
|
|
("D2", "Device", "LED", "LED_STATUS", "LED_SMD:LED_0402_1005Metric", "C2286", (220, 160),
|
|
{"2": "LED_ST_A", "1": "GND"}),
|
|
# --- PWR_FLAG (pas sur 3V3 : déjà piloté par V_OUT du LDO, power output)
|
|
("PF1", "power", "PWR_FLAG", "PWR_FLAG", "", "", (200, 40), {"1": "GND"}),
|
|
("PF3", "power", "PWR_FLAG", "PWR_FLAG", "", "", (212, 40), {"1": "5V"}),
|
|
("PF4", "power", "PWR_FLAG", "PWR_FLAG", "", "", (218, 40), {"1": "VBUS"}),
|
|
("PF5", "power", "PWR_FLAG", "PWR_FLAG", "", "", (224, 40), {"1": "CP_VDD"}),
|
|
]
|
|
|
|
|
|
LABEL_ANGLE = {0: 180, 180: 0, 90: 270, 270: 90}
|
|
|
|
|
|
def main():
|
|
# 1+2. lib_symbols embarqués
|
|
needed = {}
|
|
for (_, libname, symname, *_rest) in [(c[0], c[1], c[2]) for c in COMPONENTS]:
|
|
needed[(libname, symname)] = True
|
|
lib_symbols = ["lib_symbols"]
|
|
pins_by_sym = {}
|
|
for (libname, symname) in needed:
|
|
sym = extract_symbol(libname, symname)
|
|
lib_symbols.append(sym)
|
|
pins_by_sym[(libname, symname)] = symbol_pins(sym)
|
|
|
|
body = []
|
|
labels = {} # (x, y) -> (net, angle) déduplication par position
|
|
no_connects = [] # (x, y)
|
|
netmap = {}
|
|
|
|
for (ref, libname, symname, value, footprint, lcsc, (X, Y), conns) in COMPONENTS:
|
|
# snap sur la grille 1.27 mm (les offsets de pins sont des multiples de 1.27)
|
|
X = round(round(X / 1.27) * 1.27, 2)
|
|
Y = round(round(Y / 1.27) * 1.27, 2)
|
|
pins = pins_by_sym[(libname, symname)]
|
|
all_nums = [n for p in pins for n in p[0]]
|
|
missing = set(all_nums) - set(conns)
|
|
extra = set(conns) - set(all_nums)
|
|
if missing or extra:
|
|
raise SystemExit("%s: broches manquantes=%s en-trop=%s" % (ref, sorted(missing), sorted(extra)))
|
|
|
|
inst = ["symbol", ["lib_id", q("%s:%s" % (libname, symname))],
|
|
["at", fnum(X), fnum(Y), "0"], ["unit", "1"],
|
|
["exclude_from_sim", "no"], ["in_bom", "yes" if libname != "power" else "no"],
|
|
["on_board", "yes"], ["dnp", "no"], ["uuid", q(new_uuid())],
|
|
["property", q("Reference"), q(ref),
|
|
["at", fnum(X), fnum(Y - 2.54), "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]]]],
|
|
["property", q("Value"), q(value),
|
|
["at", fnum(X), fnum(Y + 2.54), "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]]]],
|
|
["property", q("Footprint"), q(footprint),
|
|
["at", fnum(X), fnum(Y), "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]], "hide"]],
|
|
["property", q("Datasheet"), q("~"),
|
|
["at", fnum(X), fnum(Y), "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]], "hide"]],
|
|
]
|
|
if lcsc:
|
|
inst.append(["property", q("LCSC"), q(lcsc),
|
|
["at", fnum(X), fnum(Y), "0"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]], "hide"]])
|
|
inst.append(["instances",
|
|
["project", q(PROJECT_NAME),
|
|
["path", q("/" + ROOT_UUID),
|
|
["reference", q(ref)], ["unit", "1"]]]])
|
|
body.append(inst)
|
|
|
|
# labels / no_connect aux extrémités de broches (rotation 0, pas de miroir :
|
|
# absolu = (X + px, Y - py))
|
|
for (nums, _name, _etype, px, py, pang) in pins:
|
|
ax = round(X + px, 2)
|
|
ay = round(Y - py, 2)
|
|
nets = {conns[n] for n in nums}
|
|
if len(nets) != 1:
|
|
raise SystemExit("%s broches empilées %s avec nets divergents %s" % (ref, nums, nets))
|
|
net = nets.pop()
|
|
if net == NC:
|
|
no_connects.append((ax, ay))
|
|
else:
|
|
key = (ax, ay)
|
|
if key in labels and labels[key][0] != net:
|
|
raise SystemExit("collision de labels en %s: %s vs %s" % (key, labels[key][0], net))
|
|
labels[key] = (net, LABEL_ANGLE[int(pang) % 360])
|
|
|
|
if footprint:
|
|
netmap[ref] = {
|
|
"footprint": footprint, "value": value, "lcsc": lcsc,
|
|
"pads": {n: (None if v == NC else v) for n, v in conns.items()},
|
|
}
|
|
|
|
for (x, y), (net, ang) in sorted(labels.items()):
|
|
body.append(["global_label", q(net), ["shape", "passive"],
|
|
["at", fnum(x), fnum(y), str(ang)],
|
|
["fields_autoplaced", "yes"],
|
|
["effects", ["font", ["size", "1.27", "1.27"]],
|
|
["justify", "right" if ang == 180 else "left"]],
|
|
["uuid", q(new_uuid())]])
|
|
for (x, y) in sorted(no_connects):
|
|
body.append(["no_connect", ["at", fnum(x), fnum(y)], ["uuid", q(new_uuid())]])
|
|
|
|
doc = ["kicad_sch", ["version", "20231120"], ["generator", q("gen_schematic.py")],
|
|
["generator_version", q("8.0")], ["uuid", q(ROOT_UUID)], ["paper", q("A3")],
|
|
["title_block", ["title", q("PLIP Telephone - Zacus")],
|
|
["date", q("2026-06-10")], ["rev", q("A")],
|
|
["company", q("Le Mystere du Professeur Zacus")]],
|
|
lib_symbols] + body + [
|
|
["sheet_instances", ["path", q("/"), ["page", q("1")]]]]
|
|
|
|
out = os.path.join(PROJ, "plip-telephone.kicad_sch")
|
|
with open(out, "w") as f:
|
|
f.write(serialize(doc) + "\n")
|
|
with open(os.path.join(HERE, "netmap.json"), "w") as f:
|
|
json.dump(netmap, f, indent=1, ensure_ascii=False, sort_keys=True)
|
|
print("écrit:", out)
|
|
print("écrit:", os.path.join(HERE, "netmap.json"))
|
|
print("labels: %d, no_connect: %d, composants: %d" % (len(labels), len(no_connects), len(COMPONENTS)))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|