Created
June 30, 2023 21:01
-
-
Save joeycastillo/1835151d5760fa730ffa566defb53698 to your computer and use it in GitHub Desktop.
VERY WORK-IN-PROGRESS case for Open Book Abridged Edition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Render front side | |
front = true; | |
// Render back side | |
back = true; | |
// level of detail | |
level_of_detail = 60; | |
// The thickness of the PCB as fabricated | |
board_thickness = 1.0; | |
// The tallest part on the back of the board | |
tallest_part = 5.5; // 4; | |
// How thick you want the sidewalls to be | |
side_wall_thickness = 1.5; | |
// How thick you want the back wall to be | |
back_wall_thickness = 1.2; | |
// How far case should extend above PCB | |
front_thickness = 2.5; | |
// Added to holes and cavities to make sure things fit | |
xy_tolerance = 0.3; | |
// Added on the Z axis to account for 3D priting irregulrities | |
z_tolerance = 0.3; | |
// The gap for tabs | |
tab_gap = 0.5; | |
// How much the clicky bit of the tab should overhang the board or top case | |
tab_lock_overhang = 1; | |
// Cut out holes for JST ports | |
jst = false; | |
// Cut out holes for two power pins on C1-02 board | |
power_debug = false; | |
// Cut out holes for pressing the BOOTSEL button | |
bootsel_hole = false; | |
// Render only BOOTSEL button (check back side too) | |
bootsel_only = false; | |
// should these be settings? | |
lock_button_tab_length = tallest_part + board_thickness; | |
tab_length = tallest_part + board_thickness; | |
// DEPRECATED | |
screen_bezel_extra = 1; | |
case_seam = -board_thickness; | |
module _cylinderForHull(x, y, wall) { | |
xsign = (x > 0) ? -1 : 1; | |
ysign = (y > 0) ? -1 : 1; | |
r = 3 + xy_tolerance + (wall ? side_wall_thickness : 0); | |
h = board_thickness + tallest_part + (wall ? front_thickness + back_wall_thickness : 0); | |
z = wall ? -back_wall_thickness - tallest_part : -tallest_part; | |
translate([ xsign * 3 + x, ysign * 3 + y, z ]) cylinder(h, r, r, $fn = level_of_detail); | |
} | |
module _peg(x, y) { | |
xsign = (x > 0) ? -1 : 1; | |
ysign = (y > 0) ? -1 : 1; | |
// translate([ xsign * 3 + x, ysign * 3 + y, -z_tolerance ]) cylinder(board_thickness, 1.05, 1.05, $fn = level_of_detail); | |
hull() { | |
// main cylinder for peg | |
translate([ xsign * 3 + x, ysign * 3 + y, -tallest_part ]) cylinder(tallest_part - z_tolerance, 2.5, 2.5, $fn = level_of_detail); | |
// extend toward the horizontal wall | |
translate([ xsign * 3 + x + xsign * -2.75, ysign * 3 + y + ysign * 2.1, -tallest_part ]) cylinder(tallest_part - z_tolerance, 1, 1, $fn = level_of_detail); | |
// extend toward the vertical wall | |
translate([ xsign * 3 + x + xsign * 2.1, ysign * 3 + y + ysign * -2.75, -tallest_part ]) cylinder(tallest_part - z_tolerance, 1, 1, $fn = level_of_detail); | |
// extend toward the corner | |
translate([ xsign * 3 + x + xsign * -2.0, ysign * 3 + y + ysign * -2.0, -tallest_part ]) cylinder(tallest_part - z_tolerance, 0.75, 0.75, $fn = level_of_detail); | |
} | |
} | |
module _htab_cutout(x, y) { | |
ysign = -1; | |
if (y > 0) { | |
ysign = 1; | |
} | |
// full width cutout (top) | |
translate([ x, y + (y > 0 ? (side_wall_thickness + xy_tolerance * 2) : 0) + ysign * (side_wall_thickness + xy_tolerance) / 2, (front_thickness + board_thickness) / 2 ]) | |
cube([10 + tab_gap, 3 + side_wall_thickness + xy_tolerance, front_thickness + board_thickness], true); | |
// left cutout | |
translate([ x - 5, y + (y > 0 ? (side_wall_thickness + xy_tolerance * 2) : 0) + ysign * (side_wall_thickness + xy_tolerance * 2) / 2, board_thickness - tab_length / 2 ]) | |
cube([tab_gap, side_wall_thickness + xy_tolerance * 2, tab_length], true); | |
// right cutout | |
translate([ x + 5, y + (y > 0 ? (side_wall_thickness + xy_tolerance * 2) : 0) + ysign * (side_wall_thickness + xy_tolerance * 2) / 2, board_thickness - tab_length / 2 ]) | |
cube([tab_gap, side_wall_thickness + xy_tolerance * 2, tab_length], true); | |
} | |
module _htab(x, top) { | |
union() { | |
tab_bottom_thickness = 1.2; | |
tab_top_thickness = 1.5; | |
tab_very_top_thickness = 0; // -side_wall_thickness + 0.01; | |
// main tab body | |
hull() { | |
// a rectangle at the bottom | |
translate([ x - 5 + tab_gap / 2 , (top ? -side_wall_thickness : 115 - tab_bottom_thickness - xy_tolerance * 2) + (top ? -xy_tolerance : xy_tolerance * 3), -tallest_part]) | |
cube([10 - tab_gap, tab_bottom_thickness + side_wall_thickness, 0.001]); | |
// a rectangle at the top | |
translate([ x - 5 + tab_gap / 2 , (top ? -side_wall_thickness : 115 - tab_top_thickness - xy_tolerance * 2) + (top ? -xy_tolerance : xy_tolerance * 3), front_thickness + board_thickness + z_tolerance]) | |
cube([10 - tab_gap, tab_top_thickness + side_wall_thickness, 0.001]); | |
} | |
// the clicky bit | |
hull() { | |
// meets "at the top" above on the Z-axis, but adds overhang | |
translate([ x - 5 + tab_gap / 2 , 0 + (top ? -side_wall_thickness : 115 - 1.5 - tab_lock_overhang) + (top ? -xy_tolerance : xy_tolerance), front_thickness + board_thickness + z_tolerance]) | |
cube([10 - tab_gap, side_wall_thickness + 1.5 + tab_lock_overhang, 0.001]); | |
// The tab body | |
translate([ x - 5 + tab_gap / 2 , (top ? -side_wall_thickness : 115 - tab_very_top_thickness - xy_tolerance * 2) + (top ? -xy_tolerance : xy_tolerance * 3), front_thickness + board_thickness + z_tolerance + 1]) | |
cube([10 - tab_gap, tab_very_top_thickness + side_wall_thickness, 0.001]); | |
} | |
} | |
} | |
module _vtab(y) { | |
union() { | |
translate([ 83.5 + xy_tolerance * 2, y + xy_tolerance, -tallest_part ]) | |
cube([ 1.5 - xy_tolerance, 10 - xy_tolerance * 2, tallest_part + board_thickness ]); | |
translate([ 83.5 - 3 + xy_tolerance * 2, y + xy_tolerance, -tallest_part ]) | |
cube([ 1.5 + 3 - xy_tolerance, 10 - xy_tolerance * 2, tallest_part - z_tolerance ]); | |
} | |
} | |
module _roundedBoxForUSB(xdim, ydim, zdim, rdim) { | |
hull() { | |
translate([rdim,rdim,0]) cylinder(h=zdim,r=rdim, $fn=level_of_detail); | |
translate([xdim-rdim,rdim,0]) cylinder(h=zdim,r=rdim, $fn=level_of_detail); | |
translate([rdim,ydim-rdim,0]) cylinder(h=zdim,r=rdim, $fn=level_of_detail); | |
translate([xdim-rdim,ydim-rdim,0]) cylinder(h=zdim,r=rdim, $fn=level_of_detail); | |
} | |
} | |
module _button(x, y, angle, hole = false) { | |
extra = hole ? 0.5 : 0; | |
difference() { | |
union () { | |
translate([ x, y, board_thickness + (hole ? 4 : 3.2) / 2 ]) rotate([0, 0, angle]) cube([6 + extra, 3.5 + extra, hole ? 4 : 3.2], true); | |
// this line was for when _button keepout was taller | |
translate([ x, y, board_thickness + 0.0 / 2 ]) rotate([0, 0, angle]) cube([11, 2.54 + extra, 0.0], true); | |
translate([ x, y, board_thickness + 0.0 ]) rotate([90, 0, angle]) translate([-3.5, 0, 0]) rotate([0, 180, 0]) cylinder(2.54, 2, 2, true, $fn=3); | |
translate([ x, y, board_thickness + 0.0 ]) rotate([90, 0, angle]) translate([3.5, 0, 0]) cylinder(2.54, 2, 2, true, $fn=3); | |
if (!hole) { | |
translate([ x, y, board_thickness + 4 / 2 ]) rotate([0, 0, angle]) cube([2.75, 2, 4], true); | |
} | |
} | |
translate([ x, y, -5 ]) cube([15, 10, 10], true); | |
} | |
} | |
module jst(x, y, width, hole = false) { | |
extra = hole ? 0.2 : 0; | |
union () { | |
translate([ x - (hole ? 5 : 0) + extra, y, -5.5 / 2 ]) cube([10 + (hole ? 10 : 0), width + extra, 5.5 + extra], true); | |
} | |
} | |
module holes() { | |
union () { | |
// front parts | |
_button(20, 106, 0, true); // B3 | |
_button(65, 106, 0, true); // B4 | |
_button(36, 106, 90, true); // B5 | |
_button(42.5, 101, 0, true); // B6 | |
_button(42.5, 106, 0, true); // B7 | |
_button(42.5, 111, 0, true); // B8 | |
_button(49, 106, 90, true); // B9 | |
translate([ 0, 5.88, board_thickness ]) cube([78, 91.2, 1.5]); // screen thickness | |
hull() { | |
translate([ 10, 7.88, board_thickness - 0.01 ]) cube([65.5, 87, 0.01]); // screen active area | |
translate([ 10 - (screen_bezel_extra / 2), 7.88 - (screen_bezel_extra / 2), board_thickness + front_thickness + 0.01 ]) cube([65.5 + screen_bezel_extra, 87 + screen_bezel_extra, 0.001]); // screen active area | |
} | |
// back parts | |
// USB | |
translate([ 45-4.2, 1, -4 ]) rotate([ 90, 0, 0 ]) _roundedBoxForUSB(8.4,2.9,4.5,0.6); | |
// SD card | |
translate([ 84, 33.5, -2 ]) cube([10, 12, 2]); | |
// accessory ports | |
if (jst) { | |
jst(5, 44, 10, true); | |
jst(5, 57.5, 12, true); | |
jst(5, 71, 10, true); | |
} | |
// battery | |
translate([ 18 - xy_tolerance, 53 - xy_tolerance, -tallest_part - back_wall_thickness ]) cube([56 + xy_tolerance * 2, 24 + xy_tolerance * 2, tallest_part]); | |
// power pins | |
if (power_debug) { | |
translate([ 79, 59, -10 ]) cylinder(10, 0.9, 1.75, $fn=level_of_detail); | |
translate([ 79, 71.7, -10 ]) cylinder(10, 0.9, 1.75, $fn=level_of_detail); | |
} | |
if (bootsel_hole) { | |
translate([ 48.5, 12.5, -100 ]) cylinder(200, 1.08, 1.08, $fn=level_of_detail); | |
} else { | |
translate([ 48.5, 12.5, -100 ]) cylinder(200, 0.2, 0.2, $fn=level_of_detail); | |
} | |
// cable routing | |
translate([ -1, 41, 0 ]) cube( [ 2, 20, 2 ] ); | |
// on/off switch | |
hull() { | |
translate([ 85 - xy_tolerance, 91, -1.5 ]) cube([0.01, 4.5, 2], true); | |
translate([ 85 + side_wall_thickness + xy_tolerance, 91, -1.5 ]) cube([0.01, 7, 4], true); | |
} | |
// reset button | |
translate([ -xy_tolerance - 0.25, 91, -1.25 ]) cube([0.5, 4, 3], true); | |
hull() { | |
translate([ -xy_tolerance, 91, -1.25 ]) cube([0.01, 1, 0.75], true); | |
translate([ -side_wall_thickness - xy_tolerance, 91, -1.25 ]) cube([0.01, 3.5, 1.75], true); | |
} | |
// Lock button cutouts: | |
// half wall thickness for button presser. TODO: not half wall width but based on button travel | |
translate([ 75, -(side_wall_thickness + xy_tolerance) / 4, (board_thickness - 1.5) / 2 ]) cube([8, (side_wall_thickness + xy_tolerance) / 2, board_thickness + 1.5], true); | |
translate([ 75, -(side_wall_thickness + xy_tolerance) / 2, board_thickness ]) cube([8, side_wall_thickness + xy_tolerance, z_tolerance], true); | |
// cut out tabs for lock button presser | |
translate([ 75 - 3.75, -(side_wall_thickness + xy_tolerance) / 2, board_thickness - lock_button_tab_length / 2 ]) cube([0.5, side_wall_thickness + xy_tolerance, lock_button_tab_length], true); | |
translate([ 75 + 3.75, -(side_wall_thickness + xy_tolerance) / 2, board_thickness - lock_button_tab_length / 2 ]) cube([0.5, side_wall_thickness + xy_tolerance, lock_button_tab_length], true); | |
// tab cutouts | |
_htab_cutout(20, 0); | |
_htab_cutout(65, 0); | |
_htab_cutout(20, 115); | |
_htab_cutout(65, 115); | |
// cutouts for battery door. HACKY! | |
// translate([ 24, 49, -50 ]) cube([16, 2, 100]); | |
// translate([ 54, 49, -50 ]) cube([16, 2, 100]); | |
// translate([ 38, 79, -50 ]) cube([16, 2, 100]); | |
} | |
} | |
module case(top) { | |
union() { | |
// /* | |
difference() { | |
difference() { | |
union() { | |
hull() { | |
_cylinderForHull(0, 0, true); | |
_cylinderForHull(85, 0, true); | |
_cylinderForHull(0, 115, true); | |
_cylinderForHull(85, 115, true); | |
} | |
} | |
hull() { | |
_cylinderForHull(0, 0, false); | |
_cylinderForHull(85, 0, false); | |
_cylinderForHull(0, 115, false); | |
_cylinderForHull(85, 115, false); | |
} | |
holes(); | |
} | |
translate([ -10, -10, top ? (-20 - case_seam) : -case_seam ]) cube([105, 135, 20]); | |
} | |
if (!top) { | |
// Pegs. board sits on these. | |
_peg(0, 0); | |
_peg(85, 0); | |
_peg(0, 115); | |
_peg(85, 115); | |
// Horizontal tabs. they hold in the board. | |
_htab(20, true); | |
_htab(65, true); | |
_htab(20, false); | |
_htab(65, false); | |
// Vertical tabs. the board sits on these too | |
_vtab(9); | |
_vtab(52.5); | |
_vtab(96); | |
// Battery box. ingress protection. | |
difference() { | |
// battery box body | |
translate([ 17, 52, -tallest_part - z_tolerance ]) cube([58, 26, tallest_part]); | |
// but hollowed out | |
translate([ 18 - xy_tolerance, 53 - xy_tolerance, -tallest_part - 1 ]) cube([56 + xy_tolerance * 2, 24 + xy_tolerance * 2, tallest_part + 2]); | |
// cutouts for solder pads | |
translate([ 13.5, 56.5, -1 ]) cube([65, 6, 1]); | |
translate([ 13.5, 67.5, -1 ]) cube([65, 6, 1]); | |
} | |
// BOOTSEL pin | |
difference() { | |
translate([ 48.5, 12.5, -tallest_part - z_tolerance ]) cylinder(tallest_part - 4, 1.58, 1.58, $fn=level_of_detail); | |
translate([ 48.5, 12.5, -100 ]) cylinder(200, 1.08, 1.08, $fn=level_of_detail); | |
} | |
// extra little bit of material between tab and lock button | |
// since it'll probably break off otherwise. | |
// making it a little shorter than the corner pegs | |
translate([ 70 + xy_tolerance, - xy_tolerance, -tallest_part ]) | |
cube([ 1 - xy_tolerance, 4.25, tallest_part - z_tolerance * 1.5 ]); | |
/* | |
// extra weight at bottom | |
difference() { | |
translate([ 0 - xy_tolerance, 78, -tallest_part ]) cube([ 85 + xy_tolerance * 2, 35, tallest_part - z_tolerance - 2 ]); | |
// this one is hacky, just want to make sure the on/off switch can fit | |
translate([ 85 - xy_tolerance, 91, -4.8 ]) cube([12, 10.5, 5], true); | |
} | |
*/ | |
} | |
// */ | |
} | |
} | |
if (back) { | |
mirror([0, 1, 0]) { // flip coordinates to match KiCad | |
translate([(3 / 2) - xy_tolerance, (3 / 2) - xy_tolerance, tallest_part + back_wall_thickness]) case(false); | |
} | |
} | |
if (front) { | |
mirror([0, 1, 0]) { // flip coordinates to match KiCad | |
translate([(3 / 2) - xy_tolerance + 85 * 2 + 5, (3 / 2) - xy_tolerance, front_thickness * 2]) rotate([ 180, 0, 180 ]) case(true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment