From 837c42a00d04b47b1bbede328dc0d2f2e14afb27 Mon Sep 17 00:00:00 2001 From: sl-mechanical Date: Sun, 15 Mar 2026 10:04:37 -0400 Subject: [PATCH] feat: Jetson Orin Nano mount bracket (Issue #612) --- chassis/jetson_orin_mount.scad | 386 +++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 chassis/jetson_orin_mount.scad diff --git a/chassis/jetson_orin_mount.scad b/chassis/jetson_orin_mount.scad new file mode 100644 index 0000000..d2c106b --- /dev/null +++ b/chassis/jetson_orin_mount.scad @@ -0,0 +1,386 @@ +// ============================================================ +// Jetson Orin Nano Carrier Board Mount — Issue #612 +// Agent : sl-mechanical +// Date : 2026-03-15 +// Part catalogue: +// 1. tnut_base — 2020 T-slot rail interface plate, M5 T-nut captive pockets +// 2. standoff_post — M2.5 captive-nut standoff post (×4), 10 mm airflow gap +// 3. side_brace — lateral stiffening brace with port-access cutouts (×2) +// 4. duct_shroud — optional top heatsink duct / fan-exhaust channel +// 5. cable_clip — snap-on cable management clip for brace edge +// +// BOM: +// 4 × M5×10 BHCS + M5 T-nuts (tnut_base to rail, 2 per rail) +// 4 × M2.5×20 SHCS (board to standoff posts) +// 4 × M2.5 hex nuts (captured in standoff posts) +// 4 × M3×8 SHCS + washers (side_brace to tnut_base) +// 2 × M3×16 SHCS (duct_shroud to side_brace tops) +// +// Jetson Orin Nano carrier board (Seeed reComputer / official dev kit): +// Board dims : 100 × 80 mm +// Mounting hole pattern : 86 × 58 mm (centre-to-centre), M2.5, Ø3.5 pad +// PCB thickness: 1.6 mm +// Connector side: -Y (USB-A, USB-C, HDMI, DP, GbE, SD on one long edge) +// Fan header & PWM header: +X short edge +// M.2 / NVMe: bottom face +// +// Print settings (PETG): +// tnut_base / standoff_post / side_brace / duct_shroud : 5 perimeters, 40 % gyroid, no supports +// cable_clip : 3 perimeters, 30 % gyroid, no supports +// +// Export commands: +// openscad -D 'RENDER="tnut_base"' -o tnut_base.stl jetson_orin_mount.scad +// openscad -D 'RENDER="standoff_post"' -o standoff_post.stl jetson_orin_mount.scad +// openscad -D 'RENDER="side_brace"' -o side_brace.stl jetson_orin_mount.scad +// openscad -D 'RENDER="duct_shroud"' -o duct_shroud.stl jetson_orin_mount.scad +// openscad -D 'RENDER="cable_clip"' -o cable_clip.stl jetson_orin_mount.scad +// openscad -D 'RENDER="assembly"' -o assembly.png jetson_orin_mount.scad +// ============================================================ + +// ── Render selector ───────────────────────────────────────── +RENDER = "assembly"; // tnut_base | standoff_post | side_brace | duct_shroud | cable_clip | assembly + +// ── Global constants ──────────────────────────────────────── +$fn = 64; +EPS = 0.01; + +// 2020 rail +RAIL_W = 20.0; +SLOT_NECK_H = 3.2; +TNUT_W = 9.8; +TNUT_H = 5.5; +TNUT_L = 12.0; +M5_D = 5.2; +M5_HEAD_D = 9.5; +M5_HEAD_H = 4.0; + +// Jetson Orin Nano carrier board +BOARD_L = 100.0; // board X +BOARD_W = 80.0; // board Y +BOARD_T = 1.6; // PCB thickness +MH_SX = 86.0; // mounting hole span X (centre-to-centre) +MH_SY = 58.0; // mounting hole span Y +M25_D = 2.7; // M2.5 clearance bore +M25_NUT_W = 5.0; // M2.5 hex nut across-flats +M25_NUT_H = 2.0; // M2.5 hex nut height +M25_HEAD_D = 5.0; // M2.5 SHCS head diameter +M25_HEAD_H = 2.5; + +// Base plate +BASE_L = 120.0; // length along X (covers board + overhang for braces) +BASE_W = 50.0; // width along Y (rail mount footprint) +BASE_T = 6.0; // plate thickness +BOLT_PITCH = 40.0; // M5 rail bolt pitch (per rail, 2 rails at Y=0 & Y=BASE_W) +M3_D = 3.2; +M3_HEAD_D = 6.0; +M3_HEAD_H = 3.0; + +// Standoff posts +POST_H = 12.0; // airflow gap + PCB seating (>= 10 mm clearance spec) +POST_OD = 8.0; // outer diameter +POST_BASE_D = 11.0; // flange diameter +POST_BASE_H = 3.0; // flange height +NUT_TRAP_H = M25_NUT_H + 0.3; +NUT_TRAP_W = M25_NUT_W + 0.4; + +// Side braces +BRACE_T = 5.0; // brace thickness (X) +BRACE_H = POST_H + POST_BASE_H + BOARD_T + 4.0; // full height +BRACE_W = BASE_W; // same width as base + +// Port-access cutouts (connector side -Y) +USB_CUT_W = 60.0; // wide cutout for USB-A stack + HDMI + DP +USB_CUT_H = 22.0; +GBE_CUT_W = 20.0; // GbE jack +GBE_CUT_H = 18.0; + +// Duct shroud +DUCT_T = 3.0; // wall thickness +DUCT_FLANGE = 6.0; // side tab width for M3 attachment +FAN_W = 40.0; // standard 40 mm blower clearance cutout +FAN_H = 10.0; // duct outlet height + +// Cable clip +CLIP_OD = 12.0; +CLIP_ID = 7.0; +CLIP_GAP = 7.5; +CLIP_W = 10.0; +SNAP_T = 1.8; + +// ── Utilities ─────────────────────────────────────────────── +module chamfer_cube(size, ch=1.0) { + hull() { + translate([ch, ch, 0]) cube([size[0]-2*ch, size[1]-2*ch, EPS]); + translate([0, 0, ch]) cube(size - [0, 0, ch]); + } +} + +module hex_pocket(af, depth) { + cylinder(d=af/cos(30), h=depth, $fn=6); +} + +// ── Part 1: tnut_base ─────────────────────────────────────── +module tnut_base() { + difference() { + union() { + chamfer_cube([BASE_L, BASE_W, BASE_T], ch=1.5); + + // Raised mounting bosses for M3 brace attachment (4 corners) + for (x = [8, BASE_L-8]) + for (y = [8, BASE_W-8]) + translate([x, y, BASE_T]) + cylinder(d=10, h=2.5); + } + + // T-nut pockets and M5 bolts — front rail (y = BASE_W/4) + for (x = [BASE_L/2 - BOLT_PITCH/2, BASE_L/2 + BOLT_PITCH/2]) { + translate([x, BASE_W/4, -EPS]) { + cylinder(d=M5_D, h=BASE_T + 2*EPS); + cylinder(d=M5_HEAD_D, h=M5_HEAD_H + EPS); + } + translate([x - TNUT_L/2, BASE_W/4 - TNUT_W/2, BASE_T - TNUT_H]) + cube([TNUT_L, TNUT_W, TNUT_H + EPS]); + } + + // T-nut pockets and M5 bolts — rear rail (y = 3*BASE_W/4) + for (x = [BASE_L/2 - BOLT_PITCH/2, BASE_L/2 + BOLT_PITCH/2]) { + translate([x, 3*BASE_W/4, -EPS]) { + cylinder(d=M5_D, h=BASE_T + 2*EPS); + cylinder(d=M5_HEAD_D, h=M5_HEAD_H + EPS); + } + translate([x - TNUT_L/2, 3*BASE_W/4 - TNUT_W/2, BASE_T - TNUT_H]) + cube([TNUT_L, TNUT_W, TNUT_H + EPS]); + } + + // M3 boss bolt holes (corner braces) + for (x = [8, BASE_L-8]) + for (y = [8, BASE_W-8]) + translate([x, y, -EPS]) + cylinder(d=M3_D, h=BASE_T + 2.5 + 2*EPS); + + // M3 boss counterbores (head from bottom) + for (x = [8, BASE_L-8]) + for (y = [8, BASE_W-8]) + translate([x, y, -EPS]) + cylinder(d=M3_HEAD_D, h=M3_HEAD_H + EPS); + + // Standoff post seating holes (board hole pattern, centred on plate) + bx0 = BASE_L/2 - MH_SX/2; + by0 = BASE_W/2 - MH_SY/2; + for (dx = [0, MH_SX]) + for (dy = [0, MH_SY]) + translate([bx0+dx, by0+dy, -EPS]) + cylinder(d=POST_BASE_D + 0.4, h=BASE_T + 2*EPS); + + // Weight relief grid (2 pockets) + translate([20, 12, -EPS]) cube([30, BASE_W-24, BASE_T/2]); + translate([BASE_L-50, 12, -EPS]) cube([30, BASE_W-24, BASE_T/2]); + + // Cable pass-through slot + translate([BASE_L/2 - 8, BASE_W/2 - 3, -EPS]) + cube([16, 6, BASE_T + 2*EPS]); + } +} + +// ── Part 2: standoff_post ─────────────────────────────────── +module standoff_post() { + difference() { + union() { + // Flange + cylinder(d=POST_BASE_D, h=POST_BASE_H); + // Post body + translate([0, 0, POST_BASE_H]) + cylinder(d=POST_OD, h=POST_H); + } + + // M2.5 through bore + translate([0, 0, -EPS]) + cylinder(d=M25_D, h=POST_BASE_H + POST_H + 2*EPS); + + // Captured hex nut trap (from top) + translate([0, 0, POST_BASE_H + POST_H - NUT_TRAP_H]) + hex_pocket(NUT_TRAP_W, NUT_TRAP_H + EPS); + + // Anti-rotation flat on nut pocket + translate([-M25_NUT_W/2 - 0.2, -POST_OD/2 - EPS, + POST_BASE_H + POST_H - NUT_TRAP_H]) + cube([M25_NUT_W + 0.4, 2.0, NUT_TRAP_H + EPS]); + } +} + +// ── Part 3: side_brace ────────────────────────────────────── +// Printed as +X face. Mirror for -X side. +module side_brace() { + difference() { + union() { + chamfer_cube([BRACE_T, BRACE_W, BRACE_H], ch=1.0); + + // Top lip to retain board edge + translate([0, 0, BRACE_H]) + cube([BRACE_T + 8.0, BRACE_W, 2.5]); + } + + // M3 bolt holes at base (attach to tnut_base bosses) + for (y = [8, BRACE_W-8]) + translate([-EPS, y, 4]) + rotate([0, 90, 0]) + cylinder(d=M3_D, h=BRACE_T + 2*EPS); + + // M3 counterbore from outer face + for (y = [8, BRACE_W-8]) + translate([-EPS, y, 4]) + rotate([0, 90, 0]) + cylinder(d=M3_HEAD_D, h=M3_HEAD_H + EPS); + + // Port-access cutout — USB/HDMI/DP cluster (centred on brace face) + translate([-EPS, BRACE_W/2 - USB_CUT_W/2, POST_BASE_H + 2.0]) + cube([BRACE_T + 2*EPS, USB_CUT_W, USB_CUT_H]); + + // GbE cutout (offset toward +Y) + translate([-EPS, BRACE_W/2 + USB_CUT_W/2 - GBE_CUT_W - 2, POST_BASE_H + 2.0]) + cube([BRACE_T + 2*EPS, GBE_CUT_W, GBE_CUT_H]); + + // M3 duct attachment holes (top edge) + for (y = [BRACE_W/4, 3*BRACE_W/4]) + translate([BRACE_T/2, y, BRACE_H - 2]) + cylinder(d=M3_D, h=10); + + // Ventilation slots (3 tall slots for airflow) + for (i = [0:2]) + translate([-EPS, + (BRACE_W - 3*8 - 2*4) / 2 + i*(8+4), + POST_BASE_H + USB_CUT_H + 6]) + cube([BRACE_T + 2*EPS, 8, BRACE_H - POST_BASE_H - USB_CUT_H - 10]); + } +} + +// ── Part 4: duct_shroud ───────────────────────────────────── +// Top cap that channels fan exhaust away from board; optional print. +module duct_shroud() { + duct_l = BASE_L - 2*BRACE_T - 1.0; // span between inner brace faces + duct_w = BRACE_W; + + difference() { + union() { + // Top plate + cube([duct_l, duct_w, DUCT_T]); + + // Front wall (fan inlet side) + translate([0, 0, -FAN_H]) + cube([DUCT_T, duct_w, FAN_H + DUCT_T]); + + // Rear wall (exhaust side — open centre) + translate([duct_l - DUCT_T, 0, -FAN_H]) + cube([DUCT_T, duct_w, FAN_H + DUCT_T]); + + // Side flanges for M3 attachment + translate([-DUCT_FLANGE, 0, -FAN_H]) + cube([DUCT_FLANGE, duct_w, FAN_H + DUCT_T]); + translate([duct_l, 0, -FAN_H]) + cube([DUCT_FLANGE, duct_w, FAN_H + DUCT_T]); + } + + // Fan cutout on top plate (centred) + translate([duct_l/2 - FAN_W/2, duct_w/2 - FAN_W/2, -EPS]) + cube([FAN_W, FAN_W, DUCT_T + 2*EPS]); + + // Fan screw holes (40 mm fan, Ø3.2 at 32 mm BC) + for (dx = [-16, 16]) + for (dy = [-16, 16]) + translate([duct_l/2 + dx, duct_w/2 + dy, -EPS]) + cylinder(d=M3_D, h=DUCT_T + 2*EPS); + + // Exhaust slot on rear wall (full width minus corners) + translate([duct_l - DUCT_T - EPS, 4, -FAN_H + 2]) + cube([DUCT_T + 2*EPS, duct_w - 8, FAN_H - 2]); + + // M3 flange attachment holes + for (y = [duct_w/4, 3*duct_w/4]) { + translate([-DUCT_FLANGE - EPS, y, -FAN_H/2]) + rotate([0, 90, 0]) + cylinder(d=M3_D, h=DUCT_FLANGE + 2*EPS); + translate([duct_l + DUCT_T - EPS, y, -FAN_H/2]) + rotate([0, 90, 0]) + cylinder(d=M3_D, h=DUCT_FLANGE + 2*EPS); + } + } +} + +// ── Part 5: cable_clip ────────────────────────────────────── +module cable_clip() { + difference() { + union() { + // Snap-wrap body + difference() { + cylinder(d=CLIP_OD + 2*SNAP_T, h=CLIP_W); + translate([0, 0, -EPS]) + cylinder(d=CLIP_ID, h=CLIP_W + 2*EPS); + // Front gap + translate([-CLIP_GAP/2, 0, -EPS]) + cube([CLIP_GAP, CLIP_OD, CLIP_W + 2*EPS]); + } + + // Mounting tab for brace edge + translate([CLIP_OD/2 + SNAP_T - EPS, -SNAP_T, 0]) + cube([8, SNAP_T*2, CLIP_W]); + } + + // Tab screw hole + translate([CLIP_OD/2 + SNAP_T + 4, 0, CLIP_W/2]) + rotate([90, 0, 0]) + cylinder(d=M3_D, h=SNAP_T*2 + 2*EPS, center=true); + } +} + +// ── Assembly ──────────────────────────────────────────────── +module assembly() { + // Base plate + color("SteelBlue") + tnut_base(); + + // Standoff posts (board hole pattern) + bx0 = BASE_L/2 - MH_SX/2; + by0 = BASE_W/2 - MH_SY/2; + for (dx = [0, MH_SX]) + for (dy = [0, MH_SY]) + color("DodgerBlue") + translate([bx0+dx, by0+dy, BASE_T]) + standoff_post(); + + // Side braces (left and right) + color("CornflowerBlue") + translate([0, 0, BASE_T]) + side_brace(); + color("CornflowerBlue") + translate([BASE_L, BRACE_W, BASE_T]) + mirror([1, 0, 0]) + mirror([0, 1, 0]) + side_brace(); + + // Board silhouette (translucent, for clearance visualisation) + color("ForestGreen", 0.25) + translate([BASE_L/2 - BOARD_L/2, BASE_W/2 - BOARD_W/2, + BASE_T + POST_BASE_H + POST_H]) + cube([BOARD_L, BOARD_W, BOARD_T]); + + // Duct shroud (above board) + color("LightSteelBlue", 0.7) + translate([BRACE_T + 0.5, 0, + BASE_T + POST_BASE_H + POST_H + BOARD_T + 2.0]) + duct_shroud(); + + // Cable clips (on brace edge, 2×) + for (y = [BRACE_W/3, 2*BRACE_W/3]) + color("SlateGray") + translate([BASE_L + 2, y, BASE_T + BRACE_H/2 - CLIP_W/2]) + rotate([0, 90, 0]) + cable_clip(); +} + +// ── Dispatch ──────────────────────────────────────────────── +if (RENDER == "tnut_base") tnut_base(); +else if (RENDER == "standoff_post") standoff_post(); +else if (RENDER == "side_brace") side_brace(); +else if (RENDER == "duct_shroud") duct_shroud(); +else if (RENDER == "cable_clip") cable_clip(); +else assembly(); -- 2.47.2