imrishabh18/rp2040-motor-controller
A library of PCB component definitions covering MOSFETs, motor drivers, USB-PD control, sensors, current monitors, transistors, diodes, resistors, speakers, connectors, and mirrored bottom-side logos, with pin mappings, footprints, and 3D models.
- Version
- 1.0.55
- License
- unset
- Stars
- 5
tests/helpers/assertPdBusVoltageMonitor.ts
import type { CircuitJson } from "circuit-json";
/** Verify the raw PD supply can be read without exposing RP2040 GPIOs to VBUS. */
export function assertPdBusVoltageMonitor(json: CircuitJson) {
const parent = new Map<string, string>();
const root = (id: string): string => {
if (!parent.has(id)) parent.set(id, id);
if (parent.get(id) !== id) parent.set(id, root(parent.get(id)!));
return parent.get(id)!;
};
for (const element of json) {
const ids = element.type === "source_trace"
? [...element.connected_source_port_ids, ...element.connected_source_net_ids]
: element.type === "source_component_internal_connection" ? element.source_port_ids : [];
for (const id of ids.slice(1)) parent.set(root(id), root(ids[0]!));
}
const component = (name: string) => {
const result = json.find(element => element.type === "source_component" && element.name === name);
if (result?.type !== "source_component") throw new Error(`Missing ${name}`);
return result;
};
const pin = (name: string, number: number) => {
const part = component(name);
const port = json.find(element => element.type === "source_port" &&
element.source_component_id === part.source_component_id && element.pin_number === number);
if (port?.type !== "source_port") throw new Error(`Missing ${name} physical pin ${number}`);
return port;
};
const net = (name: string, number: number) => root(pin(name, number).source_port_id);
const same = (a: [string, number], b: [string, number]) => {
if (net(...a) !== net(...b)) throw new Error(`Disconnected ${a.join(".")} / ${b.join(".")}`);
};
const monitor = component("U_PD_MON");
if (monitor.manufacturer_part_number !== "INA226AIDGSR" ||
monitor.supplier_part_numbers?.jlcpcb?.[0] !== "C49851") {
throw new Error("PD monitor must use the imported JLCPCB INA226 footprint");
}
const bypass = component("C_PD_MON");
if (!("capacitance" in bypass) || bypass.capacitance !== 1e-7) throw new Error("Missing 100 nF INA226 bypass");
for (const [a, b] of [
[["U_PD_MON", 8], ["J_MOTOR_USB", 6]], // VBUS before fuse and blocking diode
[["U_PD_MON", 10], ["U_PD_MON", 8]], // no shunt on unused current input
[["U_PD_MON", 9], ["U_PD_MON", 8]],
[["U_PD_MON", 6], ["L_AVDD", 1]],
[["U_PD_MON", 6], ["C_PD_MON", 1]],
[["U_PD_MON", 7], ["C_PD_MON", 2]],
[["U_PD_MON", 1], ["U_PD_MON", 7]], // 7-bit I2C address 0x40
[["U_PD_MON", 2], ["U_PD_MON", 7]],
[["U_PD_MON", 4], ["U_TEMP", 6]],
[["U_PD_MON", 5], ["U_TEMP", 1]],
[["U1", 27], ["R_BUZZER_GATE", 1]], // GPIO16 already drives the buzzer
[["R_BUZZER_GATE", 2], ["Q_BUZZER", 1]],
] as [string, number][][]) same(a!, b!);
if (!pin("U_PD_MON", 3).do_not_connect) throw new Error("INA226 ALERT must be explicitly unconnected");
if (new Set([
net("U_PD_MON", 8), net("U_PD_MON", 6), net("U_PD_MON", 7),
net("U_PD_MON", 4), net("U_PD_MON", 5), net("J_USB", 18),
net("D_MOTOR_BLOCK", 1),
]).size !== 7) throw new Error("PD voltage monitor shorts power or I2C domains");
const physical = json.find(element => element.type === "pcb_component" &&
element.source_component_id === monitor.source_component_id);
if (physical?.type === "pcb_component" && physical.layer !== "top") {
throw new Error("PD monitor must be populated on the top for single-sided assembly");
}
if (physical?.type === "pcb_component") {
const left = physical.center.x - physical.width / 2;
const right = physical.center.x + physical.width / 2;
const bottom = physical.center.y - physical.height / 2;
const top = physical.center.y + physical.height / 2;
for (const hole of json.filter(element => element.type === "pcb_hole" || element.type === "pcb_plated_hole")) {
const x = hole.x, y = hole.y;
const dimensions = hole as typeof hole & {
outer_width?: number; outer_height?: number; outer_diameter?: number;
hole_width?: number; hole_height?: number; hole_diameter?: number;
};
const halfWidth = (dimensions.outer_width ?? dimensions.outer_diameter ?? dimensions.hole_width ?? dimensions.hole_diameter ?? 0) / 2;
const halfHeight = (dimensions.outer_height ?? dimensions.outer_diameter ?? dimensions.hole_height ?? dimensions.hole_diameter ?? 0) / 2;
if (x + halfWidth + 0.2 > left && x - halfWidth - 0.2 < right &&
y + halfHeight + 0.2 > bottom && y - halfHeight - 0.2 < top) {
throw new Error(`PD monitor body conflicts with through-hole geometry at (${x}, ${y})`);
}
}
}
return { status: "pass", monitorPart: "C49851", rawPdBus: true, i2cAddress: "0x40",
i2cGpios: [26, 27], buzzerGpio: 16, motorEnableGpio: 22,
requiresDataUsbAndApplicationFirmware: true };
}
