|
@@ -0,0 +1,185 @@
|
|
|
|
|
+// Waveshare 2.13" e-Paper HAT (B) — ESP32 Nametag Case
|
|
|
|
|
+// Display slides in from X−; FPC routes through internal 180° bend guide at X+
|
|
|
|
|
+
|
|
|
|
|
+// --- Display Dimensions (landscape: portrait rotated 90°) ---
|
|
|
|
|
+DISP_W = 59.20; // overall width (X) — portrait height
|
|
|
|
|
+DISP_D = 29.20; // overall depth (Y) — portrait width
|
|
|
|
|
+DISP_T = 1.0; // thickness (Z)
|
|
|
|
|
+
|
|
|
|
|
+// --- Active (Visible) Area ---
|
|
|
|
|
+ACT_W = 48.55; // active area width (X) — portrait AA height
|
|
|
|
|
+ACT_D = 23.71; // active area depth (Y) — portrait AA width
|
|
|
|
|
+
|
|
|
|
|
+// Border offsets from display edge to active area:
|
|
|
|
|
+// X-: portrait top border ≈ 1.95 mm (open/entry side)
|
|
|
|
|
+// X+: portrait FPC border ≈ 8.70 mm (closed/backstop; FPC exits here)
|
|
|
|
|
+// Y±: symmetric ≈ 2.75 mm each side
|
|
|
|
|
+ACT_X_OFFSET = 1.95;
|
|
|
|
|
+ACT_Y_OFFSET = 2.75;
|
|
|
|
|
+
|
|
|
|
|
+// --- FPC Cable (exits X+ side of display) ---
|
|
|
|
|
+FPC_W = 12.5;
|
|
|
|
|
+FPC_T = 0.3;
|
|
|
|
|
+FPC_LEN = 20.0; // visualization length of hanging tail
|
|
|
|
|
+
|
|
|
|
|
+// --- FPC Bend Guide ---
|
|
|
|
|
+BEND_R = 2.0; // inner bend radius (mm)
|
|
|
|
|
+BEND_WALL = 1.5; // wall thickness around channel (mm)
|
|
|
|
|
+
|
|
|
|
|
+// --- FPC Excess-Cable Pocket (under display floor) ---
|
|
|
|
|
+FPC_POCKET_D = 0.75; // depth below display floor (Z), slip-fit with assembly clearance
|
|
|
|
|
+FPC_POCKET_L = 20.0; // length running toward X− from the X+ cavity wall
|
|
|
|
|
+
|
|
|
|
|
+// --- Resin Drain Holes (Z− face, under bend guide arc bottom) ---
|
|
|
|
|
+DRAIN_HOLES = true; // enable drain holes for SLA printing
|
|
|
|
|
+DRAIN_D = 3.0; // hole diameter (mm)
|
|
|
|
|
+DRAIN_MARGIN = 1.0; // clearance from FPC cable corner to hole edge (mm)
|
|
|
|
|
+
|
|
|
|
|
+// --- Case Parameters ---
|
|
|
|
|
+WALL = 4.2; // = BEND_WALL + 2*BEND_R − DISP_T − TOL: keeps exactly BEND_WALL below channel
|
|
|
|
|
+LIP = 1.5; // front bezel lip thickness (Z)
|
|
|
|
|
+TOL = 0.3; // per-side display clearance
|
|
|
|
|
+CHAM = 1.0;
|
|
|
|
|
+CHAM_ANGLE = 60;
|
|
|
|
|
+CORNER_R = 2.5;
|
|
|
|
|
+X_PAD = 0; // extra bezel thickness added at X− (entry) end
|
|
|
|
|
+
|
|
|
|
|
+// --- Magnetic Strip Pocket (back face) ---
|
|
|
|
|
+MAG_L = 1.772 * 25.4; // strip length ≈ 45.01 mm
|
|
|
|
|
+MAG_W = 0.512 * 25.4; // strip width ≈ 13.00 mm
|
|
|
|
|
+MAG_LIP_H = 2.0; // pocket depth
|
|
|
|
|
+MAG_TOL = 0.2; // per-side clearance
|
|
|
|
|
+
|
|
|
|
|
+// --- Preview Controls ---
|
|
|
|
|
+SHOW_DISP = false;
|
|
|
|
|
+SCREEN_EXPLODE = 0; // mm to pull display toward X− in preview
|
|
|
|
|
+SLICE_Z = 0; // show only below this Z for cross-section view (0 = off)
|
|
|
|
|
+SLICE_Y = 25; // show only below this Y for cross-section view (0 = off)
|
|
|
|
|
+
|
|
|
|
|
+// --- Derived Dimensions ---
|
|
|
|
|
+cav_w = DISP_W + TOL * 2;
|
|
|
|
|
+cav_d = DISP_D + TOL * 2;
|
|
|
|
|
+case_w = WALL + cav_w; // WALL on X+; guide flat face at x=case_w
|
|
|
|
|
+case_h = WALL + DISP_T + LIP;
|
|
|
|
|
+case_d = WALL * 2 + cav_d;
|
|
|
|
|
+_guide_or = BEND_R + BEND_WALL; // guide depth; case extends this far past case_w
|
|
|
|
|
+_guide_cz = WALL + DISP_T + TOL - BEND_R; // guide center Z: aligns guide top with entry slot top
|
|
|
|
|
+_sweep_half = 112.5; // half of 225° sweep; arms exit at ±_sweep_half°
|
|
|
|
|
+_exit_x = case_w + BEND_R * cos(-_sweep_half); // X of bottom exit arm on inner radius
|
|
|
|
|
+_exit_z = _guide_cz + BEND_R * sin(-_sweep_half); // Z of bottom exit arm on inner radius
|
|
|
|
|
+_exit_span_x = abs(BEND_R * cos(_sweep_half)); // X span from arc tip back to guide center
|
|
|
|
|
+
|
|
|
|
|
+_cham_xy = CHAM * tan(CHAM_ANGLE);
|
|
|
|
|
+
|
|
|
|
|
+// Drain holes: punched from Z− face up to just inside the arc bottom
|
|
|
|
|
+// Y offset keeps hole edges DRAIN_MARGIN clear of FPC cable corners (at ±FPC_W/2 from center)
|
|
|
|
|
+_drain_depth = _guide_cz - BEND_R + 0.5; // 1.8 mm solid + 0.5 mm into arc
|
|
|
|
|
+_drain_y_off = FPC_W / 2 - DRAIN_D / 2 - DRAIN_MARGIN; // inset from cable corner
|
|
|
|
|
+
|
|
|
|
|
+win_x = X_PAD + TOL + ACT_X_OFFSET;
|
|
|
|
|
+win_y = WALL + TOL + ACT_Y_OFFSET;
|
|
|
|
|
+
|
|
|
|
|
+// Magnetic strip pocket centered on full case length (including guide extension and X_PAD)
|
|
|
|
|
+_lip_il = MAG_L + 2 * MAG_TOL;
|
|
|
|
|
+_lip_iw = MAG_W + 2 * MAG_TOL;
|
|
|
|
|
+_lip_x = (X_PAD + case_w + _guide_or) / 2 - _lip_il / 2;
|
|
|
|
|
+_lip_y = case_d / 2 - _lip_iw / 2;
|
|
|
|
|
+
|
|
|
|
|
+// -------------------------------------------------------------------
|
|
|
|
|
+// Display mockup — approximate geometry for preview only
|
|
|
|
|
+// -------------------------------------------------------------------
|
|
|
|
|
+module EPD213B() {
|
|
|
|
|
+ color([0.82, 0.84, 0.80])
|
|
|
|
|
+ cube([DISP_W, DISP_D, DISP_T]);
|
|
|
|
|
+ color("white")
|
|
|
|
|
+ translate([ACT_X_OFFSET, ACT_Y_OFFSET, DISP_T])
|
|
|
|
|
+ cube([ACT_W, ACT_D, 0.02]);
|
|
|
|
|
+ color([1.0, 0.85, 0.1])
|
|
|
|
|
+ translate([DISP_W, (DISP_D - FPC_W) / 2, (DISP_T - FPC_T) / 2])
|
|
|
|
|
+ cube([FPC_LEN, FPC_W, FPC_T]);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// -------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+module rounded_box(l, w, h, r) {
|
|
|
|
|
+ hull()
|
|
|
|
|
+ for (dx = [r, l-r], dy = [r, w-r])
|
|
|
|
|
+ translate([dx, dy, 0]) cylinder(r=r, h=h, $fn=32);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+module epd_case() {
|
|
|
|
|
+ _slot_w = FPC_W + 1.0; // 0.5 mm per-side assembly clearance
|
|
|
|
|
+
|
|
|
|
|
+ difference() {
|
|
|
|
|
+ // Case body extended in X+ to enclose the bend guide, X_PAD extends X− end
|
|
|
|
|
+ rounded_box(X_PAD + case_w + _guide_or, case_d, case_h, CORNER_R);
|
|
|
|
|
+
|
|
|
|
|
+ // Display cavity — slot opens through X− face, extends full depth including X_PAD
|
|
|
|
|
+ translate([-0.01, WALL, WALL])
|
|
|
|
|
+ cube([X_PAD + cav_w + 0.01, cav_d, DISP_T + TOL]);
|
|
|
|
|
+
|
|
|
|
|
+ // Active area window + chamfered top edges
|
|
|
|
|
+ translate([win_x, win_y, WALL + DISP_T - 0.01])
|
|
|
|
|
+ cube([ACT_W, ACT_D, LIP - CHAM + 0.02]);
|
|
|
|
|
+ hull() {
|
|
|
|
|
+ translate([win_x, win_y, case_h - CHAM])
|
|
|
|
|
+ cube([ACT_W, ACT_D, 0.01]);
|
|
|
|
|
+ translate([win_x - _cham_xy, win_y - _cham_xy, case_h])
|
|
|
|
|
+ cube([ACT_W + 2*_cham_xy, ACT_D + 2*_cham_xy, 0.01]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Magnetic strip pocket in back face
|
|
|
|
|
+ translate([_lip_x, _lip_y, -0.01])
|
|
|
|
|
+ cube([_lip_il, _lip_iw, MAG_LIP_H + 0.01]);
|
|
|
|
|
+
|
|
|
|
|
+ // Combined cable channel — entry + return, interior walls removed
|
|
|
|
|
+ // Top is flat at WALL+DISP_T+TOL; floor slopes up from guide exit to cavity wall
|
|
|
|
|
+ hull() {
|
|
|
|
|
+ translate([X_PAD + _exit_x - 0.01, case_d / 2 - _slot_w / 2, _exit_z])
|
|
|
|
|
+ cube([_exit_span_x + 0.01, _slot_w, WALL + DISP_T + TOL - _exit_z]);
|
|
|
|
|
+ translate([X_PAD + cav_w - 0.01, case_d / 2 - _slot_w / 2, WALL - FPC_POCKET_D])
|
|
|
|
|
+ cube([0.02, _slot_w, DISP_T + TOL + FPC_POCKET_D]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // FPC excess-cable pocket below display floor — cable coils here after the bend guide
|
|
|
|
|
+ translate([X_PAD + cav_w - FPC_POCKET_L, case_d / 2 - _slot_w / 2,
|
|
|
|
|
+ WALL - FPC_POCKET_D])
|
|
|
|
|
+ cube([FPC_POCKET_L + 0.01, _slot_w, FPC_POCKET_D + 0.01]);
|
|
|
|
|
+
|
|
|
|
|
+ // Internal 225° bend guide channel
|
|
|
|
|
+ translate([X_PAD + case_w, case_d / 2, _guide_cz])
|
|
|
|
|
+ rotate([-90, 0, 0])
|
|
|
|
|
+ translate([0, 0, -_slot_w / 2])
|
|
|
|
|
+ linear_extrude(_slot_w)
|
|
|
|
|
+ intersection() {
|
|
|
|
|
+ circle(r = BEND_R, $fn = 64);
|
|
|
|
|
+ polygon(concat([[0, 0]],
|
|
|
|
|
+ [for (a = [112.5 : -5 : -112.5])
|
|
|
|
|
+ [(BEND_R + 0.01) * cos(a), (BEND_R + 0.01) * sin(a)]]));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Resin drain holes through Z− face at arc bottom
|
|
|
|
|
+ // Symmetric pair inset from FPC cable corners by DRAIN_MARGIN so cable glides past
|
|
|
|
|
+ if (DRAIN_HOLES)
|
|
|
|
|
+ for (s = [-1, 1])
|
|
|
|
|
+ translate([X_PAD + case_w, case_d / 2 + s * _drain_y_off, -0.01])
|
|
|
|
|
+ cylinder(d = DRAIN_D, h = _drain_depth + 0.01, $fn = 32);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// -------------------------------------------------------------------
|
|
|
|
|
+// Render
|
|
|
|
|
+// -------------------------------------------------------------------
|
|
|
|
|
+color("#004B49")
|
|
|
|
|
+ intersection() {
|
|
|
|
|
+ epd_case();
|
|
|
|
|
+ cube([X_PAD + case_w + _guide_or + 1,
|
|
|
|
|
+ ($preview && SLICE_Y > 0) ? SLICE_Y : case_d + 1,
|
|
|
|
|
+ ($preview && SLICE_Z > 0) ? SLICE_Z : case_h + 1]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+if ($preview && SHOW_DISP)
|
|
|
|
|
+ translate([X_PAD + TOL - SCREEN_EXPLODE, WALL + TOL, WALL])
|
|
|
|
|
+ EPD213B();
|