// ============================================================ // RPi5 Industrial Enclosure — Luckfox DHX-10.1" Touchscreen // Version: 003 // Fixes vs 002: // 1. Kickstand completely redesigned — shorter, thinner, clearly // attached to rear cover bottom, triangular gussets on each side // 2. GPIO top-wall cutout removed (GPIO is fully internal; access // requires removing the rear cover, correct for industrial use) // 3. Pi cavity depth verified and annotated // 4. Bezel two-piece connection: corner bosses on rear cover + // matching through-holes on bezel — intentional removable joint // ============================================================ // ── SCREEN PARAMETERS ─────────────────────────────────────── scr_w = 236; // screen outer width (mm) scr_h = 144; // screen outer height (mm) scr_d = 19; // screen outer depth (mm) scr_active_w = 222; // active area width (mm) ← confirm with screen datasheet scr_active_h = 130; // active area height (mm) ← confirm with screen datasheet scr_mount_x = 75; // screen rear M2.5 hole pattern X (mm) ← verify scr_mount_y = 75; // screen rear M2.5 hole pattern Y (mm) ← verify // ── RASPBERRY PI 5 PARAMETERS ──────────────────────────────── pi_w = 85; // Pi board width (mm) pi_h = 56; // Pi board height (mm) pi_d = 17; // Pi board + tallest component height (mm) pi_mnt_x = 58; // Pi mount hole pattern X (mm) pi_mnt_y = 49; // Pi mount hole pattern Y (mm) pi_standoff = 5; // standoff height: screen rear face → Pi PCB (mm) pi_offset_x = 0; // Pi centre X offset from screen centre (mm) pi_offset_y = 5; // Pi centre Y offset upward from screen centre (mm) // ── ENCLOSURE PARAMETERS ───────────────────────────────────── wall = 2.5; // wall thickness throughout (mm) chamfer = 1.5; // external edge chamfer size (mm) recess = 1.0; // screen recess depth in front bezel (mm) gap = 0.3; // bezel ↔ rear cover fit clearance (mm) // ── VENT PARAMETERS ────────────────────────────────────────── vent_w = 3; // vent slot width (mm) vent_l = 20; // vent slot length (mm) vent_sp = 4; // slot gap edge-to-edge (mm) soc_vent_sz = 30; // SoC direct-vent zone size (mm, square) // ── CABLE GLAND PARAMETERS ─────────────────────────────────── gland_count = 2; // number of M16 cable glands gland_dia = 16.5; // M16 clearance hole diameter (mm) gland_spacing= 40; // centre-to-centre spacing (mm) // ── KICKSTAND PARAMETERS ───────────────────────────────────── // The kickstand is a flat plate + two triangular gussets, integral // with the rear cover bottom. When the unit stands on the kickstand // the plate lies flat on the desk and the screen tilts back // (90 - ks_tilt) degrees from vertical. ks_tilt = 75; // screen angle from horizontal when standing (deg) // 75° from horiz = 15° lean-back from vertical ks_depth = 55; // plate reach behind rear face (mm) — shorter than 002 ks_width = 180; // plate span across enclosure width (mm) ks_thick = 5; // plate thickness (mm) ks_gusset_h = 30; // gusset height up the rear cover face (mm) // ── ASSEMBLY PARAMETERS ────────────────────────────────────── m3_dia = 3.4; // M3 clearance hole (mm) insert_dia = 4.2; // M3 heat-set insert OD (mm) insert_h = 6; // heat-set insert depth (mm) boss_od = insert_dia + 3.5; // insert boss outer diameter (mm) corner_inset = wall + boss_od/2 + 1; // corner boss/hole X and Y inset (mm) // ── DERIVED DIMENSIONS ─────────────────────────────────────── // // Rear cavity depth check: // pi_standoff (5) + pi_d (17) + cable headroom (10) = 32 mm → rear_d // rear_d is the full depth of the rear cover cavity. // The screen body (scr_d=19 mm) is NOT included in rear_d; // the rear cover encloses only the space BEHIND the screen rear face. // rear_d = pi_standoff + pi_d + 10; // = 32 mm, Pi fits with 10 mm to spare enc_w = scr_w + 2*wall; // enclosure outer width (241 mm) enc_h = scr_h + 2*wall; // enclosure outer height (149 mm) enc_d = rear_d + wall; // rear cover total depth ( 34.5 mm) // Pi centre in enclosure XY coordinates (wall-offset from screen centre) pi_enc_cx = wall + scr_w/2 + pi_offset_x; // 120.5 mm with defaults pi_enc_cy = wall + scr_h/2 + pi_offset_y; // 79.5 mm with defaults // Z of Pi PCB surface, measured from rear cover rear face pi_z = wall + pi_standoff; // 7.5 mm with defaults // Kickstand tip drop: how far below Y=0 the far edge must sit so the // bottom surface is horizontal when the unit tilts to ks_tilt from horizontal ks_drop = ks_depth * tan(90 - ks_tilt); // ≈ 14.7 mm for ks_tilt=75 $fn = 48; // ============================================================ // PRIMITIVES // ============================================================ // Chamfered rectangular box (all 12 edges, chamfer = c) module cbox(w, h, d, c=chamfer) { hull() { translate([c, c, 0]) cube([w-2*c, h-2*c, d ]); translate([0, c, c]) cube([w, h-2*c, d-2*c ]); translate([c, 0, c]) cube([w-2*c, h, d-2*c ]); } } // Rounded-end vent slot: length along Y, width w, extrudes in +Z by d module slot(len, w, d) { r = w/2; hull() { translate([0, -len/2+r, 0]) cylinder(r=r, h=d); translate([0, len/2-r, 0]) cylinder(r=r, h=d); } } // Row of n vent slots stepping in X module vent_row(n, len, w, spacing, depth) { for(i = [0:n-1]) translate([i*(w+spacing), 0, 0]) slot(len, w, depth); } // M3 heat-set insert boss (sits proud from an inner face) module insert_boss(total_h = insert_h + 4) { difference() { cylinder(d=boss_od, h=total_h); cylinder(d=insert_dia, h=insert_h); translate([0,0,insert_h]) cylinder(d=m3_dia, h=total_h); } } // ============================================================ // KICKSTAND // ============================================================ // Geometry (all in rear-cover model space, rear face = Z=0 plane): // // Side view (Y-Z plane): // // Y=ks_gusset_h ─┐ // │ ← gusset strip on rear face // Y=0 ───────────┼──────────────────────────────────── rear cover bottom // │╲ ← gusset triangle // plate ──┼─╲────────────────────────────────────────── // (ks_thick)│ ╲ (sloping, thicker at tip) // ╲ ╲___________________________________ // Y=-(ks_thick+ks_drop) Z=-ks_depth // // The plate and gussets are extruded across ks_width in X. // The gussets (hull triangles) brace the plate against the rear face, // preventing the kickstand from snapping off at the root. // module kickstand() { ks_x0 = (enc_w - ks_width) / 2; translate([ks_x0, 0, 0]) { // ── Main plate ──────────────────────────────────────── // Wedge: root at Z=0 is ks_thick tall; // tip at Z=-ks_depth is (ks_thick+ks_drop) tall. // Top surface flush with enclosure bottom (Y=0). hull() { // Root strip — along rear face translate([0, -ks_thick, 0]) cube([ks_width, ks_thick, wall]); // Tip strip — at full reach, thicker to keep plate horizontal translate([0, -(ks_thick + ks_drop), -ks_depth]) cube([ks_width, ks_thick + ks_drop, wall]); } // ── Triangular gussets (left + right ends) ──────────── // Each gusset is a hull of three patches: // A – vertical strip up the rear face (height = ks_gusset_h) // B – small square at plate root (Y=-ks_thick, Z=0) // C – small square at plate tip (Y=-(ks_thick+ks_drop), Z=-ks_depth) for(bx = [0, ks_width - ks_thick]) { hull() { // A: attachment strip going up the rear face translate([bx, 0, -wall]) cube([ks_thick, ks_gusset_h, wall]); // B: plate root corner translate([bx, -ks_thick, -wall]) cube([ks_thick, ks_thick, wall]); // C: plate tip corner translate([bx, -(ks_thick + ks_drop), -ks_depth]) cube([ks_thick, ks_thick, wall]); } } } } // ============================================================ // FRONT BEZEL // ============================================================ // Two-piece design: bezel + rear cover join with M3 screws through // the bezel corners into heat-set inserts in the rear cover bosses. // The bezel is intentionally removable for Pi access. module front_bezel() { difference() { cbox(enc_w, enc_h, wall + recess); // Active display window (full depth cut) translate([(enc_w - scr_active_w)/2, (enc_h - scr_active_h)/2, -0.1]) cube([scr_active_w, scr_active_h, wall+recess+0.2]); // 1 mm recess pocket — bezel lip grips screen edge translate([(enc_w - scr_w)/2, (enc_h - scr_h)/2, wall]) cube([scr_w, scr_h, recess+0.1]); // M3 screw clearance holes at 4 corners for(x = [corner_inset, enc_w - corner_inset]) for(y = [corner_inset, enc_h - corner_inset]) translate([x, y, -0.1]) cylinder(d=m3_dia, h=wall+recess+0.2); } } // ============================================================ // REAR COVER // ============================================================ module rear_cover() { n_vent = 6; vent_block_w = n_vent*(vent_w+vent_sp) - vent_sp; difference() { union() { // Main body cbox(enc_w, enc_h, enc_d); // Kickstand (integral, no supports needed — prints face-down) kickstand(); // Insert bosses at 4 corners (inner rear face, flush with enc_d) for(x = [corner_inset, enc_w - corner_inset]) for(y = [corner_inset, enc_h - corner_inset]) translate([x, y, enc_d]) rotate([180, 0, 0]) insert_boss(); } // ── HOLLOW INTERIOR ─────────────────────────────────── // Cavity = full screen footprint, from wall to enc_d (open toward bezel) translate([wall, wall, wall]) cube([scr_w, scr_h, enc_d]); // ── PORT CUTOUTS ────────────────────────────────────── // NOTE: The Pi's port edges are internal (Pi centred on screen). // Cutouts in the enclosure walls are reference openings for // short cable extensions routed to the wall. Adjust Y/Z offsets // to match your exact cable routing once screen mount is verified. // LEFT WALL — USB-C power + HDMI ×2 // Approximate Y positions relative to Pi bottom edge pi_bot = pi_enc_cy - pi_h/2; // USB-C power translate([-0.1, pi_bot + 3, pi_z + 2]) cube([wall+0.2, 11, 11]); // HDMI 0 translate([-0.1, pi_bot + 16, pi_z + 2]) cube([wall+0.2, 17, 9]); // HDMI 1 translate([-0.1, pi_bot + 35, pi_z + 2]) cube([wall+0.2, 17, 9]); // RIGHT WALL — RJ45 + USB-A ×4 pi_top = pi_enc_cy + pi_h/2; // RJ45 translate([enc_w-wall-0.1, pi_top - 24, pi_z + 1]) cube([wall+0.2, 22, 16]); // USB-A ×4 (two stacked pairs) translate([enc_w-wall-0.1, pi_bot + 2, pi_z + 1]) cube([wall+0.2, 50, 15]); // BOTTOM WALL — USB-C touch connector on screen side edge // (screen's own USB-C touch port, not Pi — sits at screen depth) translate([-0.1, enc_h/2 - 6, wall + scr_d - 5]) cube([wall+0.2, 12, 8]); // ── COOLING VENTS ───────────────────────────────────── // Bottom intake — 6 slots through bottom wall translate([enc_w/2 - vent_block_w/2, -0.1, wall + 8]) rotate([-90, 0, 0]) vent_row(n_vent, vent_l, vent_w, vent_sp, wall+0.2); // Top exhaust — 6 slots through top wall translate([enc_w/2 - vent_block_w/2, enc_h - wall + 0.1, wall + 8]) rotate([90, 0, 0]) vent_row(n_vent, vent_l, vent_w, vent_sp, wall+0.2); // SoC direct-vent — slot array in rear panel centred over Pi SoC translate([pi_enc_cx - soc_vent_sz/2, pi_enc_cy - soc_vent_sz/2, enc_d - wall - 0.1]) { n_soc = floor(soc_vent_sz / (vent_w + vent_sp)); for(i = [0:n_soc-1]) translate([i*(vent_w+vent_sp), soc_vent_sz/2 - vent_l/2, 0]) slot(vent_l, vent_w, wall+0.2); } // ── CABLE GLANDS — rear panel, bottom area ──────────── // Two M16 glands through the rear face (Z=0 plane). // Positioned below Pi, above kickstand root. for(i = [0:gland_count-1]) { cx = enc_w/2 + (i - (gland_count-1)/2) * gland_spacing; translate([cx, wall + gland_dia/2 + 4, -0.1]) cylinder(d=gland_dia, h=wall+0.2); } } } // ============================================================ // SCENE — exploded assembly view // Front bezel floats above rear cover to show the joint // ============================================================ color("DarkSlateGray", 0.9) translate([0, 0, enc_d + 12]) front_bezel(); color("SlateGray", 0.85) rear_cover();