board
Solar MPPT Power System
Public Unreviewedby TylerK
A 30 W solar battery charger with regulated 5 V and 3.3 V rails: an STM32G031 running perturb & observe MPPT and a CC/CV charge state machine, steering a TI LM5175 4-switch buck-boost through a single analog control input. Four-layer KiCad board, schematic and PCB generated from Python, with an electrothermal simulation run against the published board file.
main
TylerK
Replace discrete-analog board with MCU/IC redesign: STM32G031 P&O MPPT + CC/CV firmware, LM5175 buck-boost, INA180 curr…
e48ba30
1mo ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
#!/usr/bin/env python3
"""Parse the kicad-cli 'kicadsexpr' netlist export into a simple JSON:
components: {ref: {value, footprint}}
ref_pin_nets: {ref: {pin: net_name}}
Uses a balanced-paren block splitter (not fragile line-based regex) since
the export's indentation/line-wrapping isn't uniform."""
import re, json, sys
def split_blocks(text, tag):
"""Every top-level '(tag ...)' block within text, balanced-paren aware."""
out = []
i = 0
n = len(text)
needle = '(' + tag
while True:
i = text.find(needle, i)
if i == -1:
break
# must be a real token boundary (not part of a longer word)
j = i + len(needle)
if j < n and (text[j].isalnum() or text[j] == '_'):
i = j
continue
depth = 0
k = i
ins = False
esc = False
while k < n:
ch = text[k]
if esc:
esc = False
elif ch == '\\':
esc = True
elif ch == '"':
ins = not ins
elif not ins:
if ch == '(':
depth += 1
elif ch == ')':
depth -= 1
if depth == 0:
k += 1
break
k += 1
out.append(text[i:k])
i = k
return out
def field(block, name, quoted=True):
pat = r'\(' + name + (r' "([^"]*)"\)' if quoted else r' ([^\s\)]+)\)')
m = re.search(pat, block)
return m.group(1) if m else None
def parse(path):
txt = open(path, errors='replace').read()
comp_start = txt.find('(components')
comp_end = txt.find('(libparts')
comp_section = txt[comp_start:comp_end]
components = {}
for block in split_blocks(comp_section, 'comp'):
ref = field(block, 'ref')
if not ref:
continue
components[ref] = {
'value': field(block, 'value') or '',
'footprint': field(block, 'footprint') or '',
}
nets_start = txt.find('(nets')
nets_section = txt[nets_start:]
ref_pin_nets = {}
for net_block in split_blocks(nets_section, 'net'):
name = field(net_block, 'name')
if name is None:
continue
for node_block in split_blocks(net_block, 'node'):
ref = field(node_block, 'ref')
pin = field(node_block, 'pin')
if ref and pin:
ref_pin_nets.setdefault(ref, {})[pin] = name
return {'components': components, 'ref_pin_nets': ref_pin_nets}
if __name__ == '__main__':
src = sys.argv[1] if len(sys.argv) > 1 else 'mppt-mcu-netlist.net'
out = sys.argv[2] if len(sys.argv) > 2 else 'mppt_mcu_netlist.json'
data = parse(src)
json.dump(data, open(out, 'w'), indent=1)
print(f"components: {len(data['components'])} refs-with-nets: {len(data['ref_pin_nets'])}")