// ============================================================ // rover_electronics_bay.scad — SaltyRover Electronics Bay // Issue: #109 Agent: sl-mechanical Date: 2026-03-01 // ============================================================ // // Enclosed electronics housing sitting on the rover deck plate. // Houses: • Flight Controller (FC) — 30.5×30.5 mm M3 standoffs // • Jetson Orin NX / Nano — 58×49 mm M3 standoffs // • Battery access slot — left side slide-in // • RPLIDAR A1M8 tower — integrated on lid top // • Ventilation slots — all 4 walls + lid // // Shared mounting patterns (swappable with SaltyLab): // FC : 30.5 × 30.5 mm M3 (MAMBA F722S / Pixhawk) // Jetson: 58 × 49 mm M3 (Orin NX / Nano Devkit carrier) // // Coordinate: bay centred at origin; Z=0 = deck top face. // Bay body rests directly on deck top (no additional standoffs). // // Print: // Material : PETG (bay body + lid) // Settings : 4 perimeters, 30% gyroid infill // Orientation: open top face up for body; lid printed flat. // Note: Bay is too large to print as one piece on most 220mm beds. // Use RENDER="front_half" and RENDER="rear_half" for split, // joined with 3× M3 bolts and alignment pins. // // Export commands: // Bay body (full, for large-bed printers): // openscad rover_electronics_bay.scad -D 'RENDER="bay_stl"' -o rover_elec_bay.stl // Front half: // openscad rover_electronics_bay.scad -D 'RENDER="front_half"' -o rover_elec_bay_front.stl // Rear half: // openscad rover_electronics_bay.scad -D 'RENDER="rear_half"' -o rover_elec_bay_rear.stl // Lid (with RPLIDAR tower): // openscad rover_electronics_bay.scad -D 'RENDER="lid_stl"' -o rover_elec_bay_lid.stl // Assembly preview: // openscad rover_electronics_bay.scad -D 'RENDER="assembly"' // ============================================================ $fn = 64; e = 0.01; // ── Bay exterior dimensions ────────────────────────────────────────────────── BAY_L = 240.0; // length left-right (X in rover coords = Y here) BAY_W = 200.0; // width fore-aft (Y in rover = X here) BAY_H = 80.0; // interior height BAY_WALL = 3.0; // wall thickness (all sides) BAY_FLOOR = 4.0; // floor thickness (rests on deck) BAY_R = 8.0; // exterior corner radius // ── Ventilation slots ──────────────────────────────────────────────────────── VENT_W = 20.0; // slot width VENT_H = 6.0; // slot height VENT_PITCH = 28.0; // slot centre-to-centre pitch VENT_FROM_BOT = 12.0; // lowest vent row height above floor exterior // ── Lid ───────────────────────────────────────────────────────────────────── LID_T = 4.0; // lid plate thickness LID_RIM_H = 8.0; // lip that drops inside bay walls (retention) LID_RIM_GAP = 0.4; // clearance between lid rim and bay inner wall // ── FC mount — 30.5×30.5 mm M3 (shared SaltyLab) ──────────────────────────── FC_PITCH = 30.5; FC_HOLE_D = 3.2; FC_STANDOFF_H = 8.0; FC_STANDOFF_OD = 7.0; // FC positioned toward front-left inside bay (offset from centre) FC_OFF_X = -BAY_L/2 + 60.0; // left side (left = cable/ESC side) FC_OFF_Y = -BAY_W/2 + 50.0; // front side // ── Jetson Orin mount — 58×49 mm M3 (shared SaltyLab) ─────────────────────── ORIN_HOLE_X = 58.0; ORIN_HOLE_Y = 49.0; ORIN_HOLE_D = 3.2; ORIN_STANDOFF_H = 10.0; ORIN_STANDOFF_OD = 7.0; // Jetson positioned toward rear-right (toward robot rear, USB/HDMI accessible) ORIN_OFF_X = BAY_L/2 - 70.0; // right side ORIN_OFF_Y = BAY_W/2 - 55.0; // rear side // ── Battery access slot (left wall slide-in) ────────────────────────────────── // The under-deck battery tray is separate (rover_battery_tray.scad). // A slot in the bay left wall allows BMS cable + main power harness. BATT_SLOT_W = 30.0; // harness slot width BATT_SLOT_H = 20.0; // harness slot height BATT_SLOT_Z = 20.0; // slot bottom above floor interior // ── RPLIDAR A1M8 tower (on lid, top centre) ─────────────────────────────────── RPL_TOWER_OD = 28.0; // tower OD (hollow column) RPL_TOWER_ID = 16.0; // hollow core ID (cable routing) RPL_TOWER_H = 100.0; // tower height above lid top face // RPLIDAR A1M8 bolt circle: 58 mm dia, 4× M3 at 45°/135°/225°/315° RPL_BC = 58.0; RPL_HOLE_D = 3.3; // M3 clearance RPL_PLATFORM_D = 90.0; // platform disk at tower top // ── Bay-to-deck attachment ──────────────────────────────────────────────────── // 8× M3 SHCS through bay floor flanges into deck (matching saltyrover_chassis_r2.scad) DECK_BOLT_D = 3.3; DECK_BOLT_INSET = 8.0; // bolt CL from exterior corner // ── Lid retention (M3 corner bolts) ────────────────────────────────────────── LID_BOLT_D = 3.3; LID_BOLT_POS = 8.0; // bolt CL from exterior wall M3_D = 3.3; M4_D = 4.3; // ============================================================ // RENDER DISPATCH // ============================================================ RENDER = "assembly"; if (RENDER == "assembly") { assembly(); } else if (RENDER == "bay_stl") { bay_body(); } else if (RENDER == "front_half") { // Split along XZ plane (Y=0) — front half intersection() { bay_body(); translate([0, -BAY_W/2 - BAY_WALL, 0]) cube([BAY_L + 2*BAY_WALL + 2, BAY_W/2 + BAY_WALL + 1, BAY_H + BAY_FLOOR + LID_T + 2]); } } else if (RENDER == "rear_half") { // Split along XZ plane (Y=0) — rear half intersection() { bay_body(); translate([0, 0, 0]) cube([BAY_L + 2*BAY_WALL + 2, BAY_W/2 + BAY_WALL + 1, BAY_H + BAY_FLOOR + LID_T + 2]); } } else if (RENDER == "lid_stl") { bay_lid(); } // ============================================================ // ASSEMBLY PREVIEW // ============================================================ module assembly() { color("OliveDrab", 0.80) bay_body(); color("DarkOliveGreen", 0.70) translate([0, 0, BAY_FLOOR + BAY_H + 1]) bay_lid(); // FC standoffs + ghost board color("LightGray", 0.60) fc_standoffs(); %color("DarkGreen", 0.30) translate([FC_OFF_X, FC_OFF_Y, BAY_FLOOR + FC_STANDOFF_H]) cube([76, 42, 3], center = true); // Jetson standoffs + ghost board color("LightGray", 0.60) jetson_standoffs(); %color("DarkBlue", 0.25) translate([ORIN_OFF_X, ORIN_OFF_Y, BAY_FLOOR + ORIN_STANDOFF_H]) cube([100, 80, 5], center = true); } // ============================================================ // BAY BODY (open-top box with ventilation + mounts) // ============================================================ module bay_body() { outer_x = BAY_L + 2*BAY_WALL; outer_y = BAY_W + 2*BAY_WALL; outer_z = BAY_FLOOR + BAY_H; difference() { // ── Outer shell (rounded rectangle) ──────────────────────────── linear_extrude(outer_z) minkowski() { square([outer_x - 2*BAY_R, outer_y - 2*BAY_R], center = true); circle(r = BAY_R); } // ── Inner cavity ─────────────────────────────────────────────── translate([-BAY_L/2, -BAY_W/2, BAY_FLOOR]) cube([BAY_L, BAY_W, BAY_H + e]); // ── Ventilation slots — left wall (−X) ──────────────────────── for (i = [-2:2]) translate([-(BAY_L/2 + BAY_WALL + e), i * VENT_PITCH - VENT_W/2, VENT_FROM_BOT]) cube([BAY_WALL + 2*e, VENT_W, VENT_H]); // ── Ventilation slots — right wall (+X) ─────────────────────── for (i = [-2:2]) translate([BAY_L/2 - e, i * VENT_PITCH - VENT_W/2, VENT_FROM_BOT]) cube([BAY_WALL + 2*e, VENT_W, VENT_H]); // ── Ventilation slots — front wall (−Y) ─────────────────────── for (i = [-2:2]) translate([i * VENT_PITCH - VENT_W/2, -(BAY_W/2 + BAY_WALL + e), VENT_FROM_BOT]) cube([VENT_W, BAY_WALL + 2*e, VENT_H]); // ── Ventilation slots — rear wall (+Y) ──────────────────────── for (i = [-2:2]) translate([i * VENT_PITCH - VENT_W/2, BAY_W/2 - e, VENT_FROM_BOT]) cube([VENT_W, BAY_WALL + 2*e, VENT_H]); // ── Battery / harness slot (left wall) ──────────────────────── translate([-(BAY_L/2 + BAY_WALL + e), -BATT_SLOT_W/2, BAY_FLOOR + BATT_SLOT_Z]) cube([BAY_WALL + 2*e, BATT_SLOT_W, BATT_SLOT_H]); // ── FC mount holes through floor ────────────────────────────── for (dx = [-FC_PITCH/2, FC_PITCH/2]) for (dy = [-FC_PITCH/2, FC_PITCH/2]) translate([FC_OFF_X + dx, FC_OFF_Y + dy, -e]) cylinder(d = FC_HOLE_D, h = BAY_FLOOR + 2*e); // ── Jetson mount holes through floor ────────────────────────── for (dx = [-ORIN_HOLE_X/2, ORIN_HOLE_X/2]) for (dy = [-ORIN_HOLE_Y/2, ORIN_HOLE_Y/2]) translate([ORIN_OFF_X + dx, ORIN_OFF_Y + dy, -e]) cylinder(d = ORIN_HOLE_D, h = BAY_FLOOR + 2*e); // ── Bay-to-deck M3 bolt holes (8× corners, through floor flange) for (sx = [-1, 1]) for (sy = [-1, 1]) { bx = sx * (BAY_L/2 + BAY_WALL - DECK_BOLT_INSET); by = sy * (BAY_W/2 + BAY_WALL - DECK_BOLT_INSET); translate([bx, by, -e]) cylinder(d = DECK_BOLT_D, h = BAY_FLOOR + 2*e); } // Extra 2 bolts per long wall (centre) for (sy = [-1, 1]) translate([0, sy * (BAY_W/2 + BAY_WALL - DECK_BOLT_INSET), -e]) cylinder(d = DECK_BOLT_D, h = BAY_FLOOR + 2*e); // ── Lid retention M3 threaded bosses cut (4× top rim corners) ── for (sx = [-1, 1]) for (sy = [-1, 1]) { lx = sx * (BAY_L/2 + BAY_WALL - LID_BOLT_POS); ly = sy * (BAY_W/2 + BAY_WALL - LID_BOLT_POS); translate([lx, ly, outer_z - 12]) cylinder(d = LID_BOLT_D - 0.3, h = 14); // M3 self-tap bore } // ── Cable pass-through grommets slots (bottom, 2× for deck slots) for (sy = [-1, 1]) hull() { translate([-15, sy * (BAY_W/2 - 6), -e]) cylinder(d = 12, h = BAY_FLOOR + 2*e); translate([ 15, sy * (BAY_W/2 - 6), -e]) cylinder(d = 12, h = BAY_FLOOR + 2*e); } } // ── FC standoffs ───────────────────────────────────────────────────────── fc_standoffs(); // ── Jetson standoffs ───────────────────────────────────────────────────── jetson_standoffs(); } // ── FC standoffs (inside bay, above floor) ─────────────────────────────────── module fc_standoffs() { for (dx = [-FC_PITCH/2, FC_PITCH/2]) for (dy = [-FC_PITCH/2, FC_PITCH/2]) translate([FC_OFF_X + dx, FC_OFF_Y + dy, BAY_FLOOR]) difference() { cylinder(d = FC_STANDOFF_OD, h = FC_STANDOFF_H); // Threaded bore (M3 screw from above) translate([0, 0, FC_STANDOFF_H - 6]) cylinder(d = 2.5, h = 7); // M3 tap drill (Ø2.5) // Through clearance from floor (to match deck FC holes) cylinder(d = FC_HOLE_D, h = FC_STANDOFF_H - 6); } } // ── Jetson Orin standoffs (inside bay, above floor) ────────────────────────── module jetson_standoffs() { for (dx = [-ORIN_HOLE_X/2, ORIN_HOLE_X/2]) for (dy = [-ORIN_HOLE_Y/2, ORIN_HOLE_Y/2]) translate([ORIN_OFF_X + dx, ORIN_OFF_Y + dy, BAY_FLOOR]) difference() { cylinder(d = ORIN_STANDOFF_OD, h = ORIN_STANDOFF_H); // M3 tap bore (top 8mm) translate([0, 0, ORIN_STANDOFF_H - 8]) cylinder(d = 2.5, h = 9); // Clearance from floor cylinder(d = ORIN_HOLE_D, h = ORIN_STANDOFF_H - 8); } } // ============================================================ // BAY LID (with RPLIDAR A1M8 tower and ventilation) // ============================================================ // Lid drops over bay walls (retention lip) and is held with 4× M3 screws. // RPLIDAR A1M8 tower rises from lid centre. // Lid ventilation slots allow convective air circulation. // ============================================================ module bay_lid() { outer_x = BAY_L + 2*BAY_WALL; outer_y = BAY_W + 2*BAY_WALL; difference() { union() { // ── Lid plate ───────────────────────────────────────────── linear_extrude(LID_T) minkowski() { square([outer_x - 2*BAY_R, outer_y - 2*BAY_R], center = true); circle(r = BAY_R); } // ── Retention rim (drops inside bay walls) ───────────────── rim_x = BAY_L - 2*LID_RIM_GAP; rim_y = BAY_W - 2*LID_RIM_GAP; translate([0, 0, -LID_RIM_H + e]) linear_extrude(LID_RIM_H) difference() { minkowski() { square([rim_x - 2*BAY_R, rim_y - 2*BAY_R], center = true); circle(r = BAY_R); } // Hollow interior offset(r = -BAY_WALL) minkowski() { square([rim_x - 2*BAY_R, rim_y - 2*BAY_R], center = true); circle(r = BAY_R); } } // ── RPLIDAR tower (centred on lid) ───────────────────────── translate([0, 0, LID_T]) rplidar_tower(); } // ── Lid ventilation slots (3× rows, 5 slots each) ───────────── for (i = [-2:2]) { translate([i * VENT_PITCH - VENT_W/2, -outer_y/2 + 20, -e]) cube([VENT_W, outer_y - 40, LID_T + 2*e]); } // ── 4× M3 lid retention bolt holes ──────────────────────────── for (sx = [-1, 1]) for (sy = [-1, 1]) { lx = sx * (outer_x/2 - LID_BOLT_POS); ly = sy * (outer_y/2 - LID_BOLT_POS); translate([lx, ly, -e]) cylinder(d = M3_D, h = LID_T + 2*e); } } } // ── RPLIDAR A1M8 tower (on lid) ────────────────────────────────────────────── // Hollow column provides height above bay for RPLIDAR 360° scan clearance. // Anti-vibration ring (rplidar_mount.scad) sits atop the platform. // Tower height: 100 mm above lid = ~185 mm total above deck. module rplidar_tower() { difference() { union() { // Hollow column cylinder(d = RPL_TOWER_OD, h = RPL_TOWER_H); // Flared base (distributes load, improves print adhesion) cylinder(d = RPL_TOWER_OD + 16, h = 8); // Top platform disk translate([0, 0, RPL_TOWER_H]) cylinder(d = RPL_PLATFORM_D, h = 8); } // Hollow core (cable routing for RPLIDAR USB) translate([0, 0, -e]) cylinder(d = RPL_TOWER_ID, h = RPL_TOWER_H + 9); // 4× base-to-lid M3 attachment holes (through flared base) for (a = [0, 90, 180, 270]) rotate([0, 0, a]) translate([(RPL_TOWER_OD + 12) / 2, 0, -e]) cylinder(d = M3_D, h = 10); // RPLIDAR A1M8 mounting holes (4× M3, 58 mm BC, 45° offset) // Matches rplidar_mount.scad / sensor_head.scad RPL_BC pattern for (a = [45, 135, 225, 315]) translate([RPL_BC/2 * cos(a), RPL_BC/2 * sin(a), RPL_TOWER_H - e]) cylinder(d = RPL_HOLE_D, h = 10); // Rotation alignment slot (sets RPLIDAR scan start angle) translate([RPL_BC/2 - 3, -2, RPL_TOWER_H - e]) cube([8, 4, 10]); } }