// RemoteRig camera node case v4 // Parametric OpenSCAD prototype for a status-panel camera node enclosure. // Units: millimeters. Coordinate system: X=width, Y=depth/front-back, Z=height. // Front/service lid is on the -Y face. Rear strap channels are on the +Y face. // // Prototype assumptions to confirm against purchased parts: // - 1.3 inch OLED module/window opening: 31 x 16 mm visible window, 35 x 19 mm panel clearance. // - Small rocker switch cutout: 13 x 19 mm rectangular snap-in opening. // - LEDs: two 3 mm panel LEDs (PWR + RGB STAT) with 3.2 mm holes. // - Boards: ESP32-C3 Super Mini 22.5 x 18 mm, ESP-01S 24.7 x 14.3 x 12 mm. $fn = 48; // ----- Main enclosure parameters ----- case_w = 68; // external width; room for OLED + LEDs + switch case_d = 42; // external depth; board stack + wiring clearance case_h = 32; // external height wall = 2.2; corner_r = 3.0; front_recess_d = 2.0; // lid sits in this front pocket, nominally flush lid_clearance = 0.35; lid_w = case_w - 8; lid_h = case_h - 6; lid_t = 2.0; lid_lip_t = 1.2; // locating lip protrudes inside service opening lid_lip_inset = 2.1; // Central body opening avoids the four corner screw bosses. Separate local // clearances below handle the OLED/switch bodies without cutting away bosses. service_opening_w = 46.0; service_opening_h = 11.5; // Hardware screw_d = 2.4; // M2 self-tapping / pilot; confirm hardware screw_head_d = 4.6; boss_d = 6.0; boss_len = 8.0; // Front panel components // OLED values are intentionally conservative prototype placeholders. oled_window_w = 31.0; oled_window_h = 16.0; oled_bezel_w = 35.0; // engraved/recessed visual outline around window oled_bezel_h = 19.0; led_hole_d = 3.2; // 3 mm LED clearance rocker_w = 13.0; // prototype cutout; measure purchased rocker rocker_h = 19.0; vent_slot_w = 7.0; vent_slot_h = 1.7; // Rear reusable cloth zip-tie / Velcro strap channels strap_bridge_w = case_w - 12; strap_bridge_h = 7.5; // outer raised tab height strap_slot_h = 4.2; // strap thickness opening; adjust for cloth ties strap_gap_y = 3.2; // clearance behind bridge for strap material strap_bridge_y = strap_gap_y + 2.2; strap_z1 = 8.5; strap_z2 = 23.5; // Cable exit cable_notch_w = 13.0; cable_notch_h = 6.0; // ----- Utility geometry ----- module rounded_box(size=[10,10,10], r=2, center_xy=true) { // Rounded in XY, straight in Z. linear_extrude(height=size[2]) offset(r=r) square([size[0]-2*r, size[1]-2*r], center=center_xy); } module y_cylinder(d, h, center=true) { rotate([90,0,0]) cylinder(d=d, h=h, center=center); } module screw_boss(x, z) { translate([x, -case_d/2 + front_recess_d + boss_len/2, z]) difference() { y_cylinder(d=boss_d, h=boss_len); y_cylinder(d=screw_d, h=boss_len + 0.8); } } module strap_bridge(zc) { // Raised rear bridge with a smooth-ish strap tunnel through it. // It intentionally overlaps the main shell by ~2.2 mm so the STL is a // single printable body, not separate coincident strap-tab volumes. translate([0, case_d/2 + strap_bridge_y/2 - 2.2, zc - strap_bridge_h/2]) difference() { rounded_box([strap_bridge_w, strap_bridge_y, strap_bridge_h], r=2.0); // Through-slot for the cloth tie. It cuts all the way through Y, // while leaving side posts that tie the upper/lower rails together. // Matching rear-wall cutouts are made in camera_node_body_v4(). translate([0, 0, (strap_bridge_h - strap_slot_h)/2]) rounded_box([strap_bridge_w - 8.0, strap_bridge_y + 0.8, strap_slot_h], r=1.2); } } // ----- Printable body ----- module camera_node_body_v4() { difference() { union() { difference() { union() { // Outer shell. translate([0,0,0]) rounded_box([case_w, case_d, case_h], r=corner_r); // Rear cloth zip-tie / Velcro strap bridges. strap_bridge(strap_z1); strap_bridge(strap_z2); } // Front recessed lid pocket (shallow, so the lid can sit flush). translate([0, -case_d/2 - 0.2, case_h/2]) cube([lid_w + lid_clearance, front_recess_d + 0.3, lid_h + lid_clearance], center=true); // Through service opening behind the lid, leaving a recessed ledge/frame. service_depth = front_recess_d + wall + 2.0; translate([0, -case_d/2 + service_depth/2, case_h/2]) cube([service_opening_w, service_depth + 0.4, service_opening_h], center=true); // Local front clearances for the OLED module and rocker-switch body. // These keep the screw-boss corner material intact while still leaving // room for front-panel components and wiring to enter the body cavity. translate([0, -case_d/2 + service_depth/2, (case_h - lid_h)/2 + lid_h - 9.5]) cube([oled_bezel_w + 2.0, service_depth + 0.4, oled_bezel_h + 3.0], center=true); translate([15.5, -case_d/2 + service_depth/2, (case_h - lid_h)/2 + 12.0]) cube([rocker_w + 3.0, service_depth + 0.4, rocker_h + 3.0], center=true); // Rear-wall pass-through slots for reusable cloth zip-ties / Velcro. // The slots align with the raised bridges, so straps can thread through // the back of the case instead of sitting in a closed decorative groove. for (zc=[strap_z1, strap_z2]) translate([0, case_d/2 + 1.0, zc]) cube([strap_bridge_w - 8.0, 8.0, strap_slot_h], center=true); // Interior electronics cavity: ESP32-C3 Super Mini + ESP-01S plus wiring/service clearance. cavity_d = case_d - front_recess_d - 2*wall; translate([0, -case_d/2 + front_recess_d + wall + cavity_d/2, case_h/2]) cube([case_w - 2*wall, cavity_d, case_h - 2*wall], center=true); // Bottom cable exit / strain-relief notch on lower front edge. translate([0, -case_d/2 + 2.0, -0.05]) cube([cable_notch_w, 9.0, cable_notch_h], center=true); } // Four protected screw bosses are added after shell hollowing so the // electronics cavity cannot cut away the receiving material. screw_x = lid_w/2 - 5.0; screw_z_low = (case_h - lid_h)/2 + 4.2; screw_z_high = case_h - screw_z_low; screw_boss(-screw_x, screw_z_low); screw_boss( screw_x, screw_z_low); screw_boss(-screw_x, screw_z_high); screw_boss( screw_x, screw_z_high); } // Final body-level pilot holes cut through the front frame into the // protected bosses. Without this pass the boss pilot holes can be // hidden behind solid frame material. screw_x = lid_w/2 - 5.0; screw_z_low = (case_h - lid_h)/2 + 4.2; screw_z_high = case_h - screw_z_low; for (x=[-screw_x, screw_x], z=[screw_z_low, screw_z_high]) translate([x, -case_d/2 + front_recess_d + boss_len/2, z]) y_cylinder(d=screw_d, h=boss_len + front_recess_d + 4.0); } } // ----- Printable front service lid / status panel ----- module camera_node_lid_v4() { difference() { union() { // Visible flush panel. translate([0,0,0]) rounded_box([lid_w, lid_t, lid_h], r=0.65); // Rear locating lip fits inside the central service opening and helps // panel alignment. Center it on the body's central opening so it // does not collide with lower vent geometry. lip_h = service_opening_h - 0.5; translate([0, lid_t/2 + lid_lip_t/2 - 0.2, (lid_h - lip_h)/2]) rounded_box([service_opening_w - 0.5, lid_lip_t, lip_h], r=0.45); // Shallow raised text pads/labels are omitted for print simplicity; add decals or paint-fill. } // OLED window and shallow module outline/recess. translate([0, -0.2, lid_h - 9.5]) cube([oled_window_w, lid_t + lid_lip_t + 0.8, oled_window_h], center=true); translate([0, -0.35, lid_h - 9.5]) cube([oled_bezel_w, 0.7, oled_bezel_h], center=true); // LED holes: separate power LED and single RGB status LED. translate([-17.0, -0.2, 11.5]) y_cylinder(d=led_hole_d, h=lid_t + lid_lip_t + 1.0); translate([-9.0, -0.2, 11.5]) y_cylinder(d=led_hole_d, h=lid_t + lid_lip_t + 1.0); // Rocker switch snap-in opening. translate([15.5, -0.2, 12.0]) cube([rocker_w, lid_t + lid_lip_t + 1.0, rocker_h], center=true); // Lower vent / style slots. for (i=[-2:2]) translate([i*9.5, -0.2, 4.8]) cube([vent_slot_w, lid_t + lid_lip_t + 1.0, vent_slot_h], center=true); // Screw clearance/counterbore holes. screw_x = lid_w/2 - 5.0; screw_z_low = 4.2; screw_z_high = lid_h - screw_z_low; for (x=[-screw_x, screw_x], z=[screw_z_low, screw_z_high]) { translate([x, -0.2, z]) y_cylinder(d=screw_d + 0.4, h=lid_t + lid_lip_t + 1.0); translate([x, -0.15, z]) y_cylinder(d=screw_head_d, h=1.2); } } } // ----- Non-print preview assembly ----- module camera_node_preview_v4(show_lid=true) { color("lightgray") camera_node_body_v4(); if (show_lid) translate([0, -case_d/2 - lid_t/2 - 0.2, (case_h - lid_h)/2]) color("gainsboro") camera_node_lid_v4(); // Internal board volume guides (not printed): ESP32-C3 and ESP-01S envelopes. color([0,0.45,0,0.35]) translate([-13, -1, 9]) cube([22.5, 18, 4], center=true); color([0,0.2,0.8,0.35]) translate([13, -1, 20]) cube([24.7, 14.3, 12], center=true); } // Select part to render from OpenSCAD CLI with: -D 'part="body"' part = "preview"; // "body", "lid", or "preview" if (part == "body") { camera_node_body_v4(); } else if (part == "lid") { camera_node_lid_v4(); } else { camera_node_preview_v4(); }