Files
chicken-coop-door/integrate_boost.py
0xWheatyz e9917837a8 feat(schematic): replace L7805 with TPS61088 boost converter
Remove the L7805 linear regulator (12V→5V) and its input/output caps,
replacing it with a TPS61088 boost converter circuit (5V→12V) for the
A4988 stepper driver. The ESP32 is now powered directly from the 5V
input rail. Includes integration script and project sym-lib-table for
the custom TPS61088 symbol library.
2026-04-20 17:49:23 -04:00

773 lines
24 KiB
Python

#!/usr/bin/env python3
"""
Transform firstpcb.kicad_sch:
1. Remove the L7805 voltage regulator and associated components
2. Insert the TPS61088 5V→12V boost converter circuit
3. Keep all other components intact
The +5V and +12V nets connect automatically via KiCad global power symbols.
"""
import re
import uuid
import sys
def uid():
return str(uuid.uuid4())
ROOT_UUID = "4ce690e5-6d5b-4693-bd2e-f4090f26df1f"
# ── UUIDs of components to REMOVE (L7805 circuit) ──
REMOVE_UUIDS = {
# L7805 regulator
"b5b8c784-5cbd-46af-b962-5f07e5e5ce66",
# C1 (0.33uF) - output cap
"be10cf51-2c79-49d9-968c-f16a919adeee",
# C2 (0.1uF) - input cap
"58617ce4-1b01-4ae9-be22-0491e474e52d",
# #PWR15 (+5V at L7805 output)
"dd887245-ee3f-48b0-ab57-caf2a8bdfdef",
# #PWR19 (+5V at C2)
"fd8b730d-5d7d-4216-98e6-0f769c039626",
# #PWR14 (+12V at L7805 input)
"ea5b2f5d-6233-46fc-93b4-4b52ebc256b5",
# #PWR17 (+12V at C1)
"ec47b49b-69a3-415f-b119-7b90e03df83a",
# #PWR16 (GND under L7805)
"940ddbc7-3e82-42b5-ad91-3df87e10ff89",
# #PWR18 (GND under C1)
"565f28b7-cc4b-427c-be9a-9e09337daabd",
# #PWR20 (GND under C2)
"e7fe0926-923d-41aa-92e6-413304613139",
}
REMOVE_WIRE_UUIDS = {
# Wire: C2 top to L7805 output rail
"dba247d2-ddb5-46ff-a6bd-5fb718c4599e",
# Wire: L7805 input to +12V symbol
"e3772378-2aa5-420d-92e3-94a8234f86b5",
# Wire: C1 to +12V symbol
"ee45c424-04dc-4913-92e9-0eec7ac6b273",
# Wire: L7805 output to +5V symbol
"48aefa74-f6f4-40ae-9135-90c2565ceadc",
}
# Also remove the L7805 lib_symbol definition
REMOVE_LIB_SYMBOL = "Regulator_Linear:L7805"
def find_block_end(text, start_pos):
"""Find the end of a parenthesized block starting at start_pos.
start_pos should point to the opening '('."""
depth = 0
i = start_pos
while i < len(text):
if text[i] == '(':
depth += 1
elif text[i] == ')':
depth -= 1
if depth == 0:
return i
i += 1
return -1
def remove_blocks_by_uuid(text, uuids):
"""Remove top-level blocks (symbol, wire, junction) containing matching UUIDs."""
for u in uuids:
# Find the UUID in the text
pattern = f'(uuid "{u}")'
idx = text.find(pattern)
if idx == -1:
print(f" WARNING: UUID {u} not found, skipping", file=sys.stderr)
continue
# Walk backward from UUID to find the containing block's opening
# Look for a tab-indented opening paren: \n\t(symbol, \n\t(wire, etc.
search_start = idx
block_start = -1
while search_start > 0:
# Find the previous newline
nl = text.rfind('\n', 0, search_start)
if nl == -1:
break
line = text[nl+1:search_start]
# Check if this line starts a top-level block
stripped = line.lstrip()
if stripped.startswith('(symbol') or stripped.startswith('(wire') or \
stripped.startswith('(junction') or stripped.startswith('(no_connect'):
block_start = nl + 1
# Find the start of the actual paren
paren_pos = text.index('(', block_start)
block_start = paren_pos
break
search_start = nl
if block_start == -1:
print(f" WARNING: Could not find block start for UUID {u}", file=sys.stderr)
continue
block_end = find_block_end(text, block_start)
if block_end == -1:
print(f" WARNING: Could not find block end for UUID {u}", file=sys.stderr)
continue
# Remove the block (including trailing newline)
end = block_end + 1
if end < len(text) and text[end] == '\n':
end += 1
# Also remove leading whitespace/tabs on the same line
while block_start > 0 and text[block_start-1] in '\t ':
block_start -= 1
print(f" Removing block with UUID {u[:12]}... ({end - block_start} chars)")
text = text[:block_start] + text[end:]
return text
def remove_lib_symbol(text, lib_id):
"""Remove a lib_symbol definition from the lib_symbols section."""
pattern = f'(symbol "{lib_id}"'
idx = text.find(pattern)
if idx == -1:
print(f" WARNING: lib_symbol {lib_id} not found", file=sys.stderr)
return text
# Find the containing (symbol block - walk back to find the right indentation
# In lib_symbols, entries are indented with two tabs
search = idx
while search > 0 and text[search-1] != '\n':
search -= 1
block_start = search
block_paren = text.index('(', block_start)
block_end = find_block_end(text, block_paren)
end = block_end + 1
if end < len(text) and text[end] == '\n':
end += 1
print(f" Removing lib_symbol {lib_id}")
text = text[:block_start] + text[end:]
return text
# ── TPS61088 boost converter circuit to INSERT ──
def gen_symbol(lib_id, ref, value, x, y, rot=0, pins=None, extra_props=""):
"""Generate a symbol placement block."""
u = uid()
if pins is None:
pins = {}
pin_str = ""
for pnum in sorted(pins.keys(), key=lambda k: int(k) if k.isdigit() else k):
pin_str += f'\t\t(pin "{pnum}"\n\t\t\t(uuid "{pins[pnum]}")\n\t\t)\n'
return f"""\t(symbol
\t\t(lib_id "{lib_id}")
\t\t(at {round(x, 2)} {round(y, 2)} {rot})
\t\t(unit 1)
\t\t(exclude_from_sim no)
\t\t(in_bom yes)
\t\t(on_board yes)
\t\t(dnp no)
\t\t(uuid "{u}")
\t\t(property "Reference" "{ref}"
\t\t\t(at {round(x + 2.54, 2)} {round(y - 2.54, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(justify left)
\t\t\t)
\t\t)
\t\t(property "Value" "{value}"
\t\t\t(at {round(x + 2.54, 2)} {round(y + 2.54, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(justify left)
\t\t\t)
\t\t)
\t\t(property "Footprint" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(property "Datasheet" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(property "Description" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t){extra_props}
{pin_str}\t\t(instances
\t\t\t(project ""
\t\t\t\t(path "/{ROOT_UUID}"
\t\t\t\t\t(reference "{ref}")
\t\t\t\t\t(unit 1)
\t\t\t\t)
\t\t\t)
\t\t)
\t)"""
def gen_pwr(lib_id, value, x, y, ref):
"""Generate a power symbol block."""
return f"""\t(symbol
\t\t(lib_id "{lib_id}")
\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t(unit 1)
\t\t(exclude_from_sim no)
\t\t(in_bom yes)
\t\t(on_board yes)
\t\t(dnp no)
\t\t(uuid "{uid()}")
\t\t(property "Reference" "{ref}"
\t\t\t(at {round(x, 2)} {round(y - 2.54, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(property "Value" "{value}"
\t\t\t(at {round(x, 2)} {round(y + 3.81, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t)
\t\t)
\t\t(property "Footprint" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(property "Datasheet" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(property "Description" ""
\t\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t\t(effects
\t\t\t\t(font
\t\t\t\t\t(size 1.27 1.27)
\t\t\t\t)
\t\t\t\t(hide yes)
\t\t\t)
\t\t)
\t\t(pin "1"
\t\t\t(uuid "{uid()}")
\t\t)
\t\t(instances
\t\t\t(project ""
\t\t\t\t(path "/{ROOT_UUID}"
\t\t\t\t\t(reference "{ref}")
\t\t\t\t\t(unit 1)
\t\t\t\t)
\t\t\t)
\t\t)
\t)"""
def gen_wire(x1, y1, x2, y2):
return f"""\t(wire
\t\t(pts
\t\t\t(xy {round(x1, 2)} {round(y1, 2)}) (xy {round(x2, 2)} {round(y2, 2)})
\t\t)
\t\t(stroke
\t\t\t(width 0)
\t\t\t(type default)
\t\t)
\t\t(uuid "{uid()}")
\t)"""
def gen_junction(x, y):
return f"""\t(junction
\t\t(at {round(x, 2)} {round(y, 2)})
\t\t(diameter 0)
\t\t(color 0 0 0 0)
\t\t(uuid "{uid()}")
\t)"""
def gen_label(x, y, name, rot=0):
return f"""\t(label "{name}"
\t\t(at {round(x, 2)} {round(y, 2)} {rot})
\t\t(effects
\t\t\t(font
\t\t\t\t(size 1.27 1.27)
\t\t\t)
\t\t)
\t\t(uuid "{uid()}")
\t)"""
def gen_no_connect(x, y):
return f"""\t(no_connect
\t\t(at {round(x, 2)} {round(y, 2)})
\t\t(uuid "{uid()}")
\t)"""
def gen_text(x, y, text):
return f"""\t(text "{text}"
\t\t(exclude_from_sim no)
\t\t(at {round(x, 2)} {round(y, 2)} 0)
\t\t(effects
\t\t\t(font
\t\t\t\t(size 1.27 1.27)
\t\t\t)
\t\t\t(justify left)
\t\t)
\t\t(uuid "{uid()}")
\t)"""
# ── TPS61088 lib_symbol definition ──
TPS61088_LIB_SYMBOL = """\t\t(symbol "TPS61088:TPS61088"
\t\t\t(exclude_from_sim no)
\t\t\t(in_bom yes)
\t\t\t(on_board yes)
\t\t\t(property "Reference" "U"
\t\t\t\t(at 0 16.51 0)
\t\t\t\t(effects (font (size 1.27 1.27)))
\t\t\t)
\t\t\t(property "Value" "TPS61088"
\t\t\t\t(at 0 13.97 0)
\t\t\t\t(effects (font (size 1.27 1.27)))
\t\t\t)
\t\t\t(property "Footprint" "Package_DFN_QFN:QFN-20-1EP_3.5x3.5mm_P0.5mm_EP2.1x2.1mm"
\t\t\t\t(at 0 -20.32 0)
\t\t\t\t(effects (font (size 1.27 1.27)) (hide yes))
\t\t\t)
\t\t\t(property "Datasheet" "https://www.ti.com/lit/ds/symlink/tps61088.pdf"
\t\t\t\t(at 0 -22.86 0)
\t\t\t\t(effects (font (size 1.27 1.27)) (hide yes))
\t\t\t)
\t\t\t(property "Description" "10A Fully-Integrated Synchronous Boost Converter"
\t\t\t\t(at 0 -25.4 0)
\t\t\t\t(effects (font (size 1.27 1.27)) (hide yes))
\t\t\t)
\t\t\t(symbol "TPS61088_0_1"
\t\t\t\t(rectangle
\t\t\t\t\t(start -10.16 12.7)
\t\t\t\t\t(end 10.16 -12.7)
\t\t\t\t\t(stroke (width 0.254) (type default))
\t\t\t\t\t(fill (type background))
\t\t\t\t)
\t\t\t)
\t\t\t(symbol "TPS61088_1_1"
\t\t\t\t(pin power_in line
\t\t\t\t\t(at -12.7 10.16 0) (length 2.54)
\t\t\t\t\t(name "VIN" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "1" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin input line
\t\t\t\t\t(at -12.7 5.08 0) (length 2.54)
\t\t\t\t\t(name "EN" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "3" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin passive line
\t\t\t\t\t(at -12.7 0 0) (length 2.54)
\t\t\t\t\t(name "SS/TR" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "4" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin passive line
\t\t\t\t\t(at -12.7 -5.08 0) (length 2.54)
\t\t\t\t\t(name "COMP" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "5" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin input line
\t\t\t\t\t(at -12.7 -10.16 0) (length 2.54)
\t\t\t\t\t(name "FB" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "6" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin passive line
\t\t\t\t\t(at 12.7 10.16 180) (length 2.54)
\t\t\t\t\t(name "SW" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "12" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin passive line
\t\t\t\t\t(at 12.7 5.08 180) (length 2.54)
\t\t\t\t\t(name "BST" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "11" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin power_out line
\t\t\t\t\t(at 12.7 0 180) (length 2.54)
\t\t\t\t\t(name "VOUT" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "9" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin open_collector line
\t\t\t\t\t(at 12.7 -5.08 180) (length 2.54)
\t\t\t\t\t(name "PGOOD" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "8" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin power_in line
\t\t\t\t\t(at 0 -15.24 90) (length 2.54)
\t\t\t\t\t(name "PGND" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "15" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t\t(pin power_in line
\t\t\t\t\t(at -5.08 -15.24 90) (length 2.54)
\t\t\t\t\t(name "AGND" (effects (font (size 1.27 1.27))))
\t\t\t\t\t(number "7" (effects (font (size 1.27 1.27))))
\t\t\t\t)
\t\t\t)
\t\t\t(embedded_fonts no)
\t\t)"""
def generate_boost_circuit():
"""Generate all the schematic elements for the TPS61088 boost converter.
Returns (junctions, wires, labels, no_connects, texts, symbols) as lists of strings.
Placed below the main circuit, centered around (80, 140).
"""
junctions = []
wires = []
labels = []
no_connects = []
texts = []
symbols = []
pwr_idx = [30] # start well after existing power refs (existing go up to #PWR23)
def next_pwr():
pwr_idx[0] += 1
return f"#PWR{pwr_idx[0]:02d}"
# IC center
cx, cy = 80.01, 140.97
# Pin absolute positions
VIN = (cx - 12.7, cy - 10.16)
EN = (cx - 12.7, cy - 5.08)
SS = (cx - 12.7, cy)
COMP = (cx - 12.7, cy + 5.08)
FB = (cx - 12.7, cy + 10.16)
SW = (cx + 12.7, cy - 10.16)
BST = (cx + 12.7, cy - 5.08)
VOUT = (cx + 12.7, cy)
PG = (cx + 12.7, cy + 5.08)
PGND = (cx, cy + 15.24)
AGND = (cx - 5.08, cy + 15.24)
# Place TPS61088 IC
ic_pins = {str(i): uid() for i in [1, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15]}
symbols.append(gen_symbol("TPS61088:TPS61088", "U1", "TPS61088", cx, cy, pins=ic_pins))
# ── INPUT SECTION ──
vin_rail_y = VIN[1]
pwr5v_x = 39.37
c1_x = 44.45
c2_x = 52.07
en_tap_x = 59.69
# +5V source
ref = next_pwr()
symbols.append(gen_pwr("power:+5V", "+5V", pwr5v_x, vin_rail_y - 5.08, ref))
wires.append(gen_wire(pwr5v_x, vin_rail_y - 5.08, pwr5v_x, vin_rail_y))
# VIN rail (segmented)
wires.append(gen_wire(pwr5v_x, vin_rail_y, c1_x, vin_rail_y))
wires.append(gen_wire(c1_x, vin_rail_y, c2_x, vin_rail_y))
wires.append(gen_wire(c2_x, vin_rail_y, en_tap_x, vin_rail_y))
wires.append(gen_wire(en_tap_x, vin_rail_y, VIN[0], vin_rail_y))
# C1 input cap (22µF) — using Device:C (pins at ±3.81)
c1_cy = vin_rail_y + 6.35
symbols.append(gen_symbol("Device:C", "C4", "22uF", c1_x, c1_cy,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(c1_x, c1_cy - 3.81, c1_x, vin_rail_y))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", c1_x, c1_cy + 6.35, ref))
wires.append(gen_wire(c1_x, c1_cy + 3.81, c1_x, c1_cy + 6.35))
# C2 input cap (22µF)
c2_cy = vin_rail_y + 6.35
symbols.append(gen_symbol("Device:C", "C5", "22uF", c2_x, c2_cy,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(c2_x, c2_cy - 3.81, c2_x, vin_rail_y))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", c2_x, c2_cy + 6.35, ref))
wires.append(gen_wire(c2_x, c2_cy + 3.81, c2_x, c2_cy + 6.35))
# EN tied to VIN
wires.append(gen_wire(EN[0], EN[1], en_tap_x, EN[1]))
wires.append(gen_wire(en_tap_x, EN[1], en_tap_x, vin_rail_y))
# ── SOFT-START ──
ss_x = 59.69
c3_cy = SS[1] + 6.35
wires.append(gen_wire(SS[0], SS[1], ss_x, SS[1]))
symbols.append(gen_symbol("Device:C", "C6", "22nF", ss_x, c3_cy,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(ss_x, SS[1], ss_x, c3_cy - 3.81))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", ss_x, c3_cy + 6.35, ref))
wires.append(gen_wire(ss_x, c3_cy + 3.81, ss_x, c3_cy + 6.35))
# ── COMPENSATION ──
comp_x = 52.07
r1_y = COMP[1] + 6.35
wires.append(gen_wire(COMP[0], COMP[1], comp_x, COMP[1]))
symbols.append(gen_symbol("Device:R", "R3", "30.1k", comp_x, r1_y, # COMP resistor
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(comp_x, COMP[1], comp_x, r1_y - 3.81))
c4_y = r1_y + 10.16
symbols.append(gen_symbol("Device:C", "C8", "47pF", comp_x, c4_y,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(comp_x, r1_y + 3.81, comp_x, c4_y - 3.81))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", comp_x, c4_y + 6.35, ref))
wires.append(gen_wire(comp_x, c4_y + 3.81, comp_x, c4_y + 6.35))
# ── IC GROUND ──
gnd_y = PGND[1] + 5.08
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", PGND[0], gnd_y, ref))
wires.append(gen_wire(PGND[0], PGND[1], PGND[0], gnd_y))
wires.append(gen_wire(AGND[0], AGND[1], AGND[0], gnd_y))
wires.append(gen_wire(AGND[0], gnd_y, PGND[0], gnd_y))
junctions.append(gen_junction(PGND[0], gnd_y))
# ── OUTPUT SECTION ──
c5_x = 97.79
l1_x = 102.87
l1_y = SW[1]
l1_pin1 = (l1_x - 3.81, l1_y)
l1_pin2 = (l1_x + 3.81, l1_y)
symbols.append(gen_symbol("Device:L", "L1", "2.2uH", l1_x, l1_y, rot=90,
pins={"1": uid(), "2": uid()}))
# SW → C5 junction → L1
wires.append(gen_wire(SW[0], SW[1], c5_x, SW[1]))
wires.append(gen_wire(c5_x, SW[1], l1_pin1[0], l1_pin1[1]))
# Bootstrap cap C5 (100nF)
c5_cy = SW[1] + 3.81
symbols.append(gen_symbol("Device:C", "C9", "100nF", c5_x, c5_cy,
pins={"1": uid(), "2": uid()}))
junctions.append(gen_junction(c5_x, SW[1]))
wires.append(gen_wire(c5_x, c5_cy + 3.81, BST[0], BST[1]))
# VOUT rail
vout_rail_x = 115.57
wires.append(gen_wire(l1_pin2[0], l1_pin2[1], vout_rail_x, l1_y))
wires.append(gen_wire(VOUT[0], VOUT[1], vout_rail_x, VOUT[1]))
wires.append(gen_wire(vout_rail_x, l1_y, vout_rail_x, VOUT[1]))
# Output caps
c6_x = 120.65
c7_x = 128.27
wires.append(gen_wire(vout_rail_x, l1_y, c6_x, l1_y))
wires.append(gen_wire(c6_x, l1_y, c7_x, l1_y))
c6_cy = l1_y + 6.35
symbols.append(gen_symbol("Device:C", "C10", "22uF", c6_x, c6_cy,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(c6_x, c6_cy - 3.81, c6_x, l1_y))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", c6_x, c6_cy + 6.35, ref))
wires.append(gen_wire(c6_x, c6_cy + 3.81, c6_x, c6_cy + 6.35))
c7_cy = l1_y + 6.35
symbols.append(gen_symbol("Device:C", "C11", "22uF", c7_x, c7_cy,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(c7_x, c7_cy - 3.81, c7_x, l1_y))
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", c7_x, c7_cy + 6.35, ref))
wires.append(gen_wire(c7_x, c7_cy + 3.81, c7_x, c7_cy + 6.35))
# +12V output
ref = next_pwr()
symbols.append(gen_pwr("power:+12V", "+12V", c7_x, l1_y - 5.08, ref))
wires.append(gen_wire(c7_x, l1_y, c7_x, l1_y - 5.08))
junctions.append(gen_junction(c7_x, l1_y))
# ── FEEDBACK DIVIDER ──
fb_x = vout_rail_x
r2_y = VOUT[1] + 6.35
symbols.append(gen_symbol("Device:R", "R4", "190k", fb_x, r2_y,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(fb_x, VOUT[1], fb_x, r2_y - 3.81))
junctions.append(gen_junction(fb_x, VOUT[1]))
r3_y = r2_y + 10.16
symbols.append(gen_symbol("Device:R", "R5", "10k", fb_x, r3_y,
pins={"1": uid(), "2": uid()}))
wires.append(gen_wire(fb_x, r2_y + 3.81, fb_x, r3_y - 3.81))
# FB tap label
fb_tap_y = r2_y + 3.81
wires.append(gen_wire(fb_x, fb_tap_y, fb_x + 5.08, fb_tap_y))
labels.append(gen_label(fb_x + 5.08, fb_tap_y, "FB"))
junctions.append(gen_junction(fb_x, fb_tap_y))
# FB label at IC
wires.append(gen_wire(FB[0], FB[1], FB[0] - 5.08, FB[1]))
labels.append(gen_label(FB[0] - 5.08, FB[1], "FB", rot=180))
# R3 to GND
ref = next_pwr()
symbols.append(gen_pwr("power:GND", "GND", fb_x, r3_y + 6.35, ref))
wires.append(gen_wire(fb_x, r3_y + 3.81, fb_x, r3_y + 6.35))
# PGOOD no connect
no_connects.append(gen_no_connect(PG[0], PG[1]))
# ── NOTES ──
nx = 39.37
ny = 177.8
texts.append(gen_text(nx, ny, "TPS61088 Boost: 5V to 12V @ 2A"))
texts.append(gen_text(nx, ny + 3.81, "VOUT = 0.6V x (1 + 190k/10k) = 12.06V"))
return junctions, wires, labels, no_connects, texts, symbols
def main():
infile = "firstpcb.kicad_sch"
outfile = "firstpcb.kicad_sch"
print(f"Reading {infile}...")
with open(infile, "r") as f:
text = f.read()
# Step 1: Remove L7805 lib_symbol
print("\nRemoving L7805 lib_symbol...")
text = remove_lib_symbol(text, REMOVE_LIB_SYMBOL)
# Step 2: Remove wires first (they reference removed components)
print("\nRemoving L7805 circuit wires...")
text = remove_blocks_by_uuid(text, REMOVE_WIRE_UUIDS)
# Step 3: Remove components and power symbols
print("\nRemoving L7805 circuit components...")
text = remove_blocks_by_uuid(text, REMOVE_UUIDS)
# Step 4: Add TPS61088 lib_symbol to lib_symbols section
print("\nAdding TPS61088 lib_symbol...")
# Find end of lib_symbols section (look for the closing paren before first junction/wire/label/symbol)
# The lib_symbols section ends with a line like "\t)"
# Find the pattern: a tab + ) that comes after all symbol definitions
lib_end_marker = "\n\t)\n"
# Find the FIRST occurrence of this pattern after "(lib_symbols"
lib_start = text.find("(lib_symbols")
if lib_start == -1:
print("ERROR: Could not find lib_symbols section", file=sys.stderr)
sys.exit(1)
# Find the end of lib_symbols by tracking paren depth
lib_paren = text.index("(", lib_start)
lib_end = find_block_end(text, lib_paren)
# Insert before the closing paren
insert_pos = lib_end
# Read Device:L lib_symbol from KiCad library (not embedded in this schematic)
device_l_sym = ""
kicad_sym_paths = [
"/nix/store/pbwczj1nsp823njj1kkw75a99jmkz958-kicad-symbols-884133df0a/share/kicad/symbols/Device.kicad_sym",
"/nix/store/6yiq36yhskjhga4mw63ypbw2wcb4f4m9-kicad-symbols-884133df0a/share/kicad/symbols/Device.kicad_sym",
]
for path in kicad_sym_paths:
try:
with open(path) as lf:
ltext = lf.read()
ls = ltext.find('\n\t(symbol "L"')
if ls != -1:
ls += 1
li = ls
while ltext[li] != '(':
li += 1
ldepth = 0
lpos = li
while lpos < len(ltext):
if ltext[lpos] == '(':
ldepth += 1
elif ltext[lpos] == ')':
ldepth -= 1
if ldepth == 0:
device_l_sym = ltext[ls:lpos+1]
device_l_sym = device_l_sym.replace('(symbol "L"', '(symbol "Device:L"', 1)
# Add extra tab for nesting inside lib_symbols
device_l_sym = '\n'.join('\t' + line for line in device_l_sym.split('\n'))
print(f" Loaded Device:L from {path}")
break
lpos += 1
if device_l_sym:
break
except FileNotFoundError:
continue
text = text[:insert_pos] + "\n" + TPS61088_LIB_SYMBOL + "\n" + device_l_sym + "\n\t" + text[insert_pos:]
# Step 5: Generate and insert boost converter circuit
print("\nGenerating TPS61088 boost converter circuit...")
juncs, wires, lbls, ncs, txts, syms = generate_boost_circuit()
# Insert before (sheet_instances
sheet_marker = "\t(sheet_instances"
sheet_pos = text.find(sheet_marker)
if sheet_pos == -1:
print("ERROR: Could not find sheet_instances", file=sys.stderr)
sys.exit(1)
insert_blocks = []
insert_blocks.extend(juncs)
insert_blocks.extend(ncs)
insert_blocks.extend(wires)
insert_blocks.extend(lbls)
insert_blocks.extend(txts)
insert_blocks.extend(syms)
insert_text = "\n".join(insert_blocks) + "\n"
text = text[:sheet_pos] + insert_text + text[sheet_pos:]
# Step 6: Write output
print(f"\nWriting {outfile}...")
with open(outfile, "w") as f:
f.write(text)
print("Done! Open in KiCad to review.")
if __name__ == "__main__":
main()