diff --git a/chassis/canable_mount.scad b/chassis/canable_mount.scad new file mode 100644 index 0000000..0e40a2e --- /dev/null +++ b/chassis/canable_mount.scad @@ -0,0 +1,265 @@ +// ============================================================ +// canable_mount.scad — CANable 2.0 USB-CAN Adapter Cradle +// Issue #654 / sl-mechanical 2026-03-16 +// ============================================================ +// Snap-fit cradle for CANable 2.0 PCB (~60 × 18 × 10 mm). +// Attaches to 2020 aluminium T-slot rail via 2× M5 T-nuts. +// +// Port access: +// USB-C port — X− end wall cutout (connector protrudes through) +// CAN terminal — X+ end wall cutout (CANH / CANL / GND wire exit) +// LED status window— slot in Y+ side wall, PCB top-face LEDs visible +// +// Retention: snap-fit cantilever lips on both side walls (PETG flex). +// Cable strain relief: zip-tie boss pair on X+ shelf (CAN wires). +// +// ⚠ VERIFY WITH CALIPERS BEFORE PRINTING: +// PCB_L, PCB_W board outline +// USBC_W, USBC_H USB-C shell at X− edge +// TERM_W, TERM_H 3-pos terminal block at X+ edge +// LED_X_CTR, LED_WIN_W LED window position on Y+ wall +// +// Print settings (PETG): +// 3 perimeters, 40 % gyroid infill, no supports, 0.2 mm layer +// Print orientation: open face UP (as modelled) +// +// BOM: +// 2 × M5×10 BHCS + 2 × M5 slide-in T-nut (2020 rail) +// +// Export commands: +// openscad -D 'RENDER="mount"' -o canable_mount.stl canable_mount.scad +// openscad -D 'RENDER="assembly"' -o canable_assembly.png canable_mount.scad +// ============================================================ + +RENDER = "assembly"; // mount | assembly + +$fn = 48; +EPS = 0.01; + +// ── ⚠ Verify before printing ───────────────────────────────── +// CANable 2.0 PCB +PCB_L = 60.0; // board length (X: USB-C end → terminal end) +PCB_W = 18.0; // board width (Y) +PCB_T = 1.6; // board thickness +COMP_H = 8.5; // tallest component above board (USB-C shell ≈ 3.5 mm; + // terminal block ≈ 8.5 mm) + +// USB-C connector (at X− end face of PCB) +USBC_W = 9.5; // connector outer width +USBC_H = 3.8; // connector outer height above board surface +USBC_Z0 = 0.0; // connector bottom offset above board surface + +// CAN screw-terminal block (at X+ end face, 3-pos 5.0 mm pitch) +TERM_W = 16.0; // terminal block span (3 × 5 mm + housing) +TERM_H = 9.0; // terminal block height above board surface +TERM_Z0 = 0.5; // terminal bottom offset above board surface + +// Status LED window (LEDs near USB-C end on PCB top face) +// Rectangular slot cut in Y+ side wall — LEDs visible from the side +LED_X_CTR = 11.0; // LED zone centre measured from PCB X− edge +LED_WIN_W = 14.0; // window width (X) +LED_WIN_H = 5.5; // window height (Z) — opens top portion of side wall + +// ── Cradle geometry ────────────────────────────────────────── +WALL_T = 2.5; // side/end wall thickness +FLOOR_T = 4.0; // floor plate thickness (accommodates M5 BHCS head pocket) +CL_SIDE = 0.30; // Y clearance per side (total 0.6 mm play) +CL_END = 0.40; // X clearance per end + +// Interior cavity +INN_W = PCB_W + 2*CL_SIDE; // Y span +INN_L = PCB_L + 2*CL_END; // X span +INN_H = PCB_T + COMP_H + 1.2; // Z height (board + tallest comp + margin) + +// Outer body +OTR_W = INN_W + 2*WALL_T; // Y +OTR_L = INN_L + 2*WALL_T; // X +OTR_H = FLOOR_T + INN_H; // Z + +// PCB reference origin within body (lower-left corner of board) +PCB_X0 = WALL_T + CL_END; // board X start inside body +PCB_Y0 = WALL_T + CL_SIDE; // board Y start inside body +PCB_Z0 = FLOOR_T; // board bottom sits on floor + +// ── Snap-fit lips ───────────────────────────────────────────── +// Cantilever ledge on inner face of each side wall, at PCB-top Z. +// Tapered (chamfered) entry guides PCB in from above. +SNAP_IN = 0.8; // how far inward ledge protrudes over PCB edge +SNAP_T = 1.2; // snap-arm thickness (thin for PETG flex) +SNAP_H = 4.0; // cantilever arm height (root at OTR_H, tip near PCB_Z0+PCB_T) +SNAP_L = 18.0; // arm length along X (centred on PCB, shorter = more flex) +// Snap on Y− wall protrudes in +Y direction; Y+ wall protrudes in −Y direction + +// ── M5 T-nut mount (2020 rail) ──────────────────────────────── +M5_D = 5.3; // M5 bolt clearance bore +M5_HEAD_D = 9.5; // M5 BHCS head pocket diameter (from bottom face) +M5_HEAD_H = 3.0; // BHCS head pocket depth +M5_SPAC = 20.0; // bolt spacing along X (centred on cradle) +// Standard M5 slide-in T-nuts used — no T-nut pocket moulded in. + +// ── Cable strain relief ─────────────────────────────────────── +// Two zip-tie anchor bosses on a shelf inside the X+ end, straddling +// the CAN terminal wires. +SR_BOSS_OD = 7.0; // boss outer diameter +SR_BOSS_H = 5.5; // boss height above floor +SR_SLOT_W = 3.5; // zip-tie slot width +SR_SLOT_T = 2.2; // zip-tie slot through-height +// Boss Y positions (straddle terminal block) +SR_Y1 = WALL_T + INN_W * 0.25; +SR_Y2 = WALL_T + INN_W * 0.75; +SR_X = OTR_L - WALL_T - SR_BOSS_OD/2 - 2.5; // just inside X+ end wall + +// ───────────────────────────────────────────────────────────── +module canable_mount() { + difference() { + // ── Outer solid body ────────────────────────────────── + union() { + cube([OTR_L, OTR_W, OTR_H]); + + // ── Snap cantilever arms on Y− wall (protrude inward +Y) ── + // Arms hang down from top of Y− wall inner face. + // Root is flush with inner face (Y = WALL_T); tip at PCB level. + translate([OTR_L/2 - SNAP_L/2, WALL_T - SNAP_T, OTR_H - SNAP_H]) + cube([SNAP_L, SNAP_T, SNAP_H]); + + // ── Snap cantilever arms on Y+ wall (protrude inward −Y) ── + translate([OTR_L/2 - SNAP_L/2, OTR_W - WALL_T, OTR_H - SNAP_H]) + cube([SNAP_L, SNAP_T, SNAP_H]); + + // ── Cable strain relief bosses (X+ end, inside) ──── + for (sy = [SR_Y1, SR_Y2]) + translate([SR_X, sy, 0]) + cylinder(d=SR_BOSS_OD, h=SR_BOSS_H); + } + + // ── Interior cavity ─────────────────────────────────── + translate([WALL_T, WALL_T, FLOOR_T]) + cube([INN_L, INN_W, INN_H + EPS]); + + // ── USB-C cutout — X− end wall ──────────────────────── + // Centred on PCB width; opened from board surface upward + translate([-EPS, + PCB_Y0 + PCB_W/2 - (USBC_W + 1.5)/2, + PCB_Z0 + USBC_Z0 - 0.5]) + cube([WALL_T + 2*EPS, USBC_W + 1.5, USBC_H + 2.5]); + + // ── CAN terminal cutout — X+ end wall ───────────────── + // Full terminal width + 2 mm margin for screwdriver access; + // height clears terminal block + wire bend radius + translate([OTR_L - WALL_T - EPS, + PCB_Y0 + PCB_W/2 - (TERM_W + 2.0)/2, + PCB_Z0 + TERM_Z0 - 0.5]) + cube([WALL_T + 2*EPS, TERM_W + 2.0, TERM_H + 5.0]); + + // ── LED status window — Y+ side wall ───────────────── + // Rectangular slot; LEDs at top-face of PCB are visible through it + translate([PCB_X0 + LED_X_CTR - LED_WIN_W/2, + OTR_W - WALL_T - EPS, + OTR_H - LED_WIN_H]) + cube([LED_WIN_W, WALL_T + 2*EPS, LED_WIN_H + EPS]); + + // ── M5 BHCS head pockets (from bottom face of floor) ── + for (mx = [OTR_L/2 - M5_SPAC/2, OTR_L/2 + M5_SPAC/2]) + translate([mx, OTR_W/2, -EPS]) { + // Clearance bore through full floor + cylinder(d=M5_D, h=FLOOR_T + 2*EPS); + // BHCS head pocket from bottom face + cylinder(d=M5_HEAD_D, h=M5_HEAD_H + EPS); + } + + // ── Snap-arm ledge slot — Y− arm (hollow out to thin arm) ── + // Arm is SNAP_T thick; cut away material behind arm + translate([OTR_L/2 - SNAP_L/2 - EPS, EPS, OTR_H - SNAP_H]) + cube([SNAP_L + 2*EPS, WALL_T - SNAP_T - EPS, SNAP_H + EPS]); + + // ── Snap-arm ledge slot — Y+ arm ────────────────────── + translate([OTR_L/2 - SNAP_L/2 - EPS, OTR_W - WALL_T + SNAP_T, OTR_H - SNAP_H]) + cube([SNAP_L + 2*EPS, WALL_T - SNAP_T - EPS, SNAP_H + EPS]); + + // ── Snap-arm inward ledge notch (entry chamfer removed) ─ + // Chamfer top of snap arm so PCB slides in easily + // Y− arm: chamfer on upper-inner edge → 45° wedge on +Y/+Z corner + translate([OTR_L/2 - SNAP_L/2 - EPS, + WALL_T - SNAP_T - EPS, + OTR_H - SNAP_IN]) + rotate([0, 0, 0]) + rotate([45, 0, 0]) + cube([SNAP_L + 2*EPS, SNAP_IN * 1.5, SNAP_IN * 1.5]); + + // Y+ arm: chamfer on upper-inner edge + translate([OTR_L/2 - SNAP_L/2 - EPS, + OTR_W - WALL_T + SNAP_T - SNAP_IN * 1.5 + EPS, + OTR_H - SNAP_IN]) + rotate([45, 0, 0]) + cube([SNAP_L + 2*EPS, SNAP_IN * 1.5, SNAP_IN * 1.5]); + + // ── Snap ledge cutout on Y− arm inner tip ───────────── + // Creates inward nub: remove top portion of arm inner tip + // leaving bottom SNAP_IN height as the retaining ledge + translate([OTR_L/2 - SNAP_L/2 - EPS, + WALL_T - SNAP_T - EPS, + PCB_Z0 + PCB_T + SNAP_IN]) + cube([SNAP_L + 2*EPS, SNAP_T + 2*EPS, + OTR_H - (PCB_Z0 + PCB_T + SNAP_IN) + EPS]); + + // ── Snap ledge cutout on Y+ arm inner tip ───────────── + translate([OTR_L/2 - SNAP_L/2 - EPS, + OTR_W - WALL_T - EPS, + PCB_Z0 + PCB_T + SNAP_IN]) + cube([SNAP_L + 2*EPS, SNAP_T + 2*EPS, + OTR_H - (PCB_Z0 + PCB_T + SNAP_IN) + EPS]); + + // ── Zip-tie slots through strain relief bosses ───────── + for (sy = [SR_Y1, SR_Y2]) + translate([SR_X, sy, + SR_BOSS_H/2 - SR_SLOT_T/2]) + rotate([0, 90, 0]) + cube([SR_SLOT_T, SR_SLOT_W, + SR_BOSS_OD + 2*EPS], + center=true); + + // ── Weight relief pocket in floor (underside) ───────── + translate([WALL_T + 8, WALL_T + 3, -EPS]) + cube([OTR_L - 2*WALL_T - 16, OTR_W - 2*WALL_T - 6, + FLOOR_T - 1.5 + EPS]); + } +} + +// ── Assembly preview ───────────────────────────────────────── +if (RENDER == "assembly") { + color("DimGray", 0.93) canable_mount(); + + // Phantom PCB + color("MidnightBlue", 0.35) + translate([PCB_X0, PCB_Y0, PCB_Z0]) + cube([PCB_L, PCB_W, PCB_T]); + + // Phantom component block (top of PCB) + color("DarkSlateGray", 0.25) + translate([PCB_X0, PCB_Y0, PCB_Z0 + PCB_T]) + cube([PCB_L, PCB_W, COMP_H]); + + // USB-C port highlight + color("Gold", 0.8) + translate([-1, + PCB_Y0 + PCB_W/2 - USBC_W/2, + PCB_Z0 + USBC_Z0]) + cube([WALL_T + 2, USBC_W, USBC_H]); + + // Terminal block highlight + color("Tomato", 0.7) + translate([OTR_L - WALL_T - 1, + PCB_Y0 + PCB_W/2 - TERM_W/2, + PCB_Z0 + TERM_Z0]) + cube([WALL_T + 2, TERM_W, TERM_H]); + + // LED zone highlight + color("LimeGreen", 0.9) + translate([PCB_X0 + LED_X_CTR - LED_WIN_W/2, + OTR_W - WALL_T - 0.5, + OTR_H - LED_WIN_H + 1]) + cube([LED_WIN_W, 1, LED_WIN_H - 2]); + +} else { + canable_mount(); +}