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.41
License
unset
Stars
3

scripts/expand-revision-routes.ts

import { AutoroutingPipelineSolver } from '@tscircuit/capacity-autorouter';
import { readFileSync,writeFileSync } from 'node:fs';
const file=process.argv[2] ?? 'dist/revision/route-plan.json';
const plan=JSON.parse(readFileSync(file,'utf8'));
const input:any=structuredClone(plan.expectedInput);
const fixedIds=new Set((input.traces??[]).map((t:any)=>t.pcb_trace_id));
input.traces=[...structuredClone(input.traces??[]),...structuredClone(plan.traces)];
const geometry=JSON.parse(readFileSync(file.replace(/route-plan\.json$/, 'circuit.json'),'utf8'));
const holes:any[]=geometry.filter((e:any)=>e.type==='pcb_plated_hole');
// The SRJ expander calls an existing multilayer pad a through_obstacle. This
// represents the same fixed plated barrel as Circuit JSON's through_pad; it
// must not be represented as a movable/new via. The expander validates a
// same-net multilayer obstacle witness for every such transition.
for(const t of input.traces)t.route=t.route.map((p:any)=>p.route_type==='through_pad'?{
 route_type:'through_obstacle',start:p.start,end:p.end,from_layer:p.start_layer,to_layer:p.end_layer,width:p.width,pcb_plated_hole_id:p.pcb_plated_hole_id,
}:p);
const pipeline=new AutoroutingPipelineSolver(input);
const phase=pipeline.pipelineDef.find(x=>x.solverName==='powerTraceExpansionSolver')!;
const power=process.argv.includes('--power');
const names=input.connections.filter((x:any)=>power ? (x.nominalTraceWidth??0)>=0.5 : (x.nominalTraceWidth??0)>=0.15 && (x.nominalTraceWidth??0)<0.5).map((x:any)=>x.name);
const connectionArg=process.argv.find(arg=>arg.startsWith('--connections='));
if(connectionArg){const requested=new Set(connectionArg.split('=')[1].split(','));names.splice(0,names.length,...names.filter((name:string)=>requested.has(name)));}
const solver=new (phase.solverClass as any)(input,{onlyConnectionNames:names,powerTraceToPadClearance:0.15,allowNewVias:false});
solver.powerTraceExpanderSolver.MAX_ITERATIONS=40_000_000;
solver.MAX_ITERATIONS=40_000_001;
console.log('Expanding',names.length,power?'power':'signal','connections');
let last=Date.now();
while(!solver.solved&&!solver.failed){solver.step();if(Date.now()-last>15000){last=Date.now();console.log(solver.iterations,JSON.stringify(solver.stats));}}
console.log('DONE',solver.solved,solver.error,JSON.stringify(solver.stats));
if(!solver.solved)process.exit(1);
const output=solver.getOutput().filter((t:any)=>!fixedIds.has(t.pcb_trace_id));
writeFileSync('work/freerouting/expanded-raw.json',JSON.stringify({output},null,2));
for(const t of output)t.route=t.route.map((p:any)=>{
 if(p.route_type!=='through_obstacle')return p;
 const h=holes.find(h=>h.pcb_plated_hole_id===p.pcb_plated_hole_id)??holes.find(h=>Math.hypot(h.x-p.start.x,h.y-p.start.y)<1e-5&&Math.hypot(h.x-p.end.x,h.y-p.end.y)<1e-5);
 if(!h)throw new Error('Through-obstacle output does not match an existing plated hole');
 return {route_type:'through_pad',start:p.start,end:p.end,start_layer:p.from_layer,end_layer:p.to_layer,width:p.width??.15,pcb_plated_hole_id:h.pcb_plated_hole_id};
});
plan.traces=output;
writeFileSync(file,JSON.stringify(plan,null,2));