muse/esp32-eink-display

A 55×46 mm two-layer ESP32-S3 e-paper controller board with USB‑C power/programming, buck regulation, FPC display connector, UC8179-style boost/charge-pump circuitry, ESD protection, controls, and routed passive components.

Version
1.3.4
License
unset
Stars
0

routing.ts

import { AutoroutingPipelineSolver, type SimpleRouteJson } from "@tscircuit/capacity-autorouter"

// The installed built-in pipeline routes the board. A final geometry cleanup
// separates distinct-net vias that its output places just under copper clearance.
// Attached wire vertices move with each via; the normal board DRC checks the result.
export async function createMuseRouter(input: SimpleRouteJson) {
  const solver = new AutoroutingPipelineSolver(input)
  const output = () => {
    const traces = solver.getOutputSimplifiedPcbTraces()
    const vias: {x:number,y:number,net:string}[] = []
    for (const t of traces) for (const p of t.route) {
      if (p.route_type !== "via") continue
      if (!vias.some(v=>v.net===t.connection_name && Math.hypot(v.x-p.x,v.y-p.y)<1e-8))
        vias.push({x:p.x,y:p.y,net:t.connection_name})
    }
    const minimum = (input.minViaPadDiameter ?? 0.5) +
      (input.minTraceToPadEdgeClearance ?? 0.15) + 0.01
    let moved = 0
    for (let i=0;i<vias.length;i++) for (let j=i+1;j<vias.length;j++) {
      const a=vias[i],b=vias[j]
      if (a.net===b.net) continue
      const distance=Math.hypot(b.x-a.x,b.y-a.y)
      if (distance>=minimum || distance<1e-8) continue
      const dx=(b.x-a.x)*(minimum-distance)/distance
      const dy=(b.y-a.y)*(minimum-distance)/distance
      for (const t of traces) if(t.connection_name===b.net) for(const p of t.route)
        if("x" in p && Math.hypot(p.x-b.x,p.y-b.y)<1e-8) {p.x+=dx;p.y+=dy}
      b.x+=dx;b.y+=dy;moved++
    }
    console.log(`Muse routing: adjusted ${moved} via clearance(s); physical vias 0.3/0.5 mm`)
    return traces
  }
  const handlers = new Map<string, ((event: unknown) => void)[]>()
  const emit = (name: string, event: unknown) => handlers.get(name)?.forEach(fn => fn(event))
  let stopped = false
  let running = false
  return {
    input,
    get isRouting() {return running},
    stop() {stopped = true; running = false},
    solveSync() {solver.solve(); return output()},
    on(name: string, fn: (event: unknown) => void) {
      handlers.set(name, [...(handlers.get(name) ?? []), fn])
    },
    start() {
      running = true
      const tick = () => {
        if (stopped) return
        try {
          for (let i=0; i<1000 && !solver.solved && !solver.failed; i++) solver.step()
          if (solver.failed) {emit("error", {error: new Error(solver.error ?? "Routing failed")}); return}
          if (solver.solved) {emit("complete", {traces: output()}); return}
          setTimeout(tick, 0)
        } catch(error) {emit("error", {error})}
      }
      setTimeout(tick, 0)
    },
  }
}