imrishabh18/rp2040-motor-controller

This circuit incorporates a USB-C Power Delivery (PD) interface with a CH224K controller, a WJ500V-5.08-04P motor output connector, multiple power conditioning components including capacitors, resistors, diodes, and an RP2040 microcontroller, to manage motor control, signal routing, and power management for a USB-powered stepper motor system.

Version
1.0.14
License
unset
Stars
0

tests/schematic-sheets.test.tsx

import { expect, test } from "bun:test";
import { convertCircuitJsonToStackedSchematicSheetsSvg } from "circuit-to-svg";
import { Circuit } from "tscircuit";
import Rp2040MotorController from "../index.circuit";

test("renders the controller, programming, motor driver, and motor power sheets", async () => {
  const circuit = new Circuit({ platform: { pcbDisabled: true } });
  circuit.add(<Rp2040MotorController />);

  await circuit.renderUntilSettled();
  const circuitJson = circuit.getCircuitJson() as any[];
  const controllerSheetId = circuitJson.flatMap((element) =>
    element.type === "schematic_sheet" && element.name === "controller"
      ? [element.schematic_sheet_id]
      : [],
  )[0];
  if (!controllerSheetId) {
    throw new Error("Controller schematic sheet is missing");
  }
  const programmingSheetId = circuitJson.flatMap((element) =>
    element.type === "schematic_sheet" &&
    element.name === "controller_programming"
      ? [element.schematic_sheet_id]
      : [],
  )[0];
  if (!programmingSheetId) {
    throw new Error("Programming schematic sheet is missing");
  }
  const schematicUnitsPerMillimeter = 1.1 / 10.16;
  const innerHalfWidth =
    (297 * schematicUnitsPerMillimeter) / 2 - 5 * schematicUnitsPerMillimeter;
  const innerHalfHeight =
    (210 * schematicUnitsPerMillimeter) / 2 - 5 * schematicUnitsPerMillimeter;
  const componentFrameClearance = 0.5;
  const sourceComponentNames = new Map(
    circuitJson.flatMap((element) =>
      element.type === "source_component"
        ? [[element.source_component_id, element.name] as const]
        : [],
    ),
  );
  const schematicComponentsByName = new Map(
    circuitJson.flatMap((element) => {
      if (element.type !== "schematic_component") {
        return [];
      }
      const name = element.source_component_id
        ? sourceComponentNames.get(element.source_component_id)
        : undefined;
      return name ? [[name, element] as const] : [];
    }),
  );
  const componentsOutsideSheetFrames = circuitJson.flatMap((element) => {
    if (element.type !== "schematic_component") {
      return [];
    }
    const halfWidth = element.size.width / 2 + componentFrameClearance;
    const halfHeight = element.size.height / 2 + componentFrameClearance;
    const insideFrame =
      element.center.x - halfWidth >= -innerHalfWidth &&
      element.center.x + halfWidth <= innerHalfWidth &&
      element.center.y - halfHeight >= -innerHalfHeight &&
      element.center.y + halfHeight <= innerHalfHeight;
    return insideFrame
      ? []
      : [
          {
            sheetId: element.schematic_sheet_id,
            name: element.source_component_id
              ? sourceComponentNames.get(element.source_component_id)
              : undefined,
            center: element.center,
            size: element.size,
          },
        ];
  });

  expect(componentsOutsideSheetFrames).toEqual([]);
  expect(
    circuitJson.filter(
      (element) => element.type === "schematic_element_outside_sheet_warning",
    ),
  ).toEqual([]);

  const getSchematicComponent = (name: string) => {
    const component = schematicComponentsByName.get(name);
    if (!component) {
      throw new Error(`Missing schematic component ${name}`);
    }
    return component;
  };
  const distanceBetweenComponents = (firstName: string, secondName: string) => {
    const first = getSchematicComponent(firstName);
    const second = getSchematicComponent(secondName);
    return Math.hypot(
      first.center.x - second.center.x,
      first.center.y - second.center.y,
    );
  };

  expect(getSchematicComponent("U1").schematic_sheet_id).toBe(
    controllerSheetId,
  );
  for (const programmingComponentName of [
    "J_USB",
    "U2",
    "U3",
    "C_FLASH",
    "C_USB_VDD",
    "C_VBUS",
    "C_USB",
    "R_USB1",
    "R_USB2",
  ]) {
    expect(
      getSchematicComponent(programmingComponentName).schematic_sheet_id,
    ).toBe(programmingSheetId);
  }

  const iovddCapacitors = [
    "C_IOVDD1",
    "C_IOVDD2",
    "C_IOVDD3",
    "C_IOVDD4",
    "C_IOVDD5",
    "C_IOVDD6",
  ].map(getSchematicComponent);
  const iovddRows = new Map<number, number>();
  for (const capacitor of iovddCapacitors) {
    const rowKey = Math.round(capacitor.center.y * 1000);
    iovddRows.set(rowKey, (iovddRows.get(rowKey) ?? 0) + 1);
  }
  const rp2040 = getSchematicComponent("U1");
  const nearestIovddCapacitorRightEdge = Math.max(
    ...iovddCapacitors.map(
      (capacitor) => capacitor.center.x + capacitor.size.width / 2,
    ),
  );
  const rp2040LeftEdge = rp2040.center.x - rp2040.size.width / 2;

  expect([...iovddRows.values()]).toEqual([6]);
  expect(rp2040LeftEdge - nearestIovddCapacitorRightEdge).toBeGreaterThan(0.75);

  const crystal = getSchematicComponent("Y1");
  expect(crystal.center.x).toBeCloseTo(-7.5, 1);
  expect(crystal.center.y).toBeCloseTo(-5, 1);

  const controllerSheet = circuitJson.find(
    (element) =>
      element.type === "schematic_sheet" &&
      element.schematic_sheet_id === controllerSheetId,
  );
  if (!controllerSheet) {
    throw new Error("Controller schematic sheet geometry is missing");
  }
  for (const switchName of ["SW_BOOT", "SW_RUN"]) {
    const schematicSwitch = getSchematicComponent(switchName);
    const switchTop =
      schematicSwitch.center.y + schematicSwitch.size.height / 2;
    expect(switchTop).toBeLessThan(controllerSheet.center.y - 0.5);
  }

  const localDecouplingDistances: Array<
    [capacitorName: string, targetName: string, maximumDistance: number]
  > = [
    ["C_CORE", "U1", 4.25],
    ["C_FLASH", "U2", 4],
    ["C_USB_VDD", "J_USB", 5],
    ["C_USB", "J_USB", 5],
    ["C_XIN", "Y1", 2.5],
    ["C_XOUT", "Y1", 2.5],
    ["C_DVDD1", "U1", 6],
    ["C_DVDD2", "U1", 7],
    ["C_VM_HF", "DRIVER", 6],
    ["C_VM_BULK", "DRIVER", 7],
    ["C_MOTOR_BULK", "F_MOTOR_VBUS", 10],
    ["C_PD_VDD", "U_PD", 3],
  ];
  for (const [
    capacitorName,
    targetName,
    maximumDistance,
  ] of localDecouplingDistances) {
    expect(distanceBetweenComponents(capacitorName, targetName)).toBeLessThan(
      maximumDistance,
    );
  }

  expect(
    convertCircuitJsonToStackedSchematicSheetsSvg(circuitJson, {
      width: 1400,
      includeVersion: true,
      showErrorsInTextOverlay: true,
    }),
  ).toMatchSvgSnapshot(import.meta.path);
}, 15_000);