diff --git a/enclosure/el512_frame.scad b/enclosure/el512_frame.scad index 880908e..1a36b83 100644 --- a/enclosure/el512_frame.scad +++ b/enclosure/el512_frame.scad @@ -62,9 +62,12 @@ d1_glass_gap = 7; // photo glass to the board's top face (ESP32 module is ~3 d1_rot = 0; // 0: USB to the right, 180: USB to the left d1_clear = 0.3; // clearance around the board d1_ledge = 2; // seat width under the short ends (the pin rows run along the long sides) -d1_tab_w = 6; // snap tab width -d1_tab_t = 1.4; // snap tab thickness +d1_arm_t = 0.8; // snap arm thickness (two 0.4 mm perimeters) +d1_arm_l = 12; // snap arm free length, root to tip +d1_arm_gap = 0.5; // snap arm to the end of the pocket d1_lip = 0.8; // how far the snap lip reaches over the board +d1_lip_w = 3; // snap lip length along the arm +d1_usb_w = 11; // kept free between the arms at each end for the USB plug d1_wall = 1.6; /* [Preview] */ @@ -103,9 +106,19 @@ d1_top = part_d - d1_glass_gap; // z of the board's top face d1_seat = d1_top - d1_t; // z of the board's bottom face d1_in_l = d1_l + 2 * d1_clear; d1_in_w = d1_w + 2 * d1_clear; +d1_flex = d1_clear + d1_lip; // worst-case arm deflection while snapping in +d1_arm_x = d1_in_l / 2 + d1_arm_gap; // inner face of the snap arms +d1_x_out = d1_arm_x + d1_arm_t + d1_flex + 0.4; // outer edge of the slot the arms flex into +d1_root = d1_in_w / 2 + d1_wall; // y of the arm roots +d1_tip = d1_root - d1_arm_l; // y of the arm tips +d1_catch = d1_top + 0.1; // z of the underside of the lips +// bending strain at the arm root with the lip pushed fully out +d1_strain = 1.5 * d1_arm_t * d1_flex / pow(d1_arm_l - d1_lip_w / 2, 2); assert(pcb_x0 >= wall + 1, "panel does not fit the frame width"); -assert(pcb_y0 - border >= d1_w + 2 * d1_wall + 4, "no room below the panel for the D1 mini"); +assert(pcb_y0 - border >= 2 * (d1_root + d1_wall) + 2, "no room below the panel for the D1 mini"); +assert(d1_tip >= d1_usb_w / 2, "snap arms reach into the USB plug's way: shorten d1_arm_l"); +assert(d1_strain <= 0.015, str("snap arms strained ", d1_strain * 100, " %, too much for PLA")); assert(panel_back - plate_t >= air_gap, "less than the recommended air gap behind the panel"); assert(screw_engage < insert_depth, "screw would bottom out in the insert nut"); assert(head_sink >= (csk ? 0.3 : socket_h + 0.2), @@ -114,14 +127,15 @@ assert(head_sink <= nut_z - 4, "screw_len too short for this stack"); echo(str("Insert depth ", part_d, " mm, air gap behind panel ", panel_back - plate_t, " mm, standoff height ", nut_z - plate_t, " mm, use M3x", screw, " ", head_type, - " (head ", head_sink, " mm below the back face)")); + " (head ", head_sink, " mm below the back face), snap arm strain ", + round(d1_strain * 1000) / 10, " %")); // ---------- 2D ---------- module outline() square([W, H]); module d1_footprint(grow = 0) { translate([d1_cx, d1_cy]) rotate(d1_rot) - square([d1_in_l + 2 * (d1_tab_t + 1) + 2 * grow, d1_in_w + 2 * d1_wall + 2 * grow], center = true); + square([2 * (d1_x_out + 1 + grow), 2 * (d1_root + d1_wall + grow)], center = true); } // Triangle cells; a horizontal strut runs along the panel's bottom edge and a vertex @@ -187,46 +201,53 @@ module screw_cut(p) translate(p) { } // Cradle for the D1 mini, board face up towards the glass. It rests on ledges under its -// short ends and two snap tabs at each end hold it down; the pin rows hang free over -// the hollow inside. The upper long wall has a slot for the wires to the panel. -module d1_cradle() { - zl = d1_top + d1_lip + 0.6; // top of the snap tabs - translate([d1_cx, d1_cy, 0]) rotate(d1_rot) { - difference() { - union() { - // long walls, flush with the board's top face - for (s = [-1, 1]) translate([-d1_in_l / 2, s > 0 ? d1_in_w / 2 : -d1_in_w / 2 - d1_wall, 0]) - cube([d1_in_l, d1_wall, d1_top]); - // seat ledges under the short ends - for (s = [-1, 1]) translate([s > 0 ? d1_in_l / 2 - d1_ledge : -d1_in_l / 2, -d1_in_w / 2, 0]) - cube([d1_ledge, d1_in_w, d1_seat]); - } - // wire slot towards the panel - translate([-10, d1_in_w / 2 - eps, plate_t]) cube([20, d1_wall + 2 * eps, d1_seat - plate_t - 2]); +// short ends and four snap arms hold it down; the pin rows hang free over the hollow +// inside. The upper long wall has a slot for the wires to the panel. +// +// The snap arms run along the short ends, anchored at the corners with the lip at the +// free end, and bend sideways: the strain runs along the printed perimeters instead of +// across the layers, so they hold up in PLA. Each arm stands on the bed in a slot through +// the plate. The arms leave d1_usb_w free in the middle of each end for the USB plug. +module d1_place() translate([d1_cx, d1_cy, 0]) rotate(d1_rot) children(); +module d1_quadrants() for (mx = [0, 1], my = [0, 1]) mirror([mx, 0, 0]) mirror([0, my, 0]) children(); + +module d1_cradle() d1_place() { + difference() { + union() { + // long walls, flush with the board's top face + for (s = [-1, 1]) translate([-d1_in_l / 2, s > 0 ? d1_in_w / 2 : -d1_in_w / 2 - d1_wall, 0]) + cube([d1_in_l, d1_wall, d1_top]); + // seat ledges under the short ends + for (s = [-1, 1]) translate([s > 0 ? d1_in_l / 2 - d1_ledge : -d1_in_l / 2, -d1_in_w / 2, 0]) + cube([d1_ledge, d1_in_w, d1_seat]); + // arm anchors, outside the long walls at the corners + d1_quadrants() translate([d1_in_l / 2 - 2, d1_root - eps, 0]) + cube([d1_x_out + 1 - (d1_in_l / 2 - 2), d1_wall + eps, d1_top]); } - // snap tabs, two per end, clear of the centred USB socket - for (sx = [-1, 1], sy = [-1, 1]) - translate([sx * (d1_in_l / 2 + 1 + d1_tab_t / 2), sy * (d1_in_w / 2 - d1_tab_w / 2), 0]) - rotate(sx > 0 ? 0 : 180) snap_tab(zl); + // wire slot towards the panel + translate([-10, d1_in_w / 2 - eps, plate_t]) cube([20, d1_wall + 2 * eps, d1_seat - plate_t - 2]); } } -// Tab standing outside the board edge, lip pointing to -X (towards the board). -// Printable without supports: the underside is a 45 degree chamfer across the 1 mm gap -// to the pocket, and only the catch itself (clearance + d1_lip) is a flat overhang. -// The tip has a short vertical face, so it does not print as a knife edge. -module snap_tab(zl) { - translate([-d1_tab_t / 2, -d1_tab_w / 2, 0]) cube([d1_tab_t, d1_tab_w, zl]); - gap = 1; // the tab stands 1 mm clear of the pocket - reach = gap + d1_clear + d1_lip; // tab face to lip tip +// Slots through the plate that free the arms from everything but their anchors. +module d1_arm_slots() d1_place() d1_quadrants() + translate([d1_in_l / 2, d1_tip - 0.6, -1]) cube([d1_x_out - d1_in_l / 2, d1_root - d1_tip + 0.6, plate_t + 2]); + +// The lip has a 45 degree chamfer underneath across the gap to the pocket, so only the +// catch itself (clearance + d1_lip) is a flat overhang, and a blunt tip. +module d1_arms() d1_place() d1_quadrants() { + t = d1_arm_t; + g = d1_arm_gap; + reach = g + d1_clear + d1_lip; // arm face to lip tip tip_h = 0.6; - translate([d1_tab_t / 2, d1_tab_w / 2, d1_top + 0.1]) rotate([90, 0, 0]) - linear_extrude(d1_tab_w) polygon([ - [0, -gap], [-d1_tab_t, -gap], // chamfer starts on the tab face - [-d1_tab_t - gap, 0], // 45 degrees across the gap - [-d1_tab_t - reach, 0], // flat catch over the board - [-d1_tab_t - reach, tip_h], // blunt tip - [-d1_tab_t, tip_h + reach], // 45 degree lead-in + translate([d1_arm_x, d1_tip, 0]) cube([t, d1_arm_l + eps, d1_catch]); + translate([d1_arm_x + t, d1_tip + d1_lip_w, d1_catch]) rotate([90, 0, 0]) + linear_extrude(d1_lip_w) polygon([ + [0, -g], [-t, -g], // chamfer starts on the arm face + [-t - g, 0], // 45 degrees across the gap + [-t - reach, 0], // flat catch over the board + [-t - reach, tip_h], // blunt tip + [-t, tip_h + reach], // 45 degree lead-in [0, tip_h + reach]]); } @@ -239,7 +260,9 @@ module frame() { d1_cradle(); } for (p = holes) screw_cut(p); + d1_arm_slots(); } + d1_arms(); } module ghosts() {