ShiboSoftwareDev/solar-battery-charger-module

This hardware setup integrates a solar panel, a lithium-ion battery, and a multi-rail buck converter with a battery charging IC, protection circuitry, and monitoring components, enabling solar power harvesting, battery charging, and power distribution in an embedded system.

Version
1.0.4
License
unset
Stars
0

scripts/generate-fabrication-csv.mts

import { readFileSync, writeFileSync } from "node:fs"
import { convertCircuitJsonToPickAndPlaceCsv } from "circuit-json-to-pnp-csv"

const circuit = JSON.parse(readFileSync("dist/index/circuit.json", "utf8"))
const components = circuit.filter((element: any) => element.type === "source_component")

const csvCell = (value: unknown) => {
  const text = value == null ? "" : String(value)
  return /[",\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text
}

const valueOf = (component: any) =>
  component.display_resistance ??
  component.display_capacitance ??
  component.display_inductance ??
  component.manufacturer_part_number ??
  component.name

const lcscOf = (component: any) => component.supplier_part_numbers?.jlcpcb?.[0] ?? ""
const assemblyOf = (component: any) =>
  component.name === "J_SOLAR"
    ? "THT"
    : "SMT"

const footprintByMpn: Record<string, string> = {
  "KF127S-5.08-2P": "THT-TerminalBlock-2P-P5.08mm",
  "B3B-PH-SM4-TB(LF)(SN)": "JST-PH-3P-SMT-P2.00mm",
  SS54: "SMA",
  GRM32ER7YA106KA12L: "1210",
  FRC2512F2R00TS: "2512",
  "1206W4F100JT5E": "1206",
  CL21B105KBFNNNE: "0805",
  BQ24650RVAR: "VQFN-16-EP-3.5x3.5mm-P0.5mm",
  "SI7288DP-T1-GE3": "PowerPAK-SO-8",
  "2N7002": "SOT-23",
  MBR0540T1G: "SOD-123",
  IHLP2525CZER100M01: "2525",
  WSL1206R0200FEA: "1206",
  "0402WGF0000TCE": "0402",
  "0402WGF4993TCE": "0402",
  "0402WGF3602TCE": "0402",
  "0402WGF1003TCE": "0402",
  "0402CG220J500NT": "0402",
  "0402WGF7501TCE": "0402",
  "0402WGF8202TCE": "0402",
  "0402WGF1002TCE": "0402",
  "0402WGF1001TCE": "0402",
  "0402WGF1004TCE": "0402",
  CL05B104KB5NNNC: "0402",
  "1210B225K500NT": "1210",
  "LTST-C190GKT": "0603",
  "LTST-C190KRKT": "0603",
  "HoLLR1206-1W-5mR-1%": "1206",
  STC3115AIQT: "VFQFPN-10-2x3mm-P0.5mm",
  GRM155Z71A105KE01D: "0402",
  CC0402KRX7R6BB224: "0402",
  TPS63031DSKR: "VSON-10-EP-2.5x2.5mm-P0.5mm",
  "DFE252012P-1R5M=P2": "2520",
  CL10B106MQ8NRNC: "0603",
}

const footprintOf = (component: any) =>
  footprintByMpn[component.manufacturer_part_number] ?? ""

// PCB-only edge contacts have no purchased part and must not appear in a
// JLCPCB assembly BOM. Keep them in Circuit JSON, but only emit components
// carrying an actual JLCPCB/LCSC catalog code into manufacturing CSVs.
const assemblyComponents = components.filter((component: any) => lcscOf(component))

const perComponentRows = assemblyComponents.map((component: any) => [
  component.name,
  valueOf(component),
  footprintOf(component),
  component.manufacturer_part_number ?? "",
  lcscOf(component),
  assemblyOf(component),
])

const grouped = new Map<string, { designators: string[]; component: any }>()
for (const component of assemblyComponents) {
  const key = [valueOf(component), footprintOf(component), component.manufacturer_part_number ?? "", lcscOf(component), assemblyOf(component)].join("\0")
  const group = grouped.get(key) ?? { designators: [], component }
  group.designators.push(component.name)
  grouped.set(key, group)
}

const groupedRows = [...grouped.values()].map(({ designators, component }) => [
  valueOf(component),
  designators.join(","),
  footprintOf(component),
  lcscOf(component),
  component.manufacturer_part_number ?? "",
  designators.length,
  assemblyOf(component),
])

const toCsv = (header: string[], rows: unknown[][]) =>
  [header, ...rows].map((row) => row.map(csvCell).join(",")).join("\n") + "\n"

writeFileSync(
  "fabrication/solar-battery-charger-tscircuit-bom.csv",
  toCsv(["Designator", "Value", "Footprint", "Manufacturer Part Number", "LCSC", "Assembly"], perComponentRows),
)
writeFileSync(
  "fabrication/solar-battery-charger-assembly-bom.csv",
  toCsv(["Comment", "Designator", "Footprint", "LCSC Part #", "Manufacturer Part Number", "Quantity", "Assembly"], groupedRows),
)
const pnpCsv = convertCircuitJsonToPickAndPlaceCsv(circuit)
  .split(/\r?\n/)
  .filter((line, index) => index === 0 || (!line.startsWith("J_CTRL,") && !line.startsWith("J_IO,")))
  .filter(Boolean)
  .join("\n") + "\n"
writeFileSync("fabrication/solar-battery-charger-pick-and-place.csv", pnpCsv)

console.log(JSON.stringify({
  componentCount: components.length,
  assemblyComponentCount: assemblyComponents.length,
  groupedBomRows: groupedRows.length,
  uniqueLcscParts: new Set(assemblyComponents.map(lcscOf)).size,
}, null, 2))