Add camera node case v4 status panel CAD #11

Merged
overseer merged 8 commits from agent/hermes/camera-node-case-v4-status-panel into dev 2026-05-23 11:31:40 -04:00
9 changed files with 80712 additions and 81 deletions
Showing only changes of commit 8c8d4e45e5 - Show all commits
+105 -15
View File
@@ -2,30 +2,116 @@
> Living queue for 3D-printed / physical hardware design work. > Living queue for 3D-printed / physical hardware design work.
## Active / Ready for prototype print ## Active / Ready for CAD prototype
### Camera node case v4 — status panel + strap mount
**Status:** Parametric OpenSCAD source created; body/lid/preview STLs exported and validated watertight. Ready for CAD review, exact part measurement, and first prototype print.
**Files:**
- `hardware/case/camera-node-case-v4.scad`
- `hardware/case/camera-node-case-v4-body.scad`
- `hardware/case/camera-node-case-v4-lid.scad`
- `hardware/case/camera-node-case-v4-preview.scad`
- `hardware/case/camera-node-case-v4-body.stl`
- `hardware/case/camera-node-case-v4-lid.stl`
- `hardware/case/camera-node-case-v4-preview.stl`
**Design direction:**
- Stand-mounted camera node enclosure; the case still does **not** mount to the GoPro.
- Primary mounting is now reusable cloth zip ties / Velcro straps through rear case channels, not a clamp/dovetail.
- Front has a recessed/flush service lid similar to a field-service status panel.
- Lid includes cutouts for:
- 1.3-inch OLED/status screen.
- separate 3 mm power LED.
- single 3 mm RGB status LED replacing red/green status LEDs.
- small rocker on/off switch.
- lower vent/style slots.
- Body includes screw bosses, recessed lid pocket, lid locating geometry, bottom cable exit/strain-relief notch, and two rear horizontal strap channels to resist rotation on a stand.
- Internal envelope is sized for known module dimensions plus service clearance:
- ESP32-C3 Super Mini: 22.5 × 18 mm.
- ESP-01S: ~24.7 × 14.3 × 12 mm.
**Prototype display content target:**
```text
CAM 03 REC ●
BAT 87% LINK OK
00:12:34
```
**Prototype dimensions to validate before production:**
- Exact 1.3-inch OLED module dimensions:
- PCB width/height/thickness.
- active display/window width/height.
- connector side and ribbon/header clearance.
- mounting-hole positions, if using module screws or adhesive tape.
- Rocker switch:
- snap-in cutout width/height.
- bezel/flange size.
- required panel thickness range.
- rear depth and terminal clearance.
- LEDs:
- preferred holder/bezel style, if any.
- final hole diameter for 3 mm PWR LED and 3 mm RGB STAT LED.
- current-limiting resistor placement.
- Wiring/service:
- USB cable diameter and bend radius.
- whether power enters through bottom notch, side notch, or panel connector.
- actual regulator/power distribution board footprint if used.
- Fasteners:
- M2 vs M2.5 vs self-tapping screws for lid.
- pilot diameter, screw length, and head/counterbore diameter.
- Mounting straps:
- cloth zip-tie / Velcro width and thickness.
- whether two strap paths are enough to prevent case rotation on the expected stand diameter.
- whether rear bridge edges need larger radii or TPU/silicone sleeve protection.
- Printability:
- rear strap bridge bridging/support behavior.
- body/lid fit after PETG shrinkage.
- lid lip clearance and screw boss robustness.
- cable notch strength and strain relief.
**Suggested OpenSCAD validation/export commands:**
```bash
openscad -o /tmp/camera-node-case-v4-body.stl hardware/case/camera-node-case-v4-body.scad
openscad -o /tmp/camera-node-case-v4-lid.stl hardware/case/camera-node-case-v4-lid.scad
openscad -o /tmp/camera-node-case-v4-preview.stl hardware/case/camera-node-case-v4-preview.scad
```
Latest validation: OpenSCAD reports `Simple: yes`; trimesh confirms body, lid, and preview STLs are watertight. Body and lid each export as a single connected printable component; preview includes separate non-print board guide volumes by design.
Or with the main parametric selector:
```bash
openscad -D 'part="body"' -o /tmp/camera-node-case-v4-body.stl hardware/case/camera-node-case-v4.scad
openscad -D 'part="lid"' -o /tmp/camera-node-case-v4-lid.stl hardware/case/camera-node-case-v4.scad
openscad -D 'part="preview"' -o /tmp/camera-node-case-v4-preview.stl hardware/case/camera-node-case-v4.scad
```
## Prior prototype reference
### Tripod electronics case v3 ### Tripod electronics case v3
**Status:** STL generated and validated watertight. **Status:** Historical design reference. In this checkout, previous v3 SCAD/STL files are not present; v4 starts a new `hardware/case/` CAD source set.
**Files:** **Previous design notes:**
- `hardware/case/tripod-case-v3.scad`
- `hardware/case/case-body-v3.stl`
- `hardware/case/case-lid-v3.stl`
- `hardware/case/tripod-clamp-v3.stl`
- `hardware/case/full-case-preview-v3.stl`
**Design notes:** - Held ESP32 + ESP8266 stack.
- Holds ESP32 + ESP8266 stack.
- Screw-on lid with vent slots. - Screw-on lid with vent slots.
- Rear dovetail-style rail/socket interface. - Rear dovetail-style rail/socket interface.
- Separate screw-tightened tripod clamp sized around a 35 mm stand/pole. - Separate screw-tightened tripod clamp sized around a 35 mm stand/pole.
- Clamp uses M3 hardware: one M3 screw across the clamp mouth, with an M3 nut trap. - Clamp used M3 hardware: one M3 screw across the clamp mouth, with an M3 nut trap.
**Prototype questions:** **Reasons superseded by v4:**
- Does the clamp close enough on smaller tripod legs, or do we need swappable inserts?
- Does the dovetail hold under vibration without a retention screw? - User requested front status/service panel with OLED, LEDs, and rocker switch.
- Are USB/LED/UART cutouts in the correct orientation for the actual boards? - Single RGB status LED replaces separate red/green status LEDs.
- Strap channels are simpler and more adaptable than a dedicated clamp/dovetail for field stands.
## Backlog ## Backlog
@@ -36,6 +122,7 @@
**Goal:** A printable enclosure for the RemoteRig hub/control panel using a 10.1-inch touchscreen and Raspberry Pi Zero / Zero 2 W. **Goal:** A printable enclosure for the RemoteRig hub/control panel using a 10.1-inch touchscreen and Raspberry Pi Zero / Zero 2 W.
**Display target:** **Display target:**
- Vendor/model: HZWDONE Raspberry Pi Screen 10.1" Touchscreen - Vendor/model: HZWDONE Raspberry Pi Screen 10.1" Touchscreen
- Resolution: 1024×600 - Resolution: 1024×600
- Interface: HDMI portable monitor - Interface: HDMI portable monitor
@@ -43,12 +130,14 @@
- Compatibility listing: Raspberry Pi 5/4/3B/B+ and Windows 11/10/8 - Compatibility listing: Raspberry Pi 5/4/3B/B+ and Windows 11/10/8
**Initial assumptions to validate:** **Initial assumptions to validate:**
- Compute: Raspberry Pi Zero / Zero 2 W mounted behind or below the display. - Compute: Raspberry Pi Zero / Zero 2 W mounted behind or below the display.
- Use case: RemoteRig local monitor/control panel at field recording setup. - Use case: RemoteRig local monitor/control panel at field recording setup.
- Likely needs: front bezel, rear electronics cavity, Pi mounting posts, HDMI/USB/power cable exits, strain relief, ventilation, and optional tripod/stand mounting. - Likely needs: front bezel, rear electronics cavity, Pi mounting posts, HDMI/USB/power cable exits, strain relief, ventilation, and optional tripod/stand mounting.
- Because this is a 10.1" panel, design should prioritize rigidity: thicker bezel ribs, rear standoffs, and possibly a two-piece shell instead of a small snap case. - Because this is a 10.1" panel, design should prioritize rigidity: thicker bezel ribs, rear standoffs, and possibly a two-piece shell instead of a small snap case.
**Required measurements before CAD:** **Required measurements before CAD:**
- Product link or datasheet for the exact HZWDONE 10.1" variant. - Product link or datasheet for the exact HZWDONE 10.1" variant.
- Screen/PCB outer dimensions: width, height, thickness. - Screen/PCB outer dimensions: width, height, thickness.
- Active display opening dimensions. - Active display opening dimensions.
@@ -60,6 +149,7 @@
- Mounting preference: desktop kickstand, tripod clamp, VESA-style holes, handle, or combination. - Mounting preference: desktop kickstand, tripod clamp, VESA-style holes, handle, or combination.
**Proposed design approach:** **Proposed design approach:**
1. Create `hardware/display-case/`. 1. Create `hardware/display-case/`.
2. Build a parametric OpenSCAD model with measured display/Pi dimensions. 2. Build a parametric OpenSCAD model with measured display/Pi dimensions.
3. Split into printable parts: front bezel, rear shell, Pi/controller tray, optional stand/tripod mount. 3. Split into printable parts: front bezel, rear shell, Pi/controller tray, optional stand/tripod mount.
+125 -59
View File
@@ -1,89 +1,147 @@
# RemoteRig — Camera Node Hardware Design # RemoteRig — Camera Node Hardware Design
> **Version:** 0.2.0 | **Status:** Draft > **Version:** 0.3.0 | **Status:** v4 CAD prototype ready for measurement/print validation
> **Target:** GoPro Hero 3 Black/Silver + ESP8266 + ESP32 + USB power bank > **Target:** GoPro Hero 3 Black/Silver + ESP32-C3 Super Mini + ESP-01S + USB power bank
## Overview ## Overview
Each camera node is two ESP boards in a small case that clips to the tripod/stand. The case **does not attach to the camera** — only to the stand. Powered by a standard USB power bank. Each camera node is two ESP boards in a small stand-mounted case. The case **does not attach to the camera**; it straps to a tripod/lighting stand with reusable cloth zip ties / Velcro straps. Powered by a standard USB power bank.
``` ```
┌─────────────────┐ ┌─────────────────┐
│ USB Power Bank │── USB ──→ GoPro (power only) │ USB Power Bank │── USB ──→ GoPro (power only)
│ (off-the-shelf)│── USB ──→ ESP32 + ESP8266 (shared) │ (off-the-shelf)│── USB ──→ Camera Node Case
└─────────────────┘ └─────────────────┘
┌────────┴────────┐ ┌────────┴────────────────────────────
Tripod Case │ ← clips to stand leg Camera Node Case v4 │ ← Velcro/cloth straps to stand
│ ┌──────────── │ ┌──────────────────────────────┐
│ │ ESP8266 ← Wi-Fi → GoPro AP (10.5.5.1) │ │ Flush/recessed service lid
│ │ (camera) │ │ │ │ 1.3 OLED: CAM/REC/BAT/LINK
├────────────┤ │ ← UART between boards │ PWR LED + RGB STAT LED │ │
│ │ ESP32 │ │ ← Wi-Fi → Travel Router │ │ Small rocker power switch │ │
│ (MQTT) └──────────────────────────────┘
└────────────┘ ESP-01S camera bridge ↔ ESP32-C3
└─────────────────┘ └─────────────────────────────────────
``` ```
## Bill of Materials ## Bill of Materials
| Item | Qty | Cost | Notes | | Item | Qty | Cost | Notes |
|------|-----|------|-------| |------|-----|------|-------|
| ESP32 Dev Board | 1 | ~$5 | MQTT bridge — talks to hub | | ESP32-C3 Super Mini | 1 | ~$4$6 | MQTT / hub-side bridge; known board envelope 22.5 × 18 mm |
| ESP8266 D1 Mini | 1 | ~$3 | Camera bridge — talks to GoPro | | ESP-01S / ESP8266 module | 1 | ~$2$3 | Camera-side GoPro Wi-Fi bridge; known envelope ~24.7 × 14.3 × 12 mm |
| USB power bank (5000mAh+) | 1 | ~$10 | Powers both boards + GoPro | | 1.3-inch OLED/status screen | 1 | ~$4$8 | Prototype CAD assumes ~31 × 16 mm visible window / ~35 × 19 mm panel clearance; confirm exact module |
| Micro-USB cable (short) | 2 | ~$2 | Power bank → boards + GoPro | | 3 mm power LED | 1 | <$1 | Separate always-power/5V indicator |
| Jumper wires F-F | 3 | ~$0.25 | UART TX/RX/GND between boards | | 3 mm RGB status LED | 1 | <$1 | Replaces separate red/green status LEDs; firmware can map node states to color |
| PETG filament | ~25g | ~$0.50 | 3D printed case | | Small rocker switch | 1 | ~$1$3 | On/off switch; prototype CAD assumes 13 × 19 mm snap-in opening |
| Velcro strap (small) | 1 | ~$0.25 | Secure power bank to stand | | USB power bank (5000 mAh+) | 1 | ~$10 | Powers camera node and GoPro |
| Short USB cables / wiring | as needed | ~$2$5 | Power bank → node + GoPro; internal power/signal wiring |
| M2 or small self-tapping screws | 4 | <$1 | Front service lid screws; pilot holes are parametric |
| PETG filament | ~3550 g | ~$1 | 3D printed case body + lid |
| Reusable cloth zip ties / Velcro straps | 2 | ~$1 | Primary stand mount through rear strap channels |
**Total per node:** ~$21 (+ GoPro already owned) **Total per node:** roughly ~$25$35 plus GoPro and power bank, depending on display/switch choice.
## 3D Printed Case ## 3D Printed Case
**Current source:** `hardware/case/tripod-case-v3.scad` **Current source:** `hardware/case/camera-node-case-v4.scad`
**Pipeline:** `hardware/DESIGN_PIPELINE.md` **Pipeline:** `hardware/DESIGN_PIPELINE.md`
Four exported prototype files: The v4 case replaces the v3 clamp/dovetail concept with a simpler strap-mounted field enclosure:
1. **Case body** — holds both boards stacked, cable ports, rear dovetail-style receiver
2. **Case lid** — screw-on cover with ventilation 1. **Case body** — shell sized around ESP32-C3 Super Mini + ESP-01S with service/wiring clearance.
3. **Tripod clamp** — separate screw-tightened C-clamp sized around a 35mm stand/pole 2. **Flush/recessed front service lid** — screw-on front status panel with locating lip.
4. **Full preview** — combined visualization STL only, not intended as the print job 3. **Front panel controls/indicators**:
- 1.3-inch OLED/status screen window.
- 3 mm **PWR** LED.
- single 3 mm **RGB STAT** LED for state-dependent colors.
- small rectangular rocker switch cutout.
- lower vent/style slots.
4. **Rear strap channels** — two raised horizontal cloth zip-tie / Velcro strap paths to reduce rotation on a tripod/stand.
5. **Bottom cable exit** — cable/strain-relief notch for USB/power wiring.
### Export wrappers
Simple per-part OpenSCAD wrappers are included:
- `hardware/case/camera-node-case-v4-body.scad`
- `hardware/case/camera-node-case-v4-lid.scad`
- `hardware/case/camera-node-case-v4-preview.scad`
Example CLI exports, if OpenSCAD is installed:
```bash
openscad -o hardware/case/camera-node-case-v4-body.stl hardware/case/camera-node-case-v4-body.scad
openscad -o hardware/case/camera-node-case-v4-lid.stl hardware/case/camera-node-case-v4-lid.scad
openscad -o hardware/case/camera-node-case-v4-preview.stl hardware/case/camera-node-case-v4-preview.scad
```
Or render the main file directly:
```bash
openscad -D 'part="body"' -o hardware/case/camera-node-case-v4-body.stl hardware/case/camera-node-case-v4.scad
openscad -D 'part="lid"' -o hardware/case/camera-node-case-v4-lid.stl hardware/case/camera-node-case-v4.scad
openscad -D 'part="preview"' -o hardware/case/camera-node-case-v4-preview.stl hardware/case/camera-node-case-v4.scad
```
### Print Settings ### Print Settings
- **Material:** PETG preferred for heat/outdoor use and clamp flex
- **Layer:** 0.2mm | **Infill:** 20% gyroid minimum; 35%+ recommended for clamp - **Material:** PETG preferred for heat/outdoor use and strap-tab durability.
- **Supports:** Likely yes for clamp ears / dovetail overhangs depending on slicer orientation - **Layer:** 0.2 mm typical.
- **Post-processing:** M3x8mm screws for lid (4x), one M3 screw + M3 nut for clamp tightening - **Infill:** 20% gyroid minimum; 30%+ recommended around rear strap bridges.
- **Supports:** likely minimal/none depending on orientation; verify rear strap bridge spans and cable notch in slicer.
- **Post-processing:** fit 4 lid screws; deburr OLED/LED/switch cutouts; soften strap-channel edges if the printed radius is too sharp for cloth ties.
## Expected Status Screen Content
Preferred 1.3-inch OLED layout/content style:
```text
CAM 03 REC ●
BAT 87% LINK OK
00:12:34
```
Suggested fields:
- `CAM` / node ID.
- `REC` state with a clear recording indicator.
- Battery percentage or supply estimate.
- `LINK OK` / degraded / disconnected state.
- Recording/session timer.
## Wiring ## Wiring
``` ```text
USB Power Bank USB Power Bank
├── USB-A → Micro-USB cable → ESP32 USB port ├── USB-A → Camera Node Case
(powers ESP32, shared 5V rail) ├── rocker switch → node power rail
│ ├── PWR LED indicator
│ ├── ESP32-C3 Super Mini
│ ├── ESP-01S / ESP8266
│ ├── 1.3-inch OLED display
│ └── RGB status LED
── USB-A → Micro-USB cable → GoPro USB port ── USB-A → GoPro USB port
(power only — no data) (power only — no data)
└── (ESP8266 powered via ESP32 3.3V pin, or via shared USB)
UART (inside case): UART / control inside case:
ESP8266 TX (GPIO1) ──→ ESP32 RX (GPIO16) ESP-01S TX ──→ ESP32-C3 RX
ESP8266 RX (GPIO3) ←── ESP32 TX (GPIO17) ESP-01S RX ←── ESP32-C3 TX
ESP8266 GND ─────────── ESP32 GND ESP-01S GND ─── ESP32-C3 GND
``` ```
**Power note:** Both boards can be powered from a single USB cable if the ESP32's VIN/5V pin is bridged to the ESP8266's VIN. Alternatively, use a USB Y-splitter cable. **Power note:** exact wiring depends on the regulator/power board used. Confirm OLED voltage, LED current limiting, and whether the rocker switches USB 5 V input or a regulated node rail.
## Wi-Fi Topology ## Wi-Fi Topology
``` ```text
GoPro Hero 3 ──(AP @ 10.5.5.1)──→ ESP8266 (camera bridge) GoPro Hero 3 ──(AP @ 10.5.5.1)──→ ESP-01S / ESP8266 camera bridge
UART │ (inside case) UART │ (inside case)
Travel Router ──(AP)─────────────────→ ESP32 (MQTT bridge) Travel Router ──(AP)────────────────────→ ESP32-C3 MQTT bridge
(10.60.1.1) │ (10.60.1.1) │
MQTT │ MQTT │
@@ -91,22 +149,30 @@ Travel Router ──(AP)─────────────────→ E
Pi Hub (10.60.1.56) Pi Hub (10.60.1.56)
``` ```
The ESP8266 and GoPro talk over Wi-Fi**no data cable between them**. The only cable to the GoPro is USB power from the battery pack. The ESP8266/ESP-01S and GoPro talk over Wi-Fi. The only cable to the GoPro is USB power from the battery pack.
## Field Setup ## Field Setup
1. **Mount GoPro** on tripod/stand 1. Mount GoPro on tripod/stand.
2. **Clip case** to tripod leg 2. Route two reusable cloth zip ties / Velcro straps through the rear v4 case channels.
3. **Connect power bank** via USB to case + GoPro 3. Strap the case to a tripod/stand leg; use both strap paths to resist rotation.
4. **Power on** — ESP32 auto-connects to travel router, ESP8266 auto-connects to GoPro 4. Connect power bank to case and GoPro.
5. **Monitor** from `http://10.60.1.56:8080` 5. Toggle rocker switch on.
6. Verify PWR LED, RGB status LED, and OLED status: camera ID, REC state, battery, link, timer.
7. Monitor from `http://10.60.1.56:8080`.
## Case Dimensions ## Case Dimensions
| | W × D × H (mm) | Prototype v4 nominal CAD dimensions:
| Part / feature | W × D × H (mm) |
|---|---| |---|---|
| Case body external | ~56.8 × 38.2 × 19.0 | | Case shell external | ~68 × 42 × 32 |
| Lid external | ~56.8 × 32.8 × 4.0 | | Case with rear strap bridges | ~68 × 45.2 × 32 |
| Tripod clamp | ~43.0 × 56.9 × 16.0 | | Front recessed lid | visible panel ~60 × 2 × 26; total with locating lip ~60 × 3 × 26 |
| Clamp pole fit | Nominal 35mm; smaller poles TBD / may need inserts | | OLED visible window assumption | ~31 × 16 |
| Total weight | TBD after prototype print | | Rocker cutout assumption | ~13 × 19 |
| Rear strap channels | two horizontal pass-through paths, ~48 mm usable slot width, ~4.2 mm strap-thickness clearance |
| Board clearance targets | ESP32-C3 22.5 × 18 mm + ESP-01S 24.7 × 14.3 × 12 mm plus wiring/service clearance |
These dimensions are placeholders for the first CAD prototype. Measure the actual OLED module, rocker switch, LEDs, screws, USB cable bend radius, and strap width/thickness before committing to production prints.
@@ -0,0 +1,4 @@
// Export wrapper for RemoteRig camera node case v4 body.
use <camera-node-case-v4.scad>
camera_node_body_v4();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
// Export wrapper for RemoteRig camera node case v4 front service lid/status panel.
use <camera-node-case-v4.scad>
camera_node_lid_v4();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
// Export wrapper for RemoteRig camera node case v4 assembly preview.
use <camera-node-case-v4.scad>
camera_node_preview_v4();
File diff suppressed because it is too large Load Diff
+237
View File
@@ -0,0 +1,237 @@
// 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 - 0.25, (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();
}