ShiboSoftwareDev/nema8-20mm-dual-usbc-stepper-controller

A compact 4-layer USB‑C PD stepper-motor controller PCB using a CH32X035 MCU, TMC2209 driver, dual USB‑C ports, power protection/regulation, ESD/TVS diodes, current-sense resistors, and NEMA‑8 motor connector.

Version
1.0.13
License
unset
Stars
0

scripts/test-manual-repairs.mjs

import fs from "node:fs"
import { runAllRoutingChecks } from "@tscircuit/checks"

let circuit = JSON.parse(fs.readFileSync("dist/index/circuit.json", "utf8"))
  .filter((element) => !String(element.type).endsWith("error"))

const samePoint = (point, x, y) => Math.hypot(point.x - x, point.y - y) < 1e-5
const groundVia = circuit.find((element) => element.type === "pcb_via" && element.pcb_via_id === "pcb_via_24")
if (false && groundVia) {
  circuit = circuit.map((element) => {
    if (element.type === "pcb_via" && element.pcb_via_id === groundVia.pcb_via_id) {
      return { ...element, x: -1.8, y: -4.7 }
    }
    if (element.type !== "pcb_trace" || element.pcb_trace_id !== groundVia.pcb_trace_id) return element
    return {
      ...element,
      route: element.route.map((point) => samePoint(point, groundVia.x, groundVia.y)
        ? { ...point, x: -1.8, y: -4.7 }
        : point),
    }
  })
}

const wire = (layer, x, y, width = 0.15) => ({ route_type: "wire", layer, x, y, width })
const via = (fromLayer, toLayer, x, y) => ({
  route_type: "via", x, y, from_layer: fromLayer, to_layer: toLayer,
  via_diameter: 0.45, via_hole_diameter: 0.3,
})

const originalEnableBranch = circuit.find((element) =>
  element.type === "pcb_trace" && element.pcb_trace_id === "source_trace_42__source_trace_43_mst1_0")
const firstInner2 = originalEnableBranch.route.findIndex((point) =>
  point.route_type === "wire" && point.layer === "inner2")
const enableTail = originalEnableBranch.route.slice(firstInner2 + 1)

const replacements = {
  source_trace_42__source_trace_43_mst0_0: [
    wire("top", -1, -3),
    wire("top", -1, -3.7),
    wire("top", -1.6, -4.7),
    via("top", "inner2", -1.6, -4.7),
    wire("inner2", -1.6, -4.7),
    wire("inner2", -4.5, -4),
    via("inner2", "top", -4.5, -4),
    wire("top", -4.5, -4),
    wire("top", -3.69, -3),
  ],
  source_trace_42__source_trace_43_mst1_0: [
    wire("inner2", -1.6, -4.7),
    ...enableTail,
  ],
  source_trace_14__source_trace_15__source_trace_16_mst1_0: [
    wire("bottom", 8.2, 5.699998, 0.2),
    wire("bottom", 8.4965, 4.2879, 0.2),
    via("bottom", "inner2", 8.4965, 4.2879),
    wire("inner2", 8.4965, 4.2879, 0.2),
    wire("inner2", 9.5, 3.2, 0.2),
    wire("inner2", 9.5, -0.5, 0.2),
    wire("inner2", 8.5, -1.0, 0.2),
    wire("inner2", 7.4, -3.7, 0.2),
    via("inner2", "bottom", 7.4, -3.7),
    wire("bottom", 7.4, -3.7, 0.2),
    wire("bottom", 8.5, -3.375, 0.2),
  ],
}

const vioTraceId = "source_trace_17__source_trace_18__source_trace_19__source_trace_20__source_trace_21__source_trace_22__source_trace_23__source_trace_24__source_trace_25_mst8_0"
circuit = circuit.map((element) => {
  if (element.type !== "pcb_trace" || element.pcb_trace_id !== vioTraceId) return element
  const route = [
    ...element.route.slice(0, 11),
    { ...element.route[10], x: -0.6, y: 5.7 },
    { ...element.route[10], x: 1.3, y: 5.7 },
    ...element.route.slice(12),
  ]
  return { ...element, route }
})

const vmSenseBTraceId = "source_trace_3__source_trace_4__source_trace_5__source_trace_6__source_trace_7__source_trace_8__source_trace_9__source_trace_10__source_trace_11_mst8_0"
circuit = circuit.map((element) => {
  if (element.type !== "pcb_trace" || element.pcb_trace_id !== vmSenseBTraceId) return element
  return {
    ...element,
    route: element.route.flatMap((point) => point.route_type === "wire" && point.layer === "bottom" &&
      samePoint(point, -2.3924, -2.575)
      ? [{ ...point, x: -2.3, y: -3.1113 }, point]
      : [point]),
  }
})

const replacementIds = new Set(Object.keys(replacements))
const viaTemplate = circuit.find((element) => element.type === "pcb_via")
const rEnPort = circuit.find((element) => element.type === "pcb_port" &&
  samePoint(element, -3.69, -3) && element.layers?.includes("top"))
circuit = circuit.flatMap((element) => {
  if (element.type === "pcb_via" && replacementIds.has(element.pcb_trace_id)) return []
  if (element.type !== "pcb_trace" || !replacements[element.pcb_trace_id]) return [element]
  const firstPort = element.pcb_trace_id === "source_trace_42__source_trace_43_mst1_0"
    ? undefined
    : element.route.find((point) => point.start_pcb_port_id)?.start_pcb_port_id
  const lastPort = [...element.route].reverse().find((point) => point.end_pcb_port_id)?.end_pcb_port_id
  const route = replacements[element.pcb_trace_id].map((point, index, all) => ({
    ...point,
    ...(index === 0 && firstPort ? { start_pcb_port_id: firstPort } : {}),
    ...(index === all.length - 1 && lastPort ? { end_pcb_port_id: lastPort } : {}),
  }))
  const newVias = route.filter((point) => point.route_type === "via").map((point, index) => ({
    ...viaTemplate,
    pcb_via_id: `manual_repair_${element.pcb_trace_id}_${index}`,
    pcb_trace_id: element.pcb_trace_id,
    x: point.x,
    y: point.y,
    hole_diameter: 0.3,
    outer_diameter: 0.45,
    layers: ["top", "inner1", "inner2", "bottom"],
    from_layer: point.from_layer,
    to_layer: point.to_layer,
    subcircuit_id: element.subcircuit_id,
  }))
  return [{ ...element, route }, ...newVias]
})

const errors = await runAllRoutingChecks(circuit)
const hard = errors.filter((error) => error.type !== "pcb_trace_warning")
console.log(`hard=${hard.length} warnings=${errors.length - hard.length}`)
console.log(hard.map((error) => `${error.type}\t${error.message}`).join("\n"))
fs.writeFileSync("/tmp/nema8-manual-repairs.json", JSON.stringify(circuit))