Skip to content

Instantly share code, notes, and snippets.

@haikusw
Forked from joeycastillo/bookcase.scad
Created July 1, 2023 23:47
Show Gist options
  • Save haikusw/d4c3f56b4d15d892f5b76d0bdfff3f33 to your computer and use it in GitHub Desktop.
Save haikusw/d4c3f56b4d15d892f5b76d0bdfff3f33 to your computer and use it in GitHub Desktop.
VERY WORK-IN-PROGRESS case for Open Book Abridged Edition
// 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