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);