abse/isv57t090-motion-controller

A 36 V-powered RP2040 motion-controller PCB that regulates 5 V, drives isolated STEP/DIR servo inputs, monitors alarms, temperature, limits and E-stop signals, and provides protected safety/field connectors.

Version
0.0.67
License
unset
Stars
0

examples/ISV57T090MotionController/ISV57T090MotionController.circuit.tsx

PCBPCB preview for examples/ISV57T090MotionController/ISV57T090MotionController.circuit.tsx
SchematicSchematic preview for examples/ISV57T090MotionController/ISV57T090MotionController.circuit.tsx
import type { ChipProps, ConnectorProps } from "@tscircuit/props"
import { XL_1608SURC_06 } from "../../imports/XL_1608SURC_06"
import { Microcontroller_RP2040 } from "../../lib/Microcontroller_RP2040/Microcontroller_RP2040.circuit"
import { MMBT3904_RANGE_100_300_ } from "../../lib/PowerBoost_MT3608/imports/MMBT3904_RANGE_100_300_"
import { TMP117AIDRVR } from "../../imports/TMP117AIDRVR"
import { TPS54360DDAR } from "../../imports/TPS54360DDAR"
import { B560C_13_F } from "../../imports/B560C_13_F"
import { MWSA0603S_8R2MT } from "../../imports/MWSA0603S_8R2MT"
import { MURS160T3G } from "../../imports/MURS160T3G"
import { SMBJ36A_13_F } from "../../imports/SMBJ36A_13_F"
import { PESD3V3U1UL } from "../../imports/PESD3V3U1UL"
import { B5819W_SL } from "../../lib/Microcontroller_RP2040/imports/B5819W_SL"

/*
 * Motion controller for the StepperOnline iSV57T-090 integrated servo.
 *
 * The motor contains its own 6 A BLDC drive and position encoder. This board
 * deliberately does not attempt to reproduce that high-energy inverter. It
 * supplies a deterministic 5 V, open-collector STEP/DIR interface, reads the
 * isolated alarm contact, and provides the safety/limit interface around it.
 */

const fieldTrace = { thickness: "1.5mm" } as const
const powerTrace = { thickness: "0.6mm" } as const
const signalTrace = { thickness: "0.2mm" } as const

const tb2Pins = { pin1: ["pin1"], pin2: ["pin2"] } as const
const tb6Pins = {
  pin1: ["PUL_P"],
  pin2: ["PUL_N"],
  pin3: ["DIR_P"],
  pin4: ["DIR_N"],
  pin5: ["ALM_P"],
  pin6: ["ALM_N"],
} as const
const io6Pins = {
  pin1: ["LIM_POS"],
  pin2: ["LIM_NEG"],
  pin3: ["ESTOP"],
  pin4: ["ESTOP_RET"],
  pin5: ["NTC_REMOTE"],
  pin6: ["GND"],
} as const
const safe2Pins = { pin1: ["SAFE_5V"], pin2: ["SAFE_RETURN"] } as const
const swd5Pins = {
  pin1: ["V3V3"],
  pin2: ["SWDIO"],
  pin3: ["SWCLK"],
  pin4: ["GND"],
  pin5: ["RUN"],
} as const
const buckPins = { pin1: ["VIN"], pin2: ["GND"], pin3: ["VOUT"] } as const
const mcuPins = {
  pin1: ["VBUS"],
  pin2: ["GND"],
  pin3: ["V3V3"],
  pin4: ["GPIO2"],
  pin5: ["GPIO3"],
  pin6: ["GPIO4"],
  pin7: ["GPIO5"],
  pin8: ["GPIO6"],
  pin9: ["GPIO7"],
  pin10: ["SWDIO"],
  pin11: ["SWCLK"],
  pin12: ["RUN"],
  pin13: ["GPIO8"],
  pin14: ["GPIO9"],
  pin15: ["GPIO10"],
  pin16: ["GPIO11"],
  pin17: ["GPIO12"],
  pin18: ["GPIO13"],
} as const

/** 5.08 mm-pitch 2-position power terminal, drill sized for 14 AWG. */
const PowerTerminal = (props: ConnectorProps) => (
  <connector
    pinLabels={tb2Pins}
    manufacturerPartNumber="DG301-5.08-02P"
    footprint={
      <footprint insertionDirection="from_above">
        <platedhole
          portHints={["pin1"]}
          shape="circle"
          pcbX="-2.54mm"
          pcbY="0mm"
          outerDiameter="3.8mm"
          holeDiameter="2.1mm"
        />
        <platedhole
          portHints={["pin2"]}
          shape="circle"
          pcbX="2.54mm"
          pcbY="0mm"
          outerDiameter="3.8mm"
          holeDiameter="2.1mm"
        />
      </footprint>
    }
    {...props}
  />
)

/** 3.5 mm-pitch pluggable field terminal for the iSV57T control connector. */
const ServoTerminal = (props: ConnectorProps) => (
  <connector
    pinLabels={tb6Pins}
    manufacturerPartNumber="KF2EDG-3.5-06P"
    footprint={
      <footprint insertionDirection="from_above">
        <platedhole
          portHints={["pin1"]}
          shape="circle"
          pcbX="-8.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin2"]}
          shape="circle"
          pcbX="-5.25mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin3"]}
          shape="circle"
          pcbX="-1.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin4"]}
          shape="circle"
          pcbX="1.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin5"]}
          shape="circle"
          pcbX="5.25mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin6"]}
          shape="circle"
          pcbX="8.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
      </footprint>
    }
    {...props}
  />
)

const IOTerminal = (props: ConnectorProps) => (
  <connector
    pinLabels={io6Pins}
    manufacturerPartNumber="KF2EDG-3.5-06P"
    footprint={
      <footprint insertionDirection="from_above">
        <platedhole
          portHints={["pin1"]}
          shape="circle"
          pcbX="-8.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin2"]}
          shape="circle"
          pcbX="-5.25mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin3"]}
          shape="circle"
          pcbX="-1.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin4"]}
          shape="circle"
          pcbX="1.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin5"]}
          shape="circle"
          pcbX="5.25mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
        <platedhole
          portHints={["pin6"]}
          shape="circle"
          pcbX="8.75mm"
          pcbY="0mm"
          outerDiameter="2.7mm"
          holeDiameter="1.3mm"
        />
      </footprint>
    }
    {...props}
  />
)

/** Fail-safe, low-side driver terminal for an external contactor or safety relay.
    It is intentionally not used to switch the servo's supply current directly. */
const SafetyTerminal = (props: ConnectorProps) => (
  <connector
    pinLabels={safe2Pins}
    manufacturerPartNumber="KF2EDG-3.5-02P"
    footprint={
      <footprint insertionDirection="from_above">
        <platedhole portHints={["pin1"]} shape="circle" pcbX="-1.75mm" outerDiameter="2.7mm" holeDiameter="1.3mm" />
        <platedhole portHints={["pin2"]} shape="circle" pcbX="1.75mm" outerDiameter="2.7mm" holeDiameter="1.3mm" />
        <courtyardrect width="6mm" height="4mm" />
      </footprint>
    }
    {...props}
  />
)

const SwdHeader = (props: ConnectorProps) => (
  <connector
    pinLabels={swd5Pins}
    manufacturerPartNumber="HDR-1x05-2.54"
    footprint={
      <footprint>
        <platedhole
          portHints={["pin1"]}
          shape="circle"
          pcbX="0mm"
          pcbY="-5.08mm"
          outerDiameter="2.2mm"
          holeDiameter="1mm"
        />
        <platedhole
          portHints={["pin2"]}
          shape="circle"
          pcbX="0mm"
          pcbY="-2.54mm"
          outerDiameter="2.2mm"
          holeDiameter="1mm"
        />
        <platedhole
          portHints={["pin3"]}
          shape="circle"
          pcbX="0mm"
          pcbY="0mm"
          outerDiameter="2.2mm"
          holeDiameter="1mm"
        />
        <platedhole
          portHints={["pin4"]}
          shape="circle"
          pcbX="0mm"
          pcbY="2.54mm"
          outerDiameter="2.2mm"
          holeDiameter="1mm"
        />
        <platedhole
          portHints={["pin5"]}
          shape="circle"
          pcbX="0mm"
          pcbY="5.08mm"
          outerDiameter="2.2mm"
          holeDiameter="1mm"
        />
      </footprint>
    }
    {...props}
  />
)

/** RECOM R-78HB5.0-0.5: 9-72 V input, regulated 5 V / 0.5 A output. */
const WideInputBuck = (props: ChipProps<typeof buckPins>) => (
  <chip
    pinLabels={buckPins}
    manufacturerPartNumber="R-78HB5.0-0.5"
    footprint={
      <footprint>
        <platedhole
          portHints={["VIN"]}
          shape="circle"
          pcbX="-7.62mm"
          pcbY="0mm"
          outerDiameter="2.8mm"
          holeDiameter="1.2mm"
        />
        <platedhole
          portHints={["GND"]}
          shape="circle"
          pcbX="0mm"
          pcbY="0mm"
          outerDiameter="2.8mm"
          holeDiameter="1.2mm"
        />
        <platedhole
          portHints={["VOUT"]}
          shape="circle"
          pcbX="7.62mm"
          pcbY="0mm"
          outerDiameter="2.8mm"
          holeDiameter="1.2mm"
        />
      </footprint>
    }
    {...props}
  />
)

/** Socket footprint for an RP2040-Zero style controller module. */
const RP2040ZeroModule = (props: ChipProps<typeof mcuPins>) => (
  <chip
    pinLabels={mcuPins}
    manufacturerPartNumber="Waveshare-RP2040-Zero"
    footprint={
      <footprint insertionDirection="from_above">
        {[-10.16, -7.62, -5.08, -2.54, 0, 2.54, 5.08, 7.62, 10.16].map(
          (pcbY, index) => (
            <platedhole
              key={`L${index}`}
              portHints={[`pin${index + 1}`]}
              shape="circle"
              pcbX="-7.62mm"
              pcbY={`${pcbY}mm`}
              outerDiameter="2.2mm"
              holeDiameter="1mm"
            />
          ),
        )}
        {[-10.16, -7.62, -5.08, -2.54, 0, 2.54, 5.08, 7.62, 10.16].map(
          (pcbY, index) => (
            <platedhole
              key={`R${index}`}
              portHints={[`pin${index + 10}`]}
              shape="circle"
              pcbX="7.62mm"
              pcbY={`${pcbY}mm`}
              outerDiameter="2.2mm"
              holeDiameter="1mm"
            />
          ),
        )}
      </footprint>
    }
    {...props}
  />
)

export default function ISV57T090MotionController() {
  return (
    <board
      width="78mm"
      height="76mm"
      title="iSV57T-090 Motion Controller"
      layers={4}
      thickness="1.6mm"
      solderMaskColor="black"
      silkscreenColor="white"
      autorouter="auto_local"
      autorouterEffortLevel="2x"
      autorouterVersion="beta_pipeline9"
      minViaHoleDiameter="0.2mm"
      minViaPadDiameter="0.45mm"
    >
      <net name="GND" isGroundNet />
      <net name="V_SERVO" isPowerNet />
      <net name="V5" isPowerNet />
      <net name="VSYS" isPowerNet />
      <net name="V3V3" isPowerNet />
      <net name="PULSE_3V3" connectsTo=".MCU > .U1 > .GPIO2" />
      <net name="DIR_3V3" connectsTo=".MCU > .U1 > .GPIO3" />
      <net name="ESTOP_3V3" connectsTo=".MCU > .U1 > .GPIO4" />
      <net name="ALARM_3V3" connectsTo=".MCU > .U1 > .GPIO5" />
      <net name="LIMIT_POS" connectsTo=".MCU > .U1 > .GPIO6" />
      <net name="LIMIT_NEG" connectsTo=".MCU > .U1 > .GPIO7" />
      <net name="TEMP_SDA" connectsTo=".MCU > .U1 > .GPIO8" />
      <net name="TEMP_SCL" connectsTo=".MCU > .U1 > .GPIO9" />
      <net name="NTC_REMOTE" connectsTo=".MCU > .U1 > .GPIO26_ADC0" />
      <net name="V_SW" />
      <net name="V_FB" />
      <net name="V_COMP" />
      <net name="V_EN" />
      {/* Ground is explicitly routed by the autorouter and also receives
          top- and bottom-layer copper pours after routing, including the
          plated M5 mounting rings. No hand-authored PCB paths or route
          hints are used. */}

      <schematicsheet
        name="power"
        displayName="36 V Input & Control Power"
        sheetIndex={1}
      />
      <schematicsheet
        name="motion"
        displayName="iSV57T Motion Interface"
        sheetIndex={2}
      />
      <schematicsheet
        name="controller"
        displayName="RP2040 Controller & Safety"
        sheetIndex={3}
      />

      <PowerTerminal
        name="J_PWR"
        pcbX={-34}
        pcbY={0}
        pcbRotation={90}
        schX={-9}
        schY={2}
        schSheetName="power"
      />
      <B5819W_SL name="D_BUCK_CTRL" pcbX={3} pcbY={19} pcbRotation={90} schX={12} schY={7} schSheetName="power" />
      <trace name="BUCK_CTRL_A" from="net.V5" to=".D_BUCK_CTRL > .anode" {...powerTrace} />
      <trace name="BUCK_CTRL_K" from=".D_BUCK_CTRL > .cathode" to="net.VSYS" {...powerTrace} />
      <fuse
        name="F1"
        currentRating="1.10A hold"
        voltageRating="60V"
        manufacturerPartNumber="1812L110/60DR"
        footprint="1812"
        pcbX={-27}
        pcbY={4}
        schX={-6.5}
        schY={2}
        schSheetName="power"
      />
      <MURS160T3G
        name="D_REV"
        pcbX={-19}
        pcbY={4}
        pcbRotation={180}
        schX={-4}
        schY={2}
        schSheetName="power"
      />
      <SMBJ36A_13_F
        name="D_TVS"
        pcbX={-27}
        pcbY={-4}
        pcbRotation={90}
        schX={0}
        schY={0}
        schSheetName="power"
      />
      <capacitor
        name="C_IN1"
        capacitance="10uF"
        footprint="1210"
        pcbX={-21}
        pcbY={-5}
        schX={0}
        schY={3}
        schSheetName="power"
      />
      <capacitor
        name="C_IN2"
        capacitance="100nF"
        footprint="0603"
        pcbX={-19}
        pcbY={-8}
        schX={0}
        schY={5}
        schSheetName="power"
      />
      <TPS54360DDAR
        name="U_BUCK"
        pcbX={-27}
        pcbY={-12}
        schX={5}
        schY={2}
        schSheetName="power"
      />
      <capacitor
        name="C_5V1"
        capacitance="22uF"
        footprint="1210"
        pcbX={-16}
        pcbY={-13}
        schX={10}
        schY={0}
        schSheetName="power"
      />
      <capacitor
        name="C_5V2"
        capacitance="100nF"
        footprint="0603"
        pcbX={-11}
        pcbY={-12}
        schX={10}
        schY={4}
        schSheetName="power"
      />
      {/* TI TPS54360 36 V-to-5 V buck: component values follow TI's 5 V
          reference application (Figure 34); power components are JLC imports. */}
      <MWSA0603S_8R2MT
        name="L_BUCK"
        pcbX={-17}
        pcbY={-18}
        pcbRotation={90}
        schX={8}
        schY={-2}
        schSheetName="power"
      />
      <B560C_13_F
        name="D_BUCK"
        pcbX={-34}
        pcbY={-16}
        pcbRotation={90}
        schX={5}
        schY={-3}
        schSheetName="power"
      />
      <capacitor name="C_BOOT" capacitance="100nF" footprint="0603" pcbX={-22} pcbY={-10} schX={2} schY={-3} schSheetName="power" />
      <resistor name="R_FB_TOP" resistance="53.6k" footprint="0603" pcbX={-9} pcbY={-19} pcbRotation={90} schX={11} schY={-3} schSheetName="power" />
      <resistor name="R_FB_BOT" resistance="10.2k" footprint="0603" pcbX={-9} pcbY={-23} pcbRotation={90} schX={11} schY={-5} schSheetName="power" />
      <resistor name="R_COMP" resistance="13k" footprint="0603" pcbX={-5} pcbY={-25} pcbRotation={90} schX={5} schY={-6} schSheetName="power" />
      <capacitor name="C_COMP" capacitance="6.8nF" footprint="0603" pcbX={-5} pcbY={-29} pcbRotation={90} schX={5} schY={-8} schSheetName="power" />
      <capacitor name="C_COMP_HF" capacitance="39pF" footprint="0603" pcbX={-1} pcbY={-25} pcbRotation={90} schX={7} schY={-6} schSheetName="power" />
      <resistor name="R_EN_TOP" resistance="523k" footprint="0603" pcbX={-31} pcbY={-25} pcbRotation={90} schX={0} schY={-6} schSheetName="power" />
      <resistor name="R_EN_BOT" resistance="84.5k" footprint="0603" pcbX={-31} pcbY={-29} pcbRotation={90} schX={0} schY={-8} schSheetName="power" />
      <resistor name="R_RT" resistance="162k" footprint="0603" pcbX={-25} pcbY={-29} pcbRotation={90} schX={3} schY={-8} schSheetName="power" />

      <trace from=".J_PWR > .pin1" to=".F1 > .pin1" {...fieldTrace} />
      <trace from=".F1 > .pin2" to=".D_REV > .anode" {...fieldTrace} />
      <trace from=".D_REV > .cathode" to="net.V_SERVO" {...fieldTrace} />
      <trace from=".J_PWR > .pin2" to="net.GND" {...fieldTrace} />
      {/* Unidirectional TVS: cathode to positive input, anode to ground. */}
      <trace from="net.V_SERVO" to=".D_TVS > .C" {...powerTrace} />
      <trace from=".D_TVS > .A" to="net.GND" {...powerTrace} />
      <trace from="net.V_SERVO" to=".C_IN1 > .pin1" {...powerTrace} />
      <trace from=".C_IN1 > .pin2" to="net.GND" {...powerTrace} />
      <trace from="net.V_SERVO" to=".C_IN2 > .pin1" {...powerTrace} />
      <trace from=".C_IN2 > .pin2" to="net.GND" {...powerTrace} />
      <trace name="BUCK_VIN" from="net.V_SERVO" to=".U_BUCK > .VIN" {...fieldTrace} />
      <trace name="BUCK_GND" from=".U_BUCK > .GND" to="net.GND" {...powerTrace} />
      <trace name="BUCK_EP" from=".U_BUCK > .EP" to="net.GND" {...powerTrace} />
      <trace name="BUCK_SW" from=".U_BUCK > .SW" to="net.V_SW" {...powerTrace} />
      <trace name="BUCK_DIODE_K" from="net.V_SW" to=".D_BUCK > .cathode" {...powerTrace} />
      <trace name="BUCK_DIODE_A" from=".D_BUCK > .anode" to="net.GND" {...powerTrace} />
      <trace name="BUCK_L" from="net.V_SW" to=".L_BUCK > .pin1" {...powerTrace} />
      <trace name="BUCK_OUT" from=".L_BUCK > .pin2" to="net.V5" {...powerTrace} />
      <trace name="BUCK_BOOT" from=".U_BUCK > .BOOT" to=".C_BOOT > .pin1" {...signalTrace} />
      <trace name="BUCK_BOOT_SW" from=".C_BOOT > .pin2" to="net.V_SW" {...signalTrace} />
      <trace name="BUCK_FB" from=".U_BUCK > .FB" to="net.V_FB" {...signalTrace} />
      <trace name="BUCK_FB_TOP" from="net.V5" to=".R_FB_TOP > .pin1" {...signalTrace} />
      <trace name="BUCK_FB_SENSE" from=".R_FB_TOP > .pin2" to="net.V_FB" {...signalTrace} />
      <trace name="BUCK_FB_BOT" from="net.V_FB" to=".R_FB_BOT > .pin1" {...signalTrace} />
      <trace name="BUCK_FB_GND" from=".R_FB_BOT > .pin2" to="net.GND" {...signalTrace} />
      <trace name="BUCK_COMP" from=".U_BUCK > .COMP" to="net.V_COMP" {...signalTrace} />
      <trace name="BUCK_COMP_R" from="net.V_COMP" to=".R_COMP > .pin1" {...signalTrace} />
      <trace name="BUCK_COMP_C" from=".R_COMP > .pin2" to=".C_COMP > .pin1" {...signalTrace} />
      <trace name="BUCK_COMP_GND" from=".C_COMP > .pin2" to="net.GND" {...signalTrace} />
      <trace name="BUCK_COMP_HF" from="net.V_COMP" to=".C_COMP_HF > .pin1" {...signalTrace} />
      <trace name="BUCK_COMP_HF_GND" from=".C_COMP_HF > .pin2" to="net.GND" {...signalTrace} />
      <trace name="BUCK_EN_TOP" from="net.V_SERVO" to=".R_EN_TOP > .pin1" {...signalTrace} />
      <trace name="BUCK_EN_DIV" from=".R_EN_TOP > .pin2" to="net.V_EN" {...signalTrace} />
      <trace name="BUCK_EN" from="net.V_EN" to=".U_BUCK > .EN" {...signalTrace} />
      <trace name="BUCK_EN_BOT" from="net.V_EN" to=".R_EN_BOT > .pin1" {...signalTrace} />
      <trace name="BUCK_EN_GND" from=".R_EN_BOT > .pin2" to="net.GND" {...signalTrace} />
      <trace name="BUCK_RT" from=".U_BUCK > .pin4" to=".R_RT > .pin1" {...signalTrace} />
      <trace name="BUCK_RT_GND" from=".R_RT > .pin2" to="net.GND" {...signalTrace} />
      <trace from="net.V5" to=".C_5V1 > .pin1" {...powerTrace} />
      <trace from=".C_5V1 > .pin2" to="net.GND" {...powerTrace} />
      <trace from="net.V5" to=".C_5V2 > .pin1" {...powerTrace} />
      <trace from=".C_5V2 > .pin2" to="net.GND" {...powerTrace} />

      <Microcontroller_RP2040
        name="MCU"
        pcbX={0}
        pcbY={0}
        schX={0}
        schY={0}
        schSheetName="controller"
        exposedNets={["VBUS", "VSYS", "V5", "GND", "V3V3"]}
      />
      <trace name="MCU_VSYS_INPUT" from="net.VSYS" to=".MCU > .U3 > .IN" {...powerTrace} />

      {/* Board temperature monitor: TMP117 at I2C address 0x48. */}
      <TMP117AIDRVR
        name="U_TEMP"
        pcbX={-13}
        pcbY={10}
        schX={8}
        schY={9}
        schSheetName="controller"
      />
      <capacitor
        name="C_TEMP"
        capacitance="100nF"
        footprint="0402"
        pcbX={-16}
        pcbY={10}
        schX={10}
        schY={9}
        schSheetName="controller"
      />
      <resistor
        name="R_TEMP_SDA"
        resistance="4.7k"
        footprint="0402"
        supplierPartNumbers={{ jlcpcb: ["C25900"] }}
        pcbX={-16}
        pcbY={14}
        schX={8}
        schY={7}
        schSheetName="controller"
      />
      <resistor
        name="R_TEMP_SCL"
        resistance="4.7k"
        footprint="0402"
        supplierPartNumbers={{ jlcpcb: ["C25900"] }}
        pcbX={-12}
        pcbY={14}
        schX={10}
        schY={7}
        schSheetName="controller"
      />
      <trace name="TEMP_3V3" from=".C_TEMP > .pin1" to=".U_TEMP > .V_POS" maxLength="15mm" {...signalTrace} />
      <trace name="TEMP_GND" from=".U_TEMP > .GND" to=".C_TEMP > .pin2" maxLength="10mm" {...signalTrace} />
      <trace name="TEMP_ADDR" from=".U_TEMP > .ADD0" to="net.GND" {...signalTrace} />
      <trace name="TEMP_SDA_NET" from="net.TEMP_SDA" to=".U_TEMP > .SDA" {...signalTrace} />
      <trace name="TEMP_SCL_NET" from="net.TEMP_SCL" to=".U_TEMP > .SCL" {...signalTrace} />
      {/* TMP117 is polled through the routed I²C bus. ALERT is intentionally
          left NC: firmware applies the temperature threshold/debounce policy. */}
      <trace name="TEMP_DECOUPLE_P" from=".C_TEMP > .pin1" to="net.V3V3" maxLength="10mm" {...signalTrace} />
      <trace name="TEMP_DECOUPLE_G" from=".C_TEMP > .pin2" to="net.GND" maxLength="10mm" {...signalTrace} />
      <trace name="TEMP_SDA_PULLUP" from=".R_TEMP_SDA > .pin1" to="net.TEMP_SDA" {...signalTrace} />
      <trace name="TEMP_SDA_PULLUP_3V3" from=".R_TEMP_SDA > .pin2" to="net.V3V3" {...signalTrace} />
      <trace name="TEMP_SCL_PULLUP" from=".R_TEMP_SCL > .pin1" to="net.TEMP_SCL" {...signalTrace} />
      <trace name="TEMP_SCL_PULLUP_3V3" from=".R_TEMP_SCL > .pin2" to="net.V3V3" {...signalTrace} />

      <testpoint
        name="TP_5V"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={-30}
        pcbY={-32}
        schX={-8}
        schY={7}
        schSheetName="power"
      />
      <testpoint
        name="TP_3V3"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={-23}
        pcbY={-32}
        schX={-5}
        schY={7}
        schSheetName="controller"
      />
      <testpoint
        name="TP_GND"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={-16}
        pcbY={-32}
        schX={-2}
        schY={7}
        schSheetName="power"
      />
      <testpoint
        name="TP_STEP"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={20}
        pcbY={6}
        schX={-2}
        schY={7}
        schSheetName="motion"
      />
      <testpoint
        name="TP_DIR"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={20}
        pcbY={2}
        schX={1}
        schY={7}
        schSheetName="motion"
      />
      <testpoint
        name="TP_ALARM"
        footprintVariant="pad"
        padShape="circle"
        padDiameter="2mm"
        pcbX={20}
        pcbY={-2}
        schX={4}
        schY={7}
        schSheetName="motion"
      />
      <trace
        name="TP_5V_NET"
        from=".TP_5V > .pin1"
        to="net.V5"
        {...signalTrace}
      />
      <trace
        name="TP_3V3_NET"
        from=".TP_3V3 > .pin1"
        to="net.V3V3"
        {...signalTrace}
      />
      <trace
        name="TP_GND_NET"
        from=".TP_GND > .pin1"
        to="net.GND"
        {...signalTrace}
      />
      <trace
        name="TP_STEP_NET"
        from=".TP_STEP > .pin1"
        to="net.PULSE_3V3"
        {...signalTrace}
      />
      <trace
        name="TP_DIR_NET"
        from=".TP_DIR > .pin1"
        to="net.DIR_3V3"
        {...signalTrace}
      />
      <trace
        name="TP_ALARM_NET"
        from=".TP_ALARM > .pin1"
        to="net.ALARM_3V3"
        {...signalTrace}
      />

      <ServoTerminal
        name="J_SERVO"
        pcbX={34}
        pcbY={10}
        pcbRotation={90}
        schX={8}
        schY={0}
        schSheetName="motion"
      />
      <MMBT3904_RANGE_100_300_
        name="Q_PULSE"
        pcbX={24}
        pcbY={15}
        schX={2}
        schY={2}
        schSheetName="motion"
      />
      <MMBT3904_RANGE_100_300_
        name="Q_DIR"
        pcbX={24}
        pcbY={9}
        schX={4}
        schY={2}
        schSheetName="motion"
      />
      <resistor
        name="R_PULSE_B"
        resistance="1k"
        footprint="0603"
        pcbX={19}
        pcbY={15}
        pcbRotation={90}
        schX={0}
        schY={2}
        schSheetName="motion"
      />
      <resistor
        name="R_DIR_B"
        resistance="1k"
        footprint="0603"
        pcbX={19}
        pcbY={9}
        pcbRotation={90}
        schX={0}
        schY={4}
        schSheetName="motion"
      />
      <resistor
        name="R_PULSE_OFF"
        resistance="100k"
        footprint="0603"
        pcbX={17}
        pcbY={18}
        pcbRotation={90}
        schX={0}
        schY={0}
        schSheetName="motion"
      />
      <resistor
        name="R_DIR_OFF"
        resistance="100k"
        footprint="0603"
        pcbX={15}
        pcbY={6}
        pcbRotation={90}
        schX={4}
        schY={0}
        schSheetName="motion"
      />
      <resistor
        name="R_ALARM_PU"
        resistance="10k"
        footprint="0603"
        pcbX={24}
        pcbY={2}
        pcbRotation={90}
        schX={10}
        schY={2}
        schSheetName="motion"
      />
      <resistor
        name="R_ALARM_DIV"
        resistance="1k"
        footprint="0603"
        pcbX={28}
        pcbY={2}
        pcbRotation={90}
        schX={12}
        schY={2}
        schSheetName="motion"
      />
      <resistor
        name="R_ALARM_PD"
        resistance="100k"
        footprint="0603"
        pcbX={28}
        pcbY={-2}
        pcbRotation={90}
        schX={12}
        schY={0}
        schSheetName="motion"
      />
      <capacitor
        name="C_ALARM"
        capacitance="1nF"
        footprint="0603"
        pcbX={29}
        pcbY={-5}
        pcbRotation={90}
        schX={10}
        schY={0}
        schSheetName="motion"
      />

      <trace from="net.V5" to=".J_SERVO > .pin1" {...powerTrace} />
      <trace
        from=".Q_PULSE > .collector"
        to=".J_SERVO > .pin2"
        {...signalTrace}
      />
      <trace from=".Q_PULSE > .emitter" to="net.GND" {...signalTrace} />
      <trace from="net.PULSE_3V3" to=".R_PULSE_B > .pin1" {...signalTrace} />
      <trace from=".R_PULSE_B > .pin2" to=".Q_PULSE > .base" {...signalTrace} />
      <trace
        from=".Q_PULSE > .base"
        to=".R_PULSE_OFF > .pin1"
        {...signalTrace}
      />
      <trace from=".R_PULSE_OFF > .pin2" to="net.GND" {...signalTrace} />
      <trace from="net.V5" to=".J_SERVO > .pin3" {...powerTrace} />
      <trace
        from=".Q_DIR > .collector"
        to=".J_SERVO > .pin4"
        {...signalTrace}
      />
      <trace from=".Q_DIR > .emitter" to="net.GND" {...signalTrace} />
      <trace from="net.DIR_3V3" to=".R_DIR_B > .pin1" {...signalTrace} />
      <trace from=".R_DIR_B > .pin2" to=".Q_DIR > .base" {...signalTrace} />
      <trace from=".Q_DIR > .base" to=".R_DIR_OFF > .pin1" {...signalTrace} />
      <trace from=".R_DIR_OFF > .pin2" to="net.GND" {...signalTrace} />
      <trace from=".J_SERVO > .pin6" to="net.GND" {...signalTrace} />
      <trace from="net.V3V3" to=".R_ALARM_PU > .pin1" {...signalTrace} />
      <trace
        from=".R_ALARM_PU > .pin2"
        to=".J_SERVO > .pin5"
        {...signalTrace}
      />
      <trace
        from=".J_SERVO > .pin5"
        to=".R_ALARM_DIV > .pin1"
        {...signalTrace}
      />
      <trace from=".R_ALARM_DIV > .pin2" to="net.ALARM_3V3" {...signalTrace} />
      <trace from="net.ALARM_3V3" to=".R_ALARM_PD > .pin1" {...signalTrace} />
      <trace from=".R_ALARM_PD > .pin2" to="net.GND" {...signalTrace} />
      <trace from="net.ALARM_3V3" to=".C_ALARM > .pin1" {...signalTrace} />
      <trace from=".C_ALARM > .pin2" to="net.GND" {...signalTrace} />

      <IOTerminal
        name="J_IO"
        pcbX={34}
        pcbY={-12}
        pcbRotation={90}
        schX={8}
        schY={-5}
        schSheetName="controller"
      />
      <resistor
        name="R_LIM_POS"
        resistance="10k"
        footprint="0603"
        pcbX={23}
        pcbY={-10}
        pcbRotation={90}
        schX={2}
        schY={-5}
        schSheetName="controller"
      />
      <resistor
        name="R_LIM_NEG"
        resistance="10k"
        footprint="0603"
        pcbX={27}
        pcbY={-10}
        pcbRotation={90}
        schX={4}
        schY={-5}
        schSheetName="controller"
      />
      <resistor
        name="R_ESTOP"
        resistance="10k"
        footprint="0603"
        pcbX={31}
        pcbY={-10}
        pcbRotation={90}
        schX={6}
        schY={-5}
        schSheetName="controller"
      />
      <capacitor
        name="C_LIM_POS"
        capacitance="10nF"
        footprint="0603"
        pcbX={23}
        pcbY={-14}
        pcbRotation={90}
        schX={2}
        schY={-8}
        schSheetName="controller"
      />
      <capacitor
        name="C_LIM_NEG"
        capacitance="10nF"
        footprint="0603"
        pcbX={27}
        pcbY={-14}
        pcbRotation={90}
        schX={4}
        schY={-8}
        schSheetName="controller"
      />
      <capacitor
        name="C_ESTOP"
        capacitance="10nF"
        footprint="0603"
        pcbX={31}
        pcbY={-14}
        pcbRotation={90}
        schX={6}
        schY={-8}
        schSheetName="controller"
      />
      <PESD3V3U1UL
        name="D_LIM_POS_ESD"
        pcbX={29}
        pcbY={-22}
        pcbRotation={180}
        schX={1}
        schY={-10}
        schSheetName="controller"
      />
      <PESD3V3U1UL
        name="D_LIM_NEG_ESD"
        pcbX={27}
        pcbY={-18}
        schX={3}
        schY={-10}
        schSheetName="controller"
      />
      <PESD3V3U1UL
        name="D_ESTOP_ESD"
        pcbX={31}
        pcbY={-18}
        schX={5}
        schY={-10}
        schSheetName="controller"
      />
      {/* Remote 10 kΩ NTC input: a second temperature channel mounted at the
          motor or enclosure, with pull-up, RC filtering and ESD clamp. */}
      <resistor
        name="R_NTC_PULL"
        resistance="10k"
        footprint="0603"
        pcbX={18}
        pcbY={-10}
        pcbRotation={90}
        schX={8}
        schY={-5}
        schSheetName="controller"
      />
      <capacitor
        name="C_NTC_FILTER"
        capacitance="10nF"
        footprint="0603"
        pcbX={18}
        pcbY={-14}
        pcbRotation={90}
        schX={8}
        schY={-8}
        schSheetName="controller"
      />
      <PESD3V3U1UL
        name="D_NTC_ESD"
        pcbX={33}
        pcbY={-26}
        schX={8}
        schY={-10}
        schSheetName="controller"
      />
      <trace from=".J_IO > .pin1" to="net.LIMIT_POS" {...signalTrace} />
      <trace from=".J_IO > .pin2" to="net.LIMIT_NEG" {...signalTrace} />
      <trace from=".J_IO > .pin3" to="net.ESTOP_3V3" {...signalTrace} />
      <trace from=".J_IO > .pin4" to="net.GND" {...signalTrace} />
      <trace name="NTC_INPUT" from=".J_IO > .pin5" to="net.NTC_REMOTE" {...signalTrace} />
      <trace from=".J_IO > .pin6" to="net.GND" {...signalTrace} />
      <trace from="net.V3V3" to=".R_LIM_POS > .pin1" {...signalTrace} />
      <trace from=".R_LIM_POS > .pin2" to="net.LIMIT_POS" {...signalTrace} />
      <trace from="net.LIMIT_POS" to=".C_LIM_POS > .pin1" {...signalTrace} />
      <trace from=".C_LIM_POS > .pin2" to="net.GND" {...signalTrace} />
      <trace
        from="net.LIMIT_POS"
        to=".D_LIM_POS_ESD > .pin1"
        {...signalTrace}
      />
      <trace from=".D_LIM_POS_ESD > .pin2" to="net.GND" {...signalTrace} />
      <trace from="net.V3V3" to=".R_LIM_NEG > .pin1" {...signalTrace} />
      <trace from=".R_LIM_NEG > .pin2" to="net.LIMIT_NEG" {...signalTrace} />
      <trace from="net.LIMIT_NEG" to=".C_LIM_NEG > .pin1" {...signalTrace} />
      <trace from=".C_LIM_NEG > .pin2" to="net.GND" {...signalTrace} />
      <trace
        from="net.LIMIT_NEG"
        to=".D_LIM_NEG_ESD > .pin1"
        {...signalTrace}
      />
      <trace from=".D_LIM_NEG_ESD > .pin2" to="net.GND" {...signalTrace} />
      <trace from="net.V3V3" to=".R_ESTOP > .pin1" {...signalTrace} />
      <trace from=".R_ESTOP > .pin2" to="net.ESTOP_3V3" {...signalTrace} />
      <trace from="net.ESTOP_3V3" to=".C_ESTOP > .pin1" {...signalTrace} />
      <trace from=".C_ESTOP > .pin2" to="net.GND" {...signalTrace} />
      <trace
        from="net.ESTOP_3V3"
        to=".D_ESTOP_ESD > .pin1"
        {...signalTrace}
      />
      <trace from=".D_ESTOP_ESD > .pin2" to="net.GND" {...signalTrace} />
      <trace name="NTC_PULLUP" from="net.V3V3" to=".R_NTC_PULL > .pin1" {...signalTrace} />
      <trace name="NTC_SENSE" from=".R_NTC_PULL > .pin2" to="net.NTC_REMOTE" {...signalTrace} />
      <trace name="NTC_FILTER" from="net.NTC_REMOTE" to=".C_NTC_FILTER > .pin1" {...signalTrace} />
      <trace name="NTC_FILTER_GND" from=".C_NTC_FILTER > .pin2" to="net.GND" {...signalTrace} />
      <trace name="NTC_ESD" from="net.NTC_REMOTE" to=".D_NTC_ESD > .pin1" {...signalTrace} />
      <trace name="NTC_ESD_GND" from=".D_NTC_ESD > .pin2" to="net.GND" {...signalTrace} />

      <XL_1608SURC_06
        name="LED_5V"
        pcbX={-36}
        pcbY={17}
        schX={-8}
        schY={-8}
        schSheetName="controller"
      />
      <resistor
        name="R_LED_5V"
        resistance="1k"
        footprint="0603"
        pcbX={-32}
        pcbY={17}
        pcbRotation={90}
        schX={-10}
        schY={-8}
        schSheetName="controller"
      />
      <trace from="net.V5" to=".R_LED_5V > .pin1" {...signalTrace} />
      <trace from=".R_LED_5V > .pin2" to=".LED_5V > .anode" {...signalTrace} />
      <trace from=".LED_5V > .cathode" to="net.GND" {...signalTrace} />

      {/* NEMA-23 face pattern: 47.14 mm square, M5 clearance, grounded PTH. */}
      {[-23.57, 23.57].flatMap((x) =>
        [-23.57, 23.57].map((y) => (
          <chip
            key={`${x}:${y}`}
            name={`MH_${x}_${y}`}
            pcbX={x}
            pcbY={y}
            pinLabels={{ pin1: ["GND"] }}
            connections={{ pin1: "net.GND" }}
            footprint={
              <footprint>
                <platedhole
                  portHints={["pin1"]}
                  shape="circle"
                  holeDiameter="5.3mm"
                  outerDiameter="7.5mm"
                />
              </footprint>
            }
          />
        )),
      )}
      {/* Fabrication copper: grounded top and bottom planes. The grounded
          NEMA mounting holes tie into these pours; non-GND nets keep the
          same 0.20 mm isolation on pads and traces. */}
      <copperpour
        name="GND_TOP"
        layer="top"
        connectsTo="net.GND"
        clearance="0.2mm"
        padMargin="0.2mm"
        traceMargin="0.2mm"
        boardEdgeMargin="0.3mm"
        coveredWithSolderMask
      />
      <copperpour
        name="GND_BOTTOM"
        layer="bottom"
        connectsTo="net.GND"
        clearance="0.2mm"
        padMargin="0.2mm"
        traceMargin="0.2mm"
        boardEdgeMargin="0.3mm"
        coveredWithSolderMask
      />
      <silkscreentext
        text="iSV57T-090 SERVO CTRL"
        pcbX={-13}
        pcbY={30}
        fontSize="1.5mm"
      />
      <silkscreenpath
        route={[
          { x: -10, y: -14 },
          { x: 10, y: -14 },
          { x: 10, y: 14 },
          { x: -10, y: 14 },
          { x: -10, y: -14 },
        ]}
      />
      <silkscreentext text="RP2040 / 12MHz XO" pcbX={0} pcbY={-16} fontSize="1.2mm" />
      <silkscreentext text="36V IN" pcbX={-34} pcbY={-8} fontSize="1.5mm" />
      <silkscreentext
        text="SERVO I/O"
        pcbX={32}
        pcbY={23}
        fontSize="1mm"
      />
      <silkscreentext
        text="LIMIT / ESTOP"
        pcbX={32}
        pcbY={-27}
        fontSize="1mm"
      />
      <silkscreentext
        text="USB-C / SWD"
        pcbX={-32}
        pcbY={-25}
        fontSize="1mm"
      />
    </board>
  )
}