imrishabh18/nema17-motor-controller-flex

An RP2040-based USB‑C/USB‑PD controller board that powers and drives a NEMA17 stepper via a DRV8825, with current/temperature/voltage monitoring, optional AS5600 encoder, buzzer/RGB status indicators, QSPI flash, and flexible motor/encoder connections.

Version
1.2.0
License
unset
Stars
0

mechanical/flex-layout.ts

import { motorMountGap } from "./Nema17Assembly";
import mainOutline from "./nema17-outline.json";

type Point = { x: number; y: number };
export const mainBoardOutline = mainOutline;
export const mainRight = 21.1499958;
// Flat distances from the main board edge to each component centre.
export const encoderTailLength = 40;
export const motorTailLength = 20;
export const encoderPosition = { x: mainRight + encoderTailLength, y: 8 };
export const motorPosition = { x: mainRight + motorTailLength, y: 0 };
export const islandStart = (x: number) => x - 6.15;
export const bendRadius = 3.5;
// Align the socket opening plane with the nominal motor header mouth (X=27)
// and both row centres at Z=-(mountGap + boardHalfThickness + rearCapHalf).
// Solving the 90-degree cylindrical fold keeps the requested 20 mm flat reach.
export const motorHeaderMouthX = 27;
export const motorHeaderRowDepth = motorMountGap + 0.1 + 2.5;
const socketOpeningHeight = 4.4;
const targetSocketPlaneX = motorHeaderMouthX + 0.1 + socketOpeningHeight;
const c = 1 - Math.PI / 4;
export const motorBendRadius = 5;
export const motorBendX = targetSocketPlaneX - c * motorBendRadius;
export const motorBendEndX = motorBendX + Math.PI * motorBendRadius / 4;
// The bisector reflects the sensor centre to shaft XY=(0,0) at a 180° fold.
// Negative angle folds under the main board; R=3.5 mm gives a 7 mm neutral-plane offset.
const encoderFoldX = (y:number) => ((encoderPosition.x**2+encoderPosition.y**2)/2-encoderPosition.y*y)/encoderPosition.x;
export const encoderBend = {x1:encoderFoldX(9),y1:9,x2:encoderFoldX(19),y2:19};

function roundedCorners(points: Point[], distance = 0.9): Point[] {
  return points.flatMap((p, i) => {
    const a = points[(i + points.length - 1) % points.length];
    const b = points[(i + 1) % points.length];
    const before = Math.hypot(p.x-a.x,p.y-a.y), after = Math.hypot(b.x-p.x,b.y-p.y);
    const d = Math.min(distance,before*0.35,after*0.35);
    const start = { x:p.x+(a.x-p.x)*d/before, y:p.y+(a.y-p.y)*d/before };
    const end = { x:p.x+(b.x-p.x)*d/after, y:p.y+(b.y-p.y)*d/after };
    return Array.from({length:9},(_,k)=>{
      const t=k/8,u=1-t;
      return {x:u*u*start.x+2*u*t*p.x+t*t*end.x,y:u*u*start.y+2*u*t*p.y+t*t*end.y};
    });
  });
}

const rightEdge = mainOutline.findIndex((p,i)=>p.x>21.14 && p.y<0 && mainOutline[i+1]?.x>21.14 && mainOutline[i+1]?.y>0);
if(rightEdge<0) throw new Error("Main board right edge not found");
const m=motorPosition.x,e=encoderPosition.x;
// One continuous outline: two separate tongues with rounded shoulders and tips.
const additions:Point[]=[
 {x:mainRight,y:-13.5},{x:mainRight+2,y:-12},{x:26.8,y:-12},
 {x:28.3,y:-4},{x:m-7,y:-4},
 {x:m-5,y:-7.5},{x:m+5,y:-7.5},{x:m+6,y:-6.5},
 {x:m+6,y:6.5},{x:m+5,y:7.5},{x:m-5,y:7.5},
 {x:m-7,y:4},{x:23.9,y:4},{x:23.9,y:10.5},
 {x:49,y:10.5},{x:e-6,y:4},
 {x:e-5,y:2},{x:e+5,y:2},{x:e+6,y:3},
 {x:e+6,y:13},{x:e+5,y:14},{x:e-5,y:14},
 {x:e-6,y:12},{x:50.5,y:16.5},{x:mainRight+3,y:16.5},{x:mainRight,y:14},
];
// Round the added contour only; retain the original four mounting corners.
const withAnchors=[mainOutline[rightEdge],...additions,mainOutline[rightEdge+1]];
const rounded=roundedCorners(withAnchors).slice(9,-9);
export const flexBoardOutline=[...mainOutline.slice(0,rightEdge+1),...rounded,...mainOutline.slice(rightEdge+1)];

export const tailNecks=[
 {name:"ENCODER_NECK",x1:mainRight+0.35,x2:islandStart(e),y1:9.8,y2:17.2},
 {name:"MOTOR_NECK",x1:26.5,x2:m-2.8,y1:-8,y2:8},
];
export const encoderStiffenerOutline=[
 {x:e-5.65,y:4.25},{x:e-4.65,y:2.6},{x:e+4.7,y:2.6},
 {x:e+5.4,y:3.6},{x:e+5.4,y:12.4},{x:e+4.7,y:13.4},
 {x:e-4.65,y:13.4},{x:e-5.65,y:11.75},
];
// Edge-open solder-access slot clears all six socket solder tails.
export const motorConnectorStiffenerOutline=[
 {x:m-3.15,y:-3.75},{x:m-2.8,y:-6.9},{x:m+4.7,y:-6.9},
 {x:m+5.4,y:-6.5},{x:m+5.4,y:-6.1},{x:m-1.3,y:-6.1},
 {x:m-1.3,y:6.1},{x:m+5.4,y:6.1},{x:m+5.4,y:6.5},
 {x:m+4.7,y:6.9},{x:m-2.8,y:6.9},{x:m-3.15,y:3.75},
];