AnasSarkiz/rp2040-usb-c-single-axis-dm542t-motion-controller--01a0e89c
A USB‑C RP2040 controller board with external QSPI flash, crystal clock, regulated 3.3/1.1/5 V power, boost-converted motor-driver supply, protected USB and limit inputs, status LEDs, reset/boot switches, and AO3400A MOSFET outputs for stepper-control signals.
- Version
- 0.1.0-prototype.19
- License
- unset
- Stars
- 0
scripts/build-bom.ts
import { writeFileSync } from "node:fs"
import { passives } from "../lib/passives"
import catalog from "../lib/catalog.json"
const imported = [
["U7", "C12462", "4.75 V driver buck-boost regulator"],
["U8", "C55266", "Driver branch current-limiting switch"],
["U9", "C96333", "3.3 V brownout supervisor"],
["L1", "C80662", "1.5 uH buck-boost inductor"],
["U1", "C2040", "RP2040 processor"],
["U2", "C131025", "16-Mbit boot flash"],
["U3", "C51118", "3.3 V LDO"],
["U4", "C7519", "USB ESD"],
["U5", "C2869734", "Reverse-current blocker"],
["U6", "C7519", "Limit input ESD"],
["J1", "C165948", "USB-C receptacle"],
["F1", "C17313", "500 mA hold resettable fuse"],
["Y1", "C20625731", "12 MHz 10 pF crystal"],
["SW1", "C318884", "BOOTSEL"],
["SW2", "C318884", "RESET"],
["LED1", "C2297", "Power LED"],
["LED2", "C2297", "Status LED"],
...["Q1", "Q2", "Q3", "Q4", "Q5"].map((name) => [
name,
"C20917",
"Output MOSFET",
]),
...["J2", "J3", "J4", "J5", "J6"].map((name) => [
name,
"C474892",
"3.5 mm pitch 2-pole screw terminal",
]),
]
const fitted = [
...imported.map(([name, code, purpose]) => ({ name, code, purpose })),
...passives,
]
const rows = fitted.map((part) => {
const entry = catalog.find((entry) => entry.componentCode === part.code)
if (!entry || !entry.dataManualUrl || !entry.canPresaleNumber)
throw new Error(`Missing sourcing evidence: ${part.name}`)
return {
Designator: part.name,
Quantity: 1,
Comment:
"rating" in part ? `${part.rating}: ${part.purpose}` : part.purpose,
Manufacturer: entry.componentBrandEn,
"Manufacturer Part Number": entry.componentModelEn,
Footprint: entry.componentSpecificationEn,
"LCSC Part #": entry.componentCode,
"Catalog URL": entry.url,
"Datasheet URL": entry.dataManualOfficialLink || entry.dataManualUrl,
"Assembly category":
entry.componentLibraryType === "base" ? "Basic" : "Extended",
"Assembly mode": entry.assemblyMode,
"JLC assembly eligible quantity": entry.canPresaleNumber,
"Checked on": entry.checked_on,
}
})
const header = Object.keys(rows[0])
const csv = (row: object) =>
Object.values(row)
.map((cell) => `"${String(cell).replace(/"/g, '""')}"`)
.join(",")
writeFileSync("bom.csv", [csv(header), ...rows.map(csv)].join("\n") + "\n")
writeFileSync(
"BOM.md",
`# A0 bill of materials\n\n**Untested prototype — schematic reviewed; routed fabrication validation pending.** ${rows.length} fitted electronic components. Each row maps to official JLC assembly catalog evidence with individual UTC check dates, stored in references/catalog. Availability is a dated snapshot, not a reservation. Through-hole terminals use manualWeld: confirm Standard PCBA/manual insertion eligibility at quote review.\n\nCSV follows [JLC's BOM field guidance](https://jlcpcb.com/help/article/bill-of-materials-for-pcb-assembly), with extra audit columns. No CPL/assembly upload has been approved.\n\n| Ref | Qty | Function/value | Manufacturer | Full MPN | Package | JLC part | Datasheet | Assembly | Eligible qty | Checked |\n|---|---:|---|---|---|---|---|---|---|---:|---|\n` +
rows
.map(
(row) =>
`| ${row.Designator} | 1 | ${row.Comment} | ${row.Manufacturer} | ${row["Manufacturer Part Number"]} | ${row.Footprint} | [${row["LCSC Part #"]}](${row["Catalog URL"]}) | [PDF](${row["Datasheet URL"]}) | ${row["Assembly category"]}, ${row["Assembly mode"]} | ${row["JLC assembly eligible quantity"]} | ${row["Checked on"]} |`,
)
.join("\n") +
"\n\nFour 3.2 mm NPTH holes and eleven bare copper test pads are PCB features, not purchased parts. M3 fasteners/standoffs are not supplied; select hardware within the 8 mm diameter clearance envelope after enclosure design. Motor, driver, USB cable and limit switches are external equipment.\n",
)
writeFileSync(
"evidence/bom-count.json",
JSON.stringify(
{
electronicComponents: rows.length,
uniqueCatalogParts: new Set(fitted.map((part) => part.code)).size,
},
null,
2,
) + "\n",
)
console.log(`BOM: ${rows.length} fitted components with exact JLC mappings`)