cable_cap.scad 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Waveshare 2.13" e-Paper HAT (B) — ESP32 Nametag Case
  2. // Display slides in from X+; FPC cable exits freely out the open X+ side
  3. // --- Display Dimensions (landscape: portrait rotated 90°) ---
  4. DISP_W = 59.20; // overall width (X) — portrait height
  5. DISP_D = 29.20; // overall depth (Y) — portrait width
  6. DISP_T = 1.0; // thickness (Z)
  7. // --- Active (Visible) Area ---
  8. ACT_W = 48.55; // active area width (X) — portrait AA height
  9. ACT_D = 23.71; // active area depth (Y) — portrait AA width
  10. // Border offsets from display edge to active area:
  11. // X-: portrait top border ≈ 1.95 mm (closed/backstop side)
  12. // X+: portrait FPC border ≈ 8.70 mm (open/entry side)
  13. // Y±: symmetric ≈ 2.75 mm each side
  14. ACT_X_OFFSET = 1.95;
  15. ACT_Y_OFFSET = 2.75;
  16. // --- FPC Cable (exits X+ side) ---
  17. FPC_W = 12.5;
  18. FPC_T = 0.3;
  19. FPC_LEN = 20.0; // visualization length of hanging tail
  20. // --- FPC Return Pocket ---
  21. FPC_FLOOR_DROP = 0.75; // extra depth below display cavity floor
  22. FPC_POCKET_LEN = 20.0; // how far in from X+ face the pocket extends
  23. FPC_POCKET_PAD = 2.0; // padding on each side of FPC cable width
  24. // --- Case Parameters ---
  25. WALL = 3.0; // must exceed MAG_LIP_H (2.0) to keep pocket floor
  26. LIP = 1.5; // front bezel lip thickness (Z)
  27. TOL = 0.3; // per-side display clearance
  28. CHAM = 1.0;
  29. CHAM_ANGLE = 60;
  30. CORNER_R = 2.5;
  31. CLIP_Z = 0; // preview: mm to remove from Z+ (0 = full model)
  32. CLIP_Y = 5; // preview: mm to remove from Y+ (0 = full model)
  33. SHOW_DISP = true;
  34. SCREEN_EXPLODE = 0; // mm to pull display toward X+ in preview
  35. // --- Slip-Fit Cap (X+ end) ---
  36. SHOW_CAP = true; // show cap in preview
  37. CAP_ENGAGE = 8.0; // sleeve engagement length over case exterior (X)
  38. CAP_WALL = 1.5; // sleeve and end-wall thickness
  39. CAP_TOL = 0.2; // per-side slip clearance between bore and case exterior
  40. CAP_TIP = 0.8; // minimum wall thickness at X- (open) edge after taper
  41. CAP_EXPLODE = 5; // preview: mm to pull cap away from case in X+ (0 = seated)
  42. // --- Magnetic Strip Locating Lip (rear face) ---
  43. MAG_L = 1.772 * 25.4; // strip length ≈ 45.01 mm
  44. MAG_W = 0.512 * 25.4; // strip width ≈ 13.00 mm
  45. MAG_LIP_H = 2.0; // lip height protruding from rear face
  46. MAG_LIP_WALL = 1.0; // lip wall thickness
  47. MAG_TOL = 0.2; // per-side clearance for strip fit
  48. // --- Derived Dimensions ---
  49. cav_w = DISP_W + TOL * 2;
  50. cav_d = DISP_D + TOL * 2;
  51. case_w = WALL + cav_w; // no X+ wall; open for display entry
  52. case_h = WALL + DISP_T + LIP;
  53. case_d = WALL * 2 + cav_d;
  54. _cham_xy = CHAM * tan(CHAM_ANGLE);
  55. win_x = WALL + TOL + ACT_X_OFFSET;
  56. win_y = WALL + TOL + ACT_Y_OFFSET;
  57. // FPC return pocket geometry (X+ face)
  58. fpc_pkt_y = WALL + TOL + (DISP_D - FPC_W) / 2 - FPC_POCKET_PAD;
  59. fpc_pkt_yw = FPC_W + 2 * FPC_POCKET_PAD;
  60. // Cap derived
  61. _cap_bore_w = case_d + 2 * CAP_TOL; // bore Y width
  62. _cap_bore_h = case_h + 2 * CAP_TOL; // bore Z height
  63. // FPC exit slot spans the full FPC pocket height (pocket floor → cavity ceiling)
  64. _cap_slot_w = FPC_W + 1.0; // slot Y width
  65. _cap_slot_h = FPC_FLOOR_DROP + DISP_T + TOL + 2 * CAP_TOL; // slot Z height
  66. // Magnetic strip recess geometry (centered on back face)
  67. _lip_il = MAG_L + 2 * MAG_TOL;
  68. _lip_iw = MAG_W + 2 * MAG_TOL;
  69. _lip_x = case_w / 2 - _lip_il / 2;
  70. _lip_y = case_d / 2 - _lip_iw / 2;
  71. // -------------------------------------------------------------------
  72. // Display mockup — approximate geometry for preview only
  73. // -------------------------------------------------------------------
  74. module EPD213B() {
  75. color([0.82, 0.84, 0.80])
  76. cube([DISP_W, DISP_D, DISP_T]);
  77. color("white")
  78. translate([ACT_X_OFFSET, ACT_Y_OFFSET, DISP_T])
  79. cube([ACT_W, ACT_D, 0.02]);
  80. color([1.0, 0.85, 0.1])
  81. translate([DISP_W, (DISP_D - FPC_W) / 2, (DISP_T - FPC_T) / 2])
  82. cube([FPC_LEN, FPC_W, FPC_T]);
  83. }
  84. // -------------------------------------------------------------------
  85. module rounded_box(l, w, h, r) {
  86. hull()
  87. for (dx = [r, l-r], dy = [r, w-r])
  88. translate([dx, dy, 0]) cylinder(r=r, h=h, $fn=32);
  89. }
  90. module epd_case() {
  91. difference() {
  92. rounded_box(case_w, case_d, case_h, CORNER_R);
  93. // Display cavity — extends 0.01 mm past X+ face to leave that side open
  94. translate([WALL, WALL, WALL])
  95. cube([cav_w + 0.01, cav_d, DISP_T + TOL]);
  96. // Active area window + chamfered top edges
  97. translate([win_x, win_y, WALL + DISP_T - 0.01])
  98. cube([ACT_W, ACT_D, LIP - CHAM + 0.02]);
  99. hull() {
  100. translate([win_x, win_y, case_h - CHAM])
  101. cube([ACT_W, ACT_D, 0.01]);
  102. translate([win_x - _cham_xy, win_y - _cham_xy, case_h])
  103. cube([ACT_W + 2*_cham_xy, ACT_D + 2*_cham_xy, 0.01]);
  104. }
  105. // FPC return pocket — drops floor of display cavity near X+ face
  106. translate([case_w - FPC_POCKET_LEN, fpc_pkt_y, WALL - FPC_FLOOR_DROP - 0.01])
  107. cube([FPC_POCKET_LEN + 0.02, fpc_pkt_yw, DISP_T + TOL + FPC_FLOOR_DROP + 0.01]);
  108. // Magnetic strip recess in back face
  109. translate([_lip_x, _lip_y, -0.01])
  110. cube([_lip_il, _lip_iw, MAG_LIP_H + 0.01]);
  111. }
  112. }
  113. // -------------------------------------------------------------------
  114. // Slip-fit cap — slides over X+ end of case; FPC exits through end-wall slot
  115. // -------------------------------------------------------------------
  116. module cable_cap() {
  117. // Translate so case exterior Y=0, Z=0 aligns with bore inside walls
  118. translate([case_w - CAP_ENGAGE + CAP_EXPLODE, -CAP_TOL - CAP_WALL, -CAP_TOL - CAP_WALL])
  119. difference() {
  120. // Outer shell: hull between two square profiles
  121. // X- (open end): inset to CAP_TIP wall thickness
  122. // X+ (closed end): full CAP_WALL thickness
  123. hull() {
  124. translate([0, CAP_WALL - CAP_TIP, CAP_WALL - CAP_TIP])
  125. cube([0.01, _cap_bore_w + 2 * CAP_TIP, _cap_bore_h + 2 * CAP_TIP]);
  126. translate([CAP_ENGAGE + CAP_WALL - 0.01, 0, 0])
  127. cube([0.01, _cap_bore_w + 2 * CAP_WALL, _cap_bore_h + 2 * CAP_WALL]);
  128. }
  129. // Bore — open on X- side; closes at end wall (local x = CAP_ENGAGE)
  130. translate([-0.01, CAP_WALL, CAP_WALL])
  131. cube([CAP_ENGAGE + 0.02, _cap_bore_w, _cap_bore_h]);
  132. // FPC cable exit slot through end wall
  133. // Y: centered on case; Z: from FPC pocket floor to display cavity ceiling
  134. translate([CAP_ENGAGE - 0.01,
  135. CAP_WALL + CAP_TOL + case_d / 2 - _cap_slot_w / 2,
  136. CAP_WALL + CAP_TOL + WALL - FPC_FLOOR_DROP])
  137. cube([CAP_WALL + 0.02, _cap_slot_w, _cap_slot_h]);
  138. }
  139. }
  140. color("#004B49")
  141. intersection() {
  142. epd_case();
  143. cube([case_w + 1,
  144. ($preview && CLIP_Y > 0) ? max(0.01, case_d - CLIP_Y) : case_d + 1,
  145. ($preview && CLIP_Z > 0) ? max(0.01, case_h - CLIP_Z) : case_h + 1]);
  146. }
  147. if ($preview && SHOW_DISP)
  148. translate([WALL + TOL + SCREEN_EXPLODE, WALL + TOL, WALL])
  149. EPD213B();
  150. if (SHOW_CAP)
  151. color("#1A5C5B")
  152. intersection() {
  153. cable_cap();
  154. translate([0, -(CAP_TOL + CAP_WALL), -(CAP_TOL + CAP_WALL)])
  155. cube([case_w + CAP_WALL + CAP_ENGAGE + CAP_EXPLODE + 1,
  156. ($preview && CLIP_Y > 0)
  157. ? max(0.01, case_d + CAP_TOL + CAP_WALL - CLIP_Y)
  158. : case_d + 2 * (CAP_TOL + CAP_WALL) + 1,
  159. case_h + 2 * (CAP_TOL + CAP_WALL) + 1]);
  160. }