Files
remote-rig/hardware/case/camera-node-case-v4.scad
T

237 lines
10 KiB
OpenSCAD
Raw Normal View History

// RemoteRig camera node case v4
// Upright appliance-style OpenSCAD prototype for a strap-mounted camera node.
// 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.
//
// v4 visual direction: tall/upright appliance/control box matching the original
// reference image, replacing the rejected wide, low generic electronics box.
// Nominal body: 56 W x 36 D x 82 H mm; with low rear strap bridges ~40 D.
//
// Prototype assumptions to confirm against purchased parts:
// - 1.3 inch OLED module/window opening: 31 x 16 mm visible window, 37 x 22 mm panel recess.
// - 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 = 56;
// ----- Main enclosure parameters -----
case_w = 56; // upright appliance-style external width
case_d = 36; // depth for module stack + wiring clearance
case_h = 82; // tall vertical appliance-style height
wall = 2.2;
corner_r = 4.0;
front_recess_d = 2.0; // lid sits in this front pocket, nominally flush
lid_clearance = 0.35;
lid_w = case_w - 8; // nearly full-height/front-width inset panel
lid_h = case_h - 8;
lid_t = 2.0;
lid_lip_t = 1.2; // locating lip protrudes inside service opening
service_opening_w = lid_w - 10.0;
service_opening_h = lid_h - 16.0;
// 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_window_w = 31.0;
oled_window_h = 16.0;
oled_bezel_w = 37.0; // shallow recessed visual outline around window
oled_bezel_h = 22.0;
oled_z = 53.0; // upper third, clear of top screw counterbores
led_hole_d = 3.2; // 3 mm LED clearance
rocker_w = 13.0; // prototype cutout; measure purchased rocker
rocker_h = 19.0;
front_slot_w = 34.0; // two long rounded horizontal slots near lower front
front_slot_h = 3.2;
// Rear reusable cloth zip-tie / Velcro strap channels: low-profile external loops.
strap_bridge_w = case_w - 14;
strap_bridge_h = 6.5; // outer raised rail height
strap_slot_h = 4.2; // strap thickness opening; adjust for cloth ties
strap_gap_y = 2.4; // clearance behind bridge for strap material
strap_bridge_y = strap_gap_y + 2.4;
strap_z1 = 23.0;
strap_z2 = 50.0;
// Cable exit
cable_notch_w = 13.0;
cable_notch_h = 6.5;
// ----- 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 xz_rounded_prism(w, d, h, r=2) {
// Rounded rectangle in the visible X/Z plane, extruded through Y.
rotate([-90,0,0])
linear_extrude(height=d, center=true)
offset(r=r)
square([w-2*r, h-2*r], center=true);
}
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) {
// Low external rear belt-loop bridge for cloth ties/Velcro.
// The slot is entirely behind the rear wall so it does not expose the
// electronics cavity; a small overlap keeps the bridge connected to shell.
bridge_overlap_y = 0.8;
translate([0, case_d/2 + strap_bridge_y/2 - bridge_overlap_y, zc - strap_bridge_h/2])
union() {
difference() {
rounded_box([strap_bridge_w, strap_bridge_y, strap_bridge_h], r=1.8);
translate([0, -strap_bridge_y/2 + bridge_overlap_y + 0.05 + (strap_bridge_y + 1.0)/2,
(strap_bridge_h - strap_slot_h)/2])
rounded_box([strap_bridge_w - 9.0, strap_bridge_y + 1.0, strap_slot_h], r=1.1);
}
// Rear-wall weld pad: mechanically joins the external bridge to the
// shell while keeping the belt slot outside the electronics cavity.
translate([0, -strap_bridge_y/2 + bridge_overlap_y, strap_bridge_h/2])
cube([strap_bridge_w, 0.9, strap_bridge_h], center=true);
}
}
// ----- Printable body -----
module camera_node_body_v4() {
difference() {
union() {
difference() {
union() {
// Upright outer shell with softened appliance-like corners.
rounded_box([case_w, case_d, case_h], r=corner_r);
// Rear cloth zip-tie / Velcro strap bridges kept flat/quiet.
strap_bridge(strap_z1);
strap_bridge(strap_z2);
}
// Full-height front recessed lid pocket, like the green reference panel.
translate([0, -case_d/2 + front_recess_d/2, case_h/2])
cube([lid_w + lid_clearance, front_recess_d + 0.4, lid_h + lid_clearance], center=true);
// Through service opening behind the lid, leaving a strong inset frame.
service_depth = front_recess_d + wall + 2.0;
translate([0, -case_d/2 + service_depth/2, case_h/2])
xz_rounded_prism(service_opening_w, service_depth + 0.4, service_opening_h, r=2.0);
// 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 center cable exit / strain-relief notch.
translate([0, -case_d/2 + 2.0, -0.05])
xz_rounded_prism(cable_notch_w, 9.0, cable_notch_h, r=1.8);
}
// 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 + 5.0;
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.
screw_x = lid_w/2 - 5.0;
screw_z_low = (case_h - lid_h)/2 + 5.0;
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 full-height flush panel; restrained and not a busy slab.
rounded_box([lid_w, lid_t, lid_h], r=0.65);
// Rear locating lip fits inside the large service opening.
translate([0, lid_t/2 + lid_lip_t/2 - 0.2, lid_h/2])
xz_rounded_prism(service_opening_w - 0.8, lid_lip_t, service_opening_h - 0.8, r=1.5);
}
// OLED window and shallow black-bezel-style recess near the top.
translate([0, -lid_t/2 + 0.35, oled_z])
xz_rounded_prism(oled_bezel_w, 0.9, oled_bezel_h, r=1.3);
translate([0, -0.2, oled_z])
xz_rounded_prism(oled_window_w, lid_t + lid_lip_t + 0.9, oled_window_h, r=0.5);
// Subtle secondary indicators flanking the rocker, below the OLED bezel.
translate([-15.0, -0.2, 33.0]) y_cylinder(d=led_hole_d, h=lid_t + lid_lip_t + 1.0);
translate([ 15.0, -0.2, 33.0]) y_cylinder(d=led_hole_d, h=lid_t + lid_lip_t + 1.0);
// Small rocker lower on the panel, offset away from the OLED, screws, and slots.
translate([0, -0.2, 33.0])
xz_rounded_prism(rocker_w, lid_t + lid_lip_t + 1.0, rocker_h, r=0.8);
// Two long rounded horizontal slots near the lower front, matching the reference.
translate([0, -0.2, 17.0])
xz_rounded_prism(front_slot_w, lid_t + lid_lip_t + 1.0, front_slot_h, r=front_slot_h/2);
translate([0, -0.2, 11.0])
xz_rounded_prism(front_slot_w, lid_t + lid_lip_t + 1.0, front_slot_h, r=front_slot_h/2);
// Screw clearance/counterbore holes.
screw_x = lid_w/2 - 5.0;
screw_z_low = 5.0;
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, -lid_t/2 + 0.55, z]) y_cylinder(d=screw_head_d, h=1.3);
}
}
}
// ----- 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.03, (case_h - lid_h)/2])
color("gainsboro") camera_node_lid_v4();
// Dark OLED bezel/window cue for visual review only (not part of exported lid STL when rendering lid).
if (show_lid)
translate([0, -case_d/2 - 0.08, (case_h - lid_h)/2 + oled_z])
color("black") xz_rounded_prism(oled_bezel_w, 0.6, oled_bezel_h, r=1.3);
// Internal board volume guides (not printed): ESP32-C3 and ESP-01S envelopes.
color([0,0.45,0,0.35]) translate([-9, -1, 26]) cube([22.5, 18, 4], center=true);
color([0,0.2,0.8,0.35]) translate([9, -1, 45]) 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();
}