imrishabh18/rp2040-motor-controller
An RP2040-based NEMA17 stepper-motor controller with USB-C programming and PD motor-power negotiation, DRV8825 dual H-bridge drive, current/temperature telemetry, protected power filtering, status RGB LED, buzzer alarm, and optional magnetic encoder.
- Version
- 1.0.42
- License
- unset
- Stars
- 3
tests/drv8825-thermal-vias.test.tsx
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { Circuit } from "tscircuit";
import Board from "../index.circuit";
import fixture from "./fixtures/C81582-thermal-vias.json";
test("DRV8825 preserves all twelve supplier thermal vias, grounded through EP", async () => {
let json: any[];
if (process.env.CIRCUIT_JSON_PATH) {
json = JSON.parse(readFileSync(process.env.CIRCUIT_JSON_PATH, "utf8"));
} else {
const c = new Circuit(); c.add(<Board />); await c.renderUntilSettled();
json = c.getCircuitJson();
}
const driver = json.find(e => e.type === "source_component" && e.name === "DRIVER");
const pcb = json.find(e => e.type === "pcb_component" && e.source_component_id === driver.source_component_id);
const pad = json.find(e => e.type === "pcb_smtpad" && e.pcb_component_id === pcb.pcb_component_id && e.port_hints?.includes("pin29"));
const ep = json.find(e => e.type === "pcb_port" && e.pcb_port_id === pad.pcb_port_id);
const joins = json.filter(e => e.type === "source_trace" && /^DRV_IMPORTED_EP_\d+ connectivity$/.test(e.display_name ?? ""));
expect(joins).toHaveLength(12);
expect(json.filter(e => e.type === "source_trace" && /^DRV_(EP_THERM|THERM)_/.test(e.display_name ?? ""))).toHaveLength(0);
const vias = joins.map(join => {
expect(join.connected_source_port_ids).toContain(ep.source_port_id);
const matching = json.filter(e => e.type === "pcb_via" && e.source_trace_id === join.source_trace_id);
expect(matching).toHaveLength(1);
return matching[0];
});
// Compare the board against original EasyEDA VIA records, including the
// slightly offset middle via, rather than approximating a regular grid.
const angle = pcb.rotation * Math.PI / 180;
for (const shape of fixture.viaShapes) {
const [, sx, sy, diameter, , holeRadius] = shape.split("~");
const x = (Number(sx) - fixture.origin.x) * 0.254;
const y = (fixture.origin.y - Number(sy)) * 0.254;
const px = pcb.center.x + x * Math.cos(angle) - y * Math.sin(angle);
const py = pcb.center.y + x * Math.sin(angle) + y * Math.cos(angle);
const matches = vias.filter(v => Math.hypot(v.x - px, v.y - py) < 1e-6);
expect(matches).toHaveLength(1);
const via = matches[0];
expect(via.outer_diameter).toBeCloseTo(Number(diameter) * 0.254, 7);
expect(via.hole_diameter).toBeCloseTo(Number(holeRadius) * 2 * 0.254, 7);
expect(new Set(via.layers)).toEqual(new Set(["top", "inner1", "inner2", "bottom"]));
const a = -(pad.ccw_rotation ?? 0) * Math.PI / 180;
const dx = via.x - pad.x, dy = via.y - pad.y;
expect(Math.abs(dx * Math.cos(a) - dy * Math.sin(a)) + via.outer_diameter / 2).toBeLessThan(pad.width / 2);
expect(Math.abs(dx * Math.sin(a) + dy * Math.cos(a)) + via.outer_diameter / 2).toBeLessThan(pad.height / 2);
}
expect(new Set(vias.map(v => `${v.x},${v.y}`)).size).toBe(12);
const ground = json.find(e => e.type === "source_net" && e.name === "GND");
expect(json.some(e => e.type === "source_trace" && e.connected_source_net_ids?.includes(ground.source_net_id) && e.connected_source_port_ids?.includes(ep.source_port_id))).toBe(true);
}, 60000);
