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
#!/usr/bin/env python3
"""Drive the placement search and write the winner out as an ANCHORS table."""
import json, os, pprint, sys
os.environ.setdefault("PCBGEN_NO_WRITE", "1")
import pcbgen, placeopt as O, placescore as PS
base = O.evaluate(pcbgen.ANCHORS, pcbgen.GRID_CLUSTERS, 1.0, 1.0, strict=False)
BASE_CC, BASE_DT = base[1], base[2] - 25.0
print(f"baseline (previous revision): cost {BASE_CC:.0f} peak {base[2]:.2f}C chain {base[5]:.0f}mm outchain {base[6]:.0f}mm")
clusters = O.seeded_clusters()
SEED = O.legalize(O.SEED)
s = O.evaluate(SEED, clusters, BASE_CC, BASE_DT)
print(f"seed : cost {s[1]:.0f} peak {s[2]:.2f}C chain {s[5]:.0f}mm outchain {s[6]:.0f}mm score {s[0]:.4f}\n")
iters = int(sys.argv[1]) if len(sys.argv) > 1 else 2500
results = []
for seed in (11, 23, 37, 59):
print(f"=== anneal seed {seed} ===")
best, score = O.anneal(SEED, clusters, BASE_CC, BASE_DT, iters=iters, seed=seed)
g = O.evaluate(best, clusters, BASE_CC, BASE_DT)
print(f" -> score {score:.4f} cost {g[1]:.0f} peak {g[2]:.2f}C chain {g[5]:.0f}mm outchain {g[6]:.0f}mm\n")
results.append((score, seed, best, g))
results.sort(key=lambda r: r[0])
score, seed, best, g = results[0]
best = O.legalize(best)
assert O.anchors_legal(best), O.overlap_list(best)
g = O.evaluate(best, clusters, BASE_CC, BASE_DT)
import placescore as _PS
_pv = _PS.pair_violations({r: (v[0], v[1]) for r, v in best.items()})
print(f"legality: {len(O.overlap_list(best))} anchor overlaps, "
f"{len(_pv)} tight-pair violations {_pv}\n")
assert not _pv
print(f"WINNER seed {seed}: score {score:.4f}")
print(f" current cost {g[1]:.0f} (prev {BASE_CC:.0f}, {100*(g[1]/BASE_CC-1):+.1f}%)")
print(f" peak temp {g[2]:.2f}C (prev {base[2]:.2f}C, {g[2]-base[2]:+.2f}C)")
print(f" chain {g[5]:.0f}mm (prev {base[5]:.0f}mm, {100*(g[5]/base[5]-1):+.1f}%)")
print(f" output chain {g[6]:.0f}mm (prev {base[6]:.0f}mm, {100*(g[6]/base[6]-1):+.1f}%)")
print(f" loop perim {sum(g[3].values()):.0f}mm (prev {sum(base[3].values()):.0f}mm)")
for k in g[3]:
print(f" {k:20s} {g[3][k]:6.1f}mm (prev {base[3][k]:6.1f}mm)")
json.dump({r: list(v) for r, v in best.items()},
open(os.path.join(os.path.dirname(__file__), "rev6_anchors.json"), "w"), indent=1)
print("\nwrote rev6_anchors.json")