From b28ee2892a391fd5759ed3cbaadcf0ca3573cb80 Mon Sep 17 00:00:00 2001 From: MangaValk Date: Thu, 22 Oct 2026 00:27:20 +0200 Subject: [PATCH] D1 mini cradle: sideways snap arms so the clips survive PLA The upright snap tabs bent across the layer lines, which cracks in PLA. Each clip is now a thin arm along the board's short end, anchored at a corner with the lip at its free end, so it flexes in the layer plane. The arms stand on the bed in slots through the plate (no supports) and leave room for the USB plug. Asserts guard the USB keep-out and keep the bending strain under 1.5 %; the echo reports it. Co-Authored-By: Claude Opus 5.5 --- enclosure/el512_frame.scad | 103 +++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 40 deletions(-) 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() {