165 lines
7.8 KiB
OpenSCAD
165 lines
7.8 KiB
OpenSCAD
// =====================================================================
|
|
// ESP32-S3-DevKitC-1 - two-part snap-fit case
|
|
//
|
|
// BASE : floor + walls. Board rests on 4 round posts, on the inner side of the pin slots, lined up in X with the first and last pin. Pin rows poke down into two floor slots, so Dupont connectors plug in from underneath.
|
|
// LID : cap with a skirt that clips over the base's inner "tongue". Has the two button holes. USB-C opening is split between both parts, so neither part needs a bridged (unsupported) cut-out.
|
|
//
|
|
//
|
|
// !! Measure your board and check the values marked [CHECK] !!
|
|
// Set show_board = true and part = "assembled" to preview the fit.
|
|
// =====================================================================
|
|
|
|
part = "print"; // "print" | "base" | "lid" | "assembled"
|
|
show_board = true; // ghost board in the "assembled" view
|
|
explode = 0; // mm - lifts the lid in the "assembled" view
|
|
|
|
$fn = 64;
|
|
|
|
/* ---------------- Board ---------------- */
|
|
pcb_l = 63.74; // [CHECK] PCB length (USB-C end -> antenna end)
|
|
pcb_w = 28.35; // [CHECK] PCB width
|
|
pcb_t = 1.6; // PCB thickness
|
|
pin_below = 11; // [CHECK] PCB underside -> pin tips (header + pin)
|
|
header_pins = 22; // pins per row
|
|
pin_pitch = 2.54;
|
|
row_dist = 24.86; // distance between the two pin rows
|
|
header_x0 = 2; // [CHECK] first pin CENTRE, measured from the USB end
|
|
comp_h = 3.5; // tallest part above PCB top
|
|
|
|
/* ---------------- USB-C opening ---------------- */
|
|
usb_slot_w = 24; // one wide slot for both connectors + cable plug overmolds
|
|
usb_plug_h = 6.5; // slot height (plug overmold)
|
|
usb_axis_h = 1.7; // connector axis height above PCB top
|
|
|
|
/* ---------------- Buttons (lid holes) ---------------- */
|
|
// [CHECK] position of each button centre: [distance from USB end edge, offset from board centre line]
|
|
boot_pos = [28, -5.68];
|
|
rst_pos = [33, -5.68];
|
|
btn_d = 3.4; // hole diameter (use a pin / paper-clip / toothpick)
|
|
|
|
/* ---------------- Case / print tuning ---------------- */
|
|
wall = 2.8; // outer wall
|
|
tongue_t = 1.6; // inner lip of the base (the part that carries the snap groove)
|
|
tol = 0.2; // sliding clearance for PETG
|
|
clr = 0.4; // board-to-wall clearance
|
|
floor_t = 2.0;
|
|
plate_t = 2.0; // lid top thickness
|
|
top_gap = 0.6; // clearance above the tallest component
|
|
overlap = 6; // how far the lid skirt slips over the base
|
|
snap_d = 0.5; // snap depth - raise to 0.6 for a firmer click
|
|
snap_len = 12; // length of each snap bump
|
|
r_in = 1.0; // inner corner radius
|
|
|
|
pin_win_w = 3.0; // floor slot width under each pin row (Dupont housing ~2.6)
|
|
pin_win_margin = 2.0; // slot overhang beyond first/last pin
|
|
post_d = 3;
|
|
post_y = 8; // post offset from centre line (inner side of the pin rows, between the header bodies)
|
|
// post X positions: in line with the first and last pin of the rows
|
|
post_x = [header_x0, header_x0 + (header_pins-1)*pin_pitch];
|
|
|
|
/* ---------------- Derived ---------------- */
|
|
L_i = pcb_l + 2*clr;
|
|
W_i = pcb_w + 2*clr;
|
|
z_pcb_bot = pin_below + 0.5; // pin tips end 0.5 mm above the print bed
|
|
z_pcb_top = z_pcb_bot + pcb_t;
|
|
z_split = z_pcb_top + 2; // joint between base and lid
|
|
z_ceil = z_pcb_top + comp_h + top_gap;
|
|
z_top = z_ceil + plate_t;
|
|
z_usb_bot = z_pcb_top + usb_axis_h - usb_plug_h/2;
|
|
z_usb_top = z_pcb_top + usb_axis_h + usb_plug_h/2;
|
|
z_snap = z_split - 3.3;
|
|
ridge_d = snap_d + tol - 0.1;
|
|
snap_x = [0.27*L_i, 0.73*L_i];
|
|
|
|
/* ---------------- Helpers ---------------- */
|
|
module rr(x0, x1, y0, y1, r) {
|
|
translate([x0+r, y0+r]) offset(r=r) square([x1-x0-2*r, y1-y0-2*r]);
|
|
}
|
|
module outline(d=0) { rr(-d, L_i+d, -W_i/2-d, W_i/2+d, r_in+d); }
|
|
module slab(d, z0, z1) { translate([0,0,z0]) linear_extrude(z1-z0) outline(d); }
|
|
|
|
// 45-degree triangular prism along X; apex points toward -Y (into the case)
|
|
module wedge(len, d) {
|
|
rotate([90,0,90]) linear_extrude(len, center=true)
|
|
polygon([[-d,0],[0.3,d+0.3],[0.3,-d-0.3]]);
|
|
}
|
|
// all snap positions: two per long side + one on the far end wall
|
|
module snap_set(off, d) {
|
|
for (xc = snap_x, s = [-1,1])
|
|
translate([xc, s*(W_i/2 + tongue_t + off), z_snap])
|
|
mirror([0, s<0 ? 1 : 0, 0]) wedge(snap_len, d);
|
|
translate([L_i + tongue_t + off, 0, z_snap])
|
|
rotate([0,0,-90]) wedge(snap_len, d);
|
|
}
|
|
|
|
/* ---------------- BASE ---------------- */
|
|
module base() {
|
|
difference() {
|
|
union() {
|
|
difference() {
|
|
slab(wall, 0, z_split);
|
|
slab(0, floor_t, z_split+1); // pocket
|
|
translate([0,0,z_split-overlap]) // rebate -> leaves the tongue
|
|
linear_extrude(overlap+1)
|
|
difference() { outline(wall+1); outline(tongue_t); }
|
|
snap_set(0, snap_d); // snap grooves
|
|
}
|
|
for (px = post_x, s = [-1,1]) // PCB support posts (in line with first / last pin)
|
|
translate([clr+px, s*post_y, floor_t-0.01])
|
|
cylinder(d=post_d, h=z_pcb_bot-floor_t+0.01);
|
|
}
|
|
// pin-row slots through the floor
|
|
for (s = [-1,1])
|
|
translate([clr + header_x0 - pin_win_margin, s*row_dist/2 - pin_win_w/2, -1])
|
|
cube([(header_pins-1)*pin_pitch + 2*pin_win_margin, pin_win_w, floor_t+2]);
|
|
// lower half of the USB-C opening (open at the top -> no bridging)
|
|
translate([-wall-1, -usb_slot_w/2, z_usb_bot])
|
|
cube([wall+1, usb_slot_w, z_split+1-z_usb_bot]);
|
|
}
|
|
}
|
|
|
|
/* ---------------- LID (modelled in assembled position) ---------------- */
|
|
module lid() {
|
|
difference() {
|
|
union() {
|
|
slab(wall, z_split, z_top); // cap
|
|
translate([0,0,z_split-overlap+0.3]) // skirt over the tongue
|
|
linear_extrude(overlap-0.3+0.01)
|
|
difference() { outline(wall); outline(tongue_t+tol); }
|
|
snap_set(tol, ridge_d); // snap ridges
|
|
}
|
|
slab(0, z_split-1, z_ceil); // cavity
|
|
// upper half of the USB-C opening (open at the skirt edge -> no bridging)
|
|
translate([-wall-1, -usb_slot_w/2, z_split-overlap-1])
|
|
cube([wall+1, usb_slot_w, z_usb_top-(z_split-overlap-1)]);
|
|
// BOOT / RST holes
|
|
for (p = [boot_pos, rst_pos])
|
|
translate([clr+p[0], p[1], z_ceil-1]) cylinder(d=btn_d, h=plate_t+2);
|
|
}
|
|
}
|
|
module lid_for_print() { translate([0,0,z_top]) rotate([180,0,0]) lid(); }
|
|
|
|
/* ---------------- Preview ghost (approximate) ---------------- */
|
|
module ghost() {
|
|
color("seagreen", 0.6) {
|
|
translate([clr, -pcb_w/2, z_pcb_bot]) cube([pcb_l, pcb_w, pcb_t]);
|
|
for (s=[-1,1]) translate([clr+header_x0-1.27, s*row_dist/2-1.27, z_pcb_bot-2.5])
|
|
cube([(header_pins-1)*pin_pitch+2.54, 2.54, 2.5]);
|
|
for (s=[-1,1]) for (i=[0:header_pins-1])
|
|
translate([clr+header_x0+i*pin_pitch-0.32, s*row_dist/2-0.32, z_pcb_bot-pin_below])
|
|
cube([0.64,0.64,pin_below]);
|
|
}
|
|
color("silver", 0.8) {
|
|
for (s=[-1,1]) translate([clr-0.5, s*6.5-4.47, z_pcb_top]) cube([7.3, 8.94, 3.2]); // USB-C (approx. y)
|
|
translate([clr+pcb_l-25.5, -9, z_pcb_top]) cube([25.5, 18, 3.2]); // module
|
|
}
|
|
color("black", 0.8) for (p=[boot_pos, rst_pos])
|
|
translate([clr+p[0], p[1], z_pcb_top]) cylinder(d=3, h=3.5);
|
|
}
|
|
|
|
/* ---------------- Output ---------------- */
|
|
if (part == "base") base();
|
|
else if (part == "lid") lid_for_print();
|
|
else if (part == "assembled") { base(); translate([0,0,explode]) lid(); if (show_board) ghost(); }
|
|
else { base(); translate([0, -(W_i + 2*wall + 10), 0]) lid_for_print(); }
|