app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
v0.9.112: single-component demo projects (John: extra components on a one-part demo board just confuse the user) - the companion resistor is gone, refdes comes from the part's own Reference property (D1/C1/...)
5 files changed
+47−94
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.111+0.9.112
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.111",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.111/adom-bridge-kicad-v0.9.111.zip",- "sha256": "75a8178fff97a637f9b57e1c53c9e57796df45e736e2019a20cc5d79b4c7287c",- "size": 407709,+ "version": "0.9.112",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.112/adom-bridge-kicad-v0.9.112.zip",+ "sha256": "a50bc546ef5b47f236626f0dee19126450adf2189e5b89064729f920d523e182",+ "size": 407166, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-16T01:08:24Z",+ "released_at": "2026-08-16T12:24:34Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.112.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.111",+ "version": "0.9.112", "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.", "homepage": "https://wiki.adom.inc/adom/adom-bridge", "author": "Adom Inc.",
handlers/make_part_project.py+40−87@@ -15,14 +15,20 @@ KiCad file formats; this verb generates them from the installed part: kicad_make_part_project {"partName":"IN-S42ATR"} kicad_show_project / show_schematic / show_2d_board / show_3d_board {...} +SINGLE-COMPONENT BY DESIGN (John, 2026-08-16: "when demoing a single component,+you don't need to add extra components to the example 2d board layout as its+just confusing for the user"). The generated design is JUST the part: the sheet+holds the one symbol with short stub wires off its pins, the board holds the one+footprint centered in tight edge cuts with two copper stubs. The reference+designator comes from the part's own Reference property (D1 for a diode, C1 for+a capacitor, ...), not a hardcoded prefix.+ Generated (formats identical to the proven kicad_demo generators): <out>/<project>.kicad_pro — minimal project- <out>/<project>.kicad_sch — the part as D1 wired to a 330R series resistor- (lib_symbols embedded => self-contained sheet)- <out>/<project>.kicad_pcb — the part's footprint + an 0805 R placed inside- edge cuts, with copper stubs; the footprint- keeps its (model ...) so the 3D board shows the- real part model.+ <out>/<project>.kicad_sch — the part alone (lib_symbols embedded => self-contained)+ <out>/<project>.kicad_pcb — the part's footprint, part-dominant edge cuts; the+ footprint keeps its (model ...) so the 3D board+ shows the real part model. """ import math import os@@ -60,37 +66,6 @@ def _pads(fp_text: str): re.findall(r'\(pad\s+"[^"]*"[^()]*\(at\s+([-\d.]+)\s+([-\d.]+)', fp_text)] -_R_LIB_ENTRY = ''' (symbol "Adom:R_330"- (pin_numbers (hide yes)) (pin_names (offset 0) (hide yes))- (exclude_from_sim no) (in_bom yes) (on_board yes)- (property "Reference" "R" (at 2.54 1.27 0) (effects (font (size 1.27 1.27)) (justify left)))- (property "Value" "330" (at 2.54 -1.27 0) (effects (font (size 1.27 1.27)) (justify left)))- (property "Footprint" "Resistor_SMD:R_0805_2012Metric" (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_330_0_1"- (rectangle (start -1.016 2.54) (end 1.016 -2.54)- (stroke (width 0.254) (type default)) (fill (type none)))- )- (symbol "R_330_1_1"- (pin passive line (at 0 3.81 270) (length 1.27) (name "~") (number "1"))- (pin passive line (at 0 -3.81 90) (length 1.27) (name "~") (number "2"))- )- )-'''--_R_FP = '''(footprint "Resistor_SMD:R_0805_2012Metric"- (version 20240108) (generator "adom-part-project") (layer "F.Cu")- (at {rx} {ry})- (attr smd)- (property "Reference" "R1" (at 0 -1.6 0) (layer "F.SilkS") (effects (font (size 0.6 0.6) (thickness 0.1))))- (property "Value" "330" (at 0 1.6 0) (layer "F.Fab") (effects (font (size 0.6 0.6) (thickness 0.1))))- (fp_rect (start -1.7 -0.9) (end 1.7 0.9) (stroke (width 0.05) (type solid)) (fill none) (layer "F.CrtYd"))- (pad "1" smd roundrect (at -0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25))- (pad "2" smd roundrect (at 0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25))-)-'''-- def handle_make_part_project(kicad_info: dict, args: dict) -> dict: """Generate <project>.kicad_pro/.kicad_sch/.kicad_pcb around an installed part. @@ -132,83 +107,62 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict: / "Documents" / proj)) out_dir.mkdir(parents=True, exist_ok=True) - # ---------------- schematic ----------------+ # The part names its own reference prefix (D for a diode, C for a capacitor…)+ ref_m = re.search(r'\(property\s+"Reference"\s+"([^"]*)"', block)+ refdes = ((ref_m.group(1).strip() if ref_m else "") or "U") + "1"++ # ---------------- schematic: JUST the part ---------------- sheet = _uuid(1) ax, ay = 127.0, 80.0 # part anchor on the sheet (y-down) lib_entry = " " + block.replace(f'(symbol "{part}"', f'(symbol "Adom:{part}"', 1) - pins = _pins(block)- wires, r_inst = "", ""- r_connected = False+ wires = "" wire_n = 300- for px, py, pa in pins[:2]:+ for px, py, pa in _pins(block)[:2]: cx, cy = ax + px, ay - py # symbol y-up -> sheet y-down # outward = away from the body = opposite the pin's pointing direction dx = -math.cos(math.radians(pa)) dy = math.sin(math.radians(pa)) # y flip folded in- ex, ey = round(cx + 8 * dx, 3), round(cy + 8 * dy, 3)- if not r_connected and abs(dy) < 0.01 and dx > 0.5:- # rightward pin: run this wire into R1's bottom pin. A pin's (at) is- # its CONNECTION point (the line extends toward the body by length),- # so R_330's bottom pin (at 0 -3.81) connects 3.81 below the anchor.- ex = 145.0- r_y = round(ey - 3.81, 3) # R center so its bottom pin lands on the wire end- r_inst = f''' (symbol (lib_id "Adom:R_330") (at 145 {r_y} 0) (unit 1)- (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(202)}")- (property "Reference" "R1" (at 147.5 {r_y - 1.3} 0) (effects (font (size 1.27 1.27)) (justify left)))- (property "Value" "330" (at 147.5 {r_y + 1.5} 0) (effects (font (size 1.27 1.27)) (justify left)))- (instances (project "{proj}" (path "/{sheet}" (reference "R1") (unit 1))))- )-'''- r_connected = True+ ex, ey = round(cx + 6 * dx, 3), round(cy + 6 * dy, 3) wires += (f' (wire (pts (xy {round(cx, 3)} {round(cy, 3)}) (xy {ex} {ey})) ' f'(stroke (width 0) (type default)) (uuid "{_uuid(wire_n)}"))\n') wire_n += 1- if not r_inst: # no rightward pin found — place R1 unwired (demo scaffold)- r_inst = f''' (symbol (lib_id "Adom:R_330") (at 145 74.92 0) (unit 1)- (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(202)}")- (property "Reference" "R1" (at 147.5 73.5 0) (effects (font (size 1.27 1.27)) (justify left)))- (property "Value" "330" (at 147.5 76.4 0) (effects (font (size 1.27 1.27)) (justify left)))- (instances (project "{proj}" (path "/{sheet}" (reference "R1") (unit 1))))- )-''' schematic = f'''(kicad_sch (version 20231120) (generator "eeschema") (generator_version "8.0") (uuid "{sheet}") (paper "A4") (title_block (title "{part} demo - generated by the Adom KiCad Bridge") (company "Adom")) (lib_symbols {lib_entry}-{_R_LIB_ENTRY} )+ ) {wires} (symbol (lib_id "Adom:{part}") (at {ax} {ay} 0) (unit 1) (exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (uuid "{_uuid(201)}")- (property "Reference" "D1" (at {ax} {ay - 7.5} 0) (effects (font (size 1.27 1.27))))+ (property "Reference" "{refdes}" (at {ax} {ay - 7.5} 0) (effects (font (size 1.27 1.27)))) (property "Value" "{part}" (at {ax} {ay + 7} 0) (effects (font (size 1.27 1.27)))) (property "Footprint" "Adom:{fp_name}" (at {ax} {ay} 0) (effects (font (size 1.27 1.27)) (hide yes)))- (instances (project "{proj}" (path "/{sheet}" (reference "D1") (unit 1))))+ (instances (project "{proj}" (path "/{sheet}" (reference "{refdes}") (unit 1)))) )-{r_inst} (sheet_instances (path "/" (page "1")))+ (sheet_instances (path "/" (page "1"))) ) ''' - # ---------------- board ----------------+ # ---------------- board: JUST the part, centered ---------------- # Part-DOMINANT outline: tight margins so the part is clearly visible in the- # 3D board view (John, 2026-08-15: on a roomy board a 0402 is an invisible- # speck and the 3D board "isn't showing the chip").+ # 3D board view (a 0402 on a roomy board reads as an empty board). part_at = (108.0, 68.0) coords = [(float(a), float(b)) for a, b in re.findall(r'\((?:start|end|at|center)\s+([-\d.]+)\s+([-\d.]+)', fp_text)] half_w = max([abs(x) for x, _ in coords] + [1.0]) half_h = max([abs(y) for _, y in coords] + [1.0]) margin = max(2.0, half_h * 0.8)- r_at = (round(part_at[0] + half_w + 4.6, 2), part_at[1]) # 0805 R just beside the part x1 = round(part_at[0] - half_w - margin, 2) y1 = round(part_at[1] - max(half_h, 1.0) - margin, 2)- x2 = round(r_at[0] + 1.7 + margin, 2)+ x2 = round(part_at[0] + half_w + margin, 2) y2 = round(part_at[1] + max(half_h, 1.0) + margin, 2) fp_placed = fp_text.replace(f'(footprint "{fp_name}"', f'(footprint "Adom:{fp_name}"', 1) fp_placed = fp_placed.replace('(layer "F.Cu")', f'(layer "F.Cu")\n (at {part_at[0]} {part_at[1]})', 1)- fp_placed = fp_placed.replace('(fp_text reference "REF**"', '(fp_text reference "D1"', 1)- fp_placed = fp_placed.replace('(property "Reference" "REF**"', '(property "Reference" "D1"', 1)+ fp_placed = fp_placed.replace('(fp_text reference "REF**"', f'(fp_text reference "{refdes}"', 1)+ fp_placed = fp_placed.replace('(property "Reference" "REF**"', f'(property "Reference" "{refdes}"', 1)+ edge = "" pts = [(x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)] for i in range(4):@@ -216,20 +170,19 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict: edge += (f' (gr_line (start {ex1} {ey1}) (end {ex2} {ey2}) ' f'(stroke (width 0.15) (type solid)) (layer "Edge.Cuts") (uuid "{_uuid(400 + i)}"))\n') + # two short copper stubs off the pads keep the board looking real without+ # adding any other component segments = "" pads = _pads(fp_text) if len(pads) >= 2 and abs(pads[0][1] - pads[1][1]) < 0.01: left, right = sorted(pads[:2]) py_abs = part_at[1] + left[1]- segments = (f' (segment (start {round(part_at[0] + right[0], 3)} {round(py_abs, 3)}) '- f'(end {round(r_at[0] - 0.9125, 3)} {round(r_at[1], 3)}) (width 0.3) (layer "F.Cu"))\n'- f' (segment (start {round(part_at[0] + left[0], 3)} {round(py_abs, 3)}) '- f'(end {round(x1 + 2, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n'- f' (segment (start {round(r_at[0] + 0.9125, 3)} {round(r_at[1], 3)}) '- f'(end {round(x2 - 2, 3)} {round(r_at[1], 3)}) (width 0.3) (layer "F.Cu"))\n')+ segments = (f' (segment (start {round(part_at[0] + left[0], 3)} {round(py_abs, 3)}) '+ f'(end {round(x1 + 0.8, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n'+ f' (segment (start {round(part_at[0] + right[0], 3)} {round(py_abs, 3)}) '+ f'(end {round(x2 - 0.8, 3)} {round(py_abs, 3)}) (width 0.3) (layer "F.Cu"))\n') - fps = (" " + fp_placed.rstrip().replace("\n", "\n ") + "\n "- + _R_FP.format(rx=r_at[0], ry=r_at[1]).rstrip().replace("\n", "\n "))+ fps = " " + fp_placed.rstrip().replace("\n", "\n ") board = f'''(kicad_pcb (version 20240108) (generator "pcbnew") (generator_version "8.0") (general (thickness 1.6)) (paper "A4")@@ -277,9 +230,9 @@ def handle_make_part_project(kicad_info: dict, args: dict) -> dict: "boardFile": files["kicad_pcb"], "partName": part, "footprintName": fp_name,- "resistorWired": r_connected,- "output": (f"Generated {proj}: the part as D1 with a 330R series resistor: "- f"schematic wired={r_connected}, board with edge cuts, copper and the part's 3D model."),+ "reference": refdes,+ "output": (f"Generated {proj}: the part alone as {refdes}, single-component by design "+ f"(schematic + part-dominant board with edge cuts, copper stubs and the part's 3D model)."), "_hint": ("Project surfaces now have something real to show: kicad_show_project {filePath: " "projectFile}, kicad_show_schematic {filePath: schematicFile}, kicad_show_2d_board / " "kicad_show_3d_board {filePath: boardFile}. The footprint keeps its (model ...) "