diff --git a/rack.bracket/LICENSE.txt b/rack.bracket/LICENSE.txt new file mode 100644 index 0000000..c04ad7a --- /dev/null +++ b/rack.bracket/LICENSE.txt @@ -0,0 +1 @@ +This thing was created by Thingiverse user russross, and is licensed under cc-nc. \ No newline at end of file diff --git a/rack.bracket/README.txt b/rack.bracket/README.txt new file mode 100644 index 0000000..50945af --- /dev/null +++ b/rack.bracket/README.txt @@ -0,0 +1 @@ +Raspberry Pi 4 2U rack-mount bracket by russross on Thingiverse: https://www.thingiverse.com/thing:4078710 \ No newline at end of file diff --git a/rack.bracket/raspberry-pi-rack-ears.scad b/rack.bracket/raspberry-pi-rack-ears.scad new file mode 100644 index 0000000..e52ea62 --- /dev/null +++ b/rack.bracket/raspberry-pi-rack-ears.scad @@ -0,0 +1,221 @@ +/* [Bolt holes in the corners] */ + +// Pick the size of the long threaded rods that run through +// the corners and hold everything together. +// +// These are the sizes that are commonly available at +// Home Depot or Lowes: +// +// 1/4 inch: 3.175 +// #12 : 2.778125 +// #10 : 2.38125 (default and recommended) +// +// Note: you should pick the same size and fudge factor +// for the frame parts. +bolt_size = 2.38125; + +// Make the bolt holes this much bigger than the size of the bolt +// to give a more comfortable fit +bolt_hole_fudge = 0.2; // [0:0.25:1] + + +/* [Notches for attaching to the frame parts] */ + +// You can optionally omit the notches for one ear that will +// join with a frame on the side that has no tabs. +// For the the other you should leave this at its default of true. +notches_enabled = true; + +// Make the slot this much taller than the tabs +// to give a more comfortable fit. +notch_fudge = 0.3; // [0:0.25:1] + + +/* [Bolt holes for mounting to the rack] */ + +// The size of the bolt holes for mounting to the server rack. +// The default is for M6 bolts. +ear_bolt_size = 3; + +// Make the ear bolt holes this much longer than they are wide +// in case the entire assembled rack is not precisely the right +// length. +ear_bolt_stretch = 3; + + +/* [Hidden] */ + +width = 82; +length = 90; +height = 5; +frame_width = 15.875; +distance_to_frame = 10; +distance_between_bolt_holes = 15.875 * 2 + 12.7; +ear_height = distance_to_frame + frame_width; + +notch_height = 2; +outer_wall_thickness = 12; +inner_wall_thickness = 7; +back_wall_thickness = 5; +wall_diff = outer_wall_thickness - inner_wall_thickness; +floor_depth = 3; +tray_depth = 5; +tray_slot_depth = 2.5; +spacer_depth = 3; +epsilon = 0.001; + +sd_window_width = 40; +sd_window_height = height - (tray_depth + floor_depth)*2; +near_pillar_width = 15; +far_pillar_width = 10; +floor_window_width = sd_window_width; +floor_window_border = (width - floor_window_width - outer_wall_thickness*2) / 2; +floor_window_length = length - back_wall_thickness - floor_window_border*2; +port_window_width = length - near_pillar_width - far_pillar_width; +port_window_frame_height = floor_depth + tray_depth + spacer_depth - notch_height; + +bolt_radius = bolt_size + bolt_hole_fudge; + +ear_bolt_radius = ear_bolt_size + bolt_hole_fudge; +floor_mini_window_width = (floor_window_width - floor_window_border) / 2; +floor_mini_window_length = (floor_window_length - floor_window_border) / 2; + +difference() { + union() { + // the main tray block + translate([0, -epsilon, 0]) { + cube([width, length + epsilon, height]); + } + + // the ear + translate([0, -height, 0]) { + cube([width, height, ear_height]); + } + + // reinforce the joint + translate([0, 0, 0]) { + rotate([45, 0, 0]) { + cube([width, height-0.5, height-0.5]); + } + } + + translate([ (width - distance_between_bolt_holes)/2 - ear_bolt_radius, 0, 0]) { + rotate([45, 0, 0]) { + cube([distance_between_bolt_holes + 2*ear_bolt_radius, height+1, height+1]); + } + } + } + + // cut the bolt holes + for (a=[[outer_wall_thickness/2, near_pillar_width/2], + [outer_wall_thickness/2, length - far_pillar_width/2], + [width - outer_wall_thickness/2, near_pillar_width/2], + [width - outer_wall_thickness/2, length - far_pillar_width/2]]) { + translate([a[0], a[1], -epsilon]) { + cylinder( h=height + 2*epsilon, + r=bolt_radius, + center=false, + $fn=360); + } + } + + // make the sides thinner + polyhedron( + points = [ + [ wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon], + [ wall_diff, near_pillar_width+wall_diff, height+notch_height+epsilon], + [ wall_diff, length-far_pillar_width-wall_diff, -epsilon], + [ wall_diff, near_pillar_width+wall_diff, -epsilon], + [-wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon], + [-wall_diff, near_pillar_width-wall_diff, height+notch_height+epsilon], + [-wall_diff, length-far_pillar_width+wall_diff, -epsilon], + [-wall_diff, near_pillar_width-wall_diff, -epsilon] + ], + faces = [ + [1,0,2,3], + [4,5,7,6], + [0,4,6,2], + [6,7,3,2], + [7,5,1,3], + [5,4,0,1] + ], + convexity = 10); + polyhedron( + points = [ + [width-wall_diff, near_pillar_width+wall_diff, height+notch_height+epsilon], + [width-wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon], + [width-wall_diff, near_pillar_width+wall_diff, -epsilon], + [width-wall_diff, length-far_pillar_width-wall_diff, -epsilon], + [width+wall_diff, near_pillar_width-wall_diff, height+notch_height+epsilon], + [width+wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon], + [width+wall_diff, near_pillar_width-wall_diff, -epsilon], + [width+wall_diff, length-far_pillar_width+wall_diff, -epsilon] + ], + faces = [ + [1,0,2,3], + [4,5,7,6], + [0,4,6,2], + [6,7,3,2], + [7,5,1,3], + [5,4,0,1] + ], + convexity = 10); + + if (notches_enabled) { + // cut the notches + translate([ wall_diff - epsilon, + near_pillar_width + wall_diff, + -epsilon]) { + cube([ inner_wall_thickness-2 + epsilon, + length - near_pillar_width - far_pillar_width - 2*wall_diff, + notch_height + notch_fudge + epsilon]); + } + translate([ width - wall_diff - (inner_wall_thickness-2), + near_pillar_width + wall_diff, + -epsilon]) { + cube([ inner_wall_thickness-2 + epsilon, + length - near_pillar_width - far_pillar_width - 2*wall_diff, + notch_height + notch_fudge + epsilon]); + } + } + + // cut some mini windows in the floor + for (a=[[outer_wall_thickness + floor_window_border, + floor_window_border], + [width - outer_wall_thickness - floor_window_border - floor_mini_window_width, + floor_window_border], + [outer_wall_thickness + floor_window_border, + 2*floor_window_border + floor_mini_window_length], + [width - outer_wall_thickness - floor_window_border - floor_mini_window_width, + 2*floor_window_border + floor_mini_window_length]]) { + translate([a[0], a[1], -epsilon]) { + cube([ floor_mini_window_width, + floor_mini_window_length, + height + 2*epsilon]); + } + } + + // cut the bolt holes on the ear + for (a=[ width/2 - distance_between_bolt_holes/2, + width/2 + distance_between_bolt_holes/2]) { + for (b=[-ear_bolt_stretch/2, ear_bolt_stretch/2]) { + translate([ a, + -height-epsilon, + ear_height - frame_width/2 + b]) { + rotate([-90,0,0]) { + cylinder( h=height + 2*epsilon, + r=ear_bolt_radius, + center=false, + $fn=360); + } + } + } + translate([ a - ear_bolt_radius, + -height-epsilon, + ear_height - frame_width/2 - ear_bolt_stretch/2]) { + cube([ ear_bolt_radius*2, + height + 2*epsilon, + ear_bolt_stretch]); + } + } +} \ No newline at end of file diff --git a/rack.bracket/raspberry-pi-rack-frame.scad b/rack.bracket/raspberry-pi-rack-frame.scad new file mode 100644 index 0000000..1b8a188 --- /dev/null +++ b/rack.bracket/raspberry-pi-rack-frame.scad @@ -0,0 +1,255 @@ +/* [Bolt holes in the corners] */ + +// Pick the size of the long threaded rods that run through +// the corners and hold everything together. +// +// These are the sizes that are commonly available at +// Home Depot or Lowes: +// +// 1/4 inch: 3.175 +// #12 : 2.778125 +// #10 : 2.38125 (default and recommended) +// +// Note: you should pick the same size and fudge factor +// for the ear parts. +bolt_size = 2.38125; + +// Make the bolt holes this much bigger than the size of the bolt +// to give a more comfortable fit +bolt_hole_fudge = 0.2; // [0:0.25:1] + + +/* [Notches and tabs for stacking] */ + +// You can optionally omit the notches for the frame on the end +// that joins with an ear with no tabs. For all of the others +// you should leave this at its default of true. +notches_enabled = true; + +// Make the slot this much taller than the tabs +// to give a more comfortable fit. +notch_fudge = 0.3; // [0:0.25:1] + + +/* [Size] */ + +// The default is to fit 12 units with PoE hats in a standard rack. +// 12 is a good fit, and it works nicely with many PoE switches, which +// often come with 12 or 24 ports. You can bump it up to 13 units and +// they will still fit nicely with PoE hats, or you can go up to 14 +// of you are not using the hats. +number_to_fit = 12; // [12:Relaxed fit, 13:Using PoE, 14:Without PoE hats] + +/* [Hidden] */ +// make the screw holes this much bigger than the +// actual screw for a more comfortable fit +// (a bigger number here will make the screws fit looser) +screw_hole_fudge = 0.15; + +height = (450.85 - 20) / number_to_fit; + +width = 82; +length = 90; + +// The height of the notches and tabs +notch_height = 2; + +// epsilon is used to slightly overlap pieces to help the previewer +epsilon = 0.001; + +bolt_radius = bolt_size + bolt_hole_fudge; + +outer_wall_thickness = 12; +inner_wall_thickness = 7; +back_wall_thickness = 5; +wall_diff = outer_wall_thickness - inner_wall_thickness; +floor_depth = 3; +tray_depth = 5; +tray_slot_depth = 2.5; +spacer_depth = 3; + +sd_window_width = 40; +sd_window_height = height - (tray_depth + floor_depth)*2; +near_pillar_width = 15; +far_pillar_width = 10; +floor_window_width = sd_window_width; +floor_window_border = (width - floor_window_width - outer_wall_thickness*2) / 2; +floor_window_length = length - back_wall_thickness - floor_window_border*2; +port_window_width = length - near_pillar_width - far_pillar_width; +port_window_frame_height = floor_depth + tray_depth + spacer_depth - notch_height; + +difference() { + union() { + // the main block + cube([width, length, height]); + + // the tabs + translate([ wall_diff, + near_pillar_width + wall_diff, + height - epsilon]) { + cube([ inner_wall_thickness - 2, + length - near_pillar_width - far_pillar_width - wall_diff*2, + notch_height + epsilon]); + } + translate([ width - outer_wall_thickness + 2, + near_pillar_width + wall_diff, + height - epsilon]) { + cube([ inner_wall_thickness - 2, + length - near_pillar_width - far_pillar_width - wall_diff*2, + notch_height + epsilon]); + } + } + + // carve out the interior + translate([outer_wall_thickness, -epsilon, floor_depth]) { + cube( [width - outer_wall_thickness*2, + length - back_wall_thickness + epsilon, + height + notch_height - floor_depth + epsilon]); + } + + // punch a hole for the sd card + translate([ (width - sd_window_width) / 2, + length - back_wall_thickness - epsilon, + tray_depth + floor_depth]) { + cube([ sd_window_width, + back_wall_thickness + 2*epsilon, + height - port_window_frame_height - tray_depth - floor_depth + notch_height]); + } + + // punch a hole for side port access + translate([ -epsilon, + near_pillar_width, + port_window_frame_height + notch_height]) { + cube([ width + 2*epsilon, + port_window_width, + height - port_window_frame_height*2]); + } + + // punch a hole in the bottom + translate([ outer_wall_thickness + floor_window_border, + floor_window_border, + -epsilon]) { + cube([ floor_window_width, + floor_window_length, + floor_depth + 2*epsilon]); + } + + // open a groove that the speaker jack can slide through + translate([ outer_wall_thickness - 2, + -epsilon, + port_window_frame_height + notch_height]) { + cube([ 2 + epsilon, + near_pillar_width + 2*epsilon, + 8]); + } + + // soften the leading edge a bit + translate([ outer_wall_thickness, + 0, + floor_depth-0.5]) { + rotate([25,0,0]) { + cube([ width - outer_wall_thickness*2, + 2, + 2]); + } + } + + if (notches_enabled) { + // cut the notches in the bottom + translate([ -epsilon, + near_pillar_width + wall_diff, + -epsilon]) { + cube([ outer_wall_thickness - 2 + epsilon, + length - near_pillar_width - far_pillar_width - wall_diff*2, + notch_height + notch_fudge + epsilon]); + } + translate([ width - outer_wall_thickness + 2, + near_pillar_width + wall_diff, + -epsilon]) { + cube([ outer_wall_thickness - 2 + epsilon, + length - near_pillar_width - far_pillar_width - wall_diff*2, + notch_height + notch_fudge + epsilon]); + } + } + + // cut out the sides to make them thinner + polyhedron( + points = [ + [ wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon], + [ wall_diff, near_pillar_width+wall_diff, height+notch_height+epsilon], + [ wall_diff, length-far_pillar_width-wall_diff, -epsilon], + [ wall_diff, near_pillar_width+wall_diff, -epsilon], + [-wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon], + [-wall_diff, near_pillar_width-wall_diff, height+notch_height+epsilon], + [-wall_diff, length-far_pillar_width+wall_diff, -epsilon], + [-wall_diff, near_pillar_width-wall_diff, -epsilon] + ], + faces = [ + [1,0,2,3], + [4,5,7,6], + [0,4,6,2], + [6,7,3,2], + [7,5,1,3], + [5,4,0,1] + ], + convexity = 10); + polyhedron( + points = [ + [width-wall_diff, near_pillar_width+wall_diff, height+notch_height+epsilon], + [width-wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon], + [width-wall_diff, near_pillar_width+wall_diff, -epsilon], + [width-wall_diff, length-far_pillar_width-wall_diff, -epsilon], + [width+wall_diff, near_pillar_width-wall_diff, height+notch_height+epsilon], + [width+wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon], + [width+wall_diff, near_pillar_width-wall_diff, -epsilon], + [width+wall_diff, length-far_pillar_width+wall_diff, -epsilon] + ], + faces = [ + [1,0,2,3], + [4,5,7,6], + [0,4,6,2], + [6,7,3,2], + [7,5,1,3], + [5,4,0,1] + ], + convexity = 10); + + // drill the bolt holes + for (a=[[outer_wall_thickness/2, near_pillar_width/2], + [outer_wall_thickness/2, length - far_pillar_width/2], + [width - outer_wall_thickness/2, near_pillar_width/2], + [width - outer_wall_thickness/2, length - far_pillar_width/2]]) { + translate([a[0], a[1], -epsilon]) { + cylinder( h=height + notch_height + 2*epsilon, + r=bolt_radius, + center=false, + $fn=360); + } + } + + // cut the tray insert slots + // note: the tray will be slightly narrower to + // give a more comfortable fit. If it is too loose or too tight, + // adjust the tray. + for (a=[ outer_wall_thickness, + width - outer_wall_thickness]) { + translate([ a, + -epsilon, + floor_depth + tray_depth/2]) { + intersection() { + rotate([0, 45, 0]) { + translate([-tray_depth/sqrt(2)/2, 0, -tray_depth/sqrt(2)/2]) { + cube([ tray_depth/sqrt(2), + length - back_wall_thickness + epsilon, + tray_depth/sqrt(2)]); + } + } + translate([-tray_depth/2+0.5, 0, -tray_depth/2]) { + cube([ tray_depth-1, + length - back_wall_thickness + epsilon, + tray_depth]); + } + } + } + } +} diff --git a/rack.bracket/raspberry-pi-rack-tray.scad b/rack.bracket/raspberry-pi-rack-tray.scad new file mode 100644 index 0000000..48d53b0 --- /dev/null +++ b/rack.bracket/raspberry-pi-rack-tray.scad @@ -0,0 +1,196 @@ +/* [Screw holes for attaching the Raspberry Pi 4] */ + +// Make the screw holes this much bigger than the +// actual screw for a more comfortable fit +// (a bigger number here will make the screws fit looser) +screw_hole_fudge = 0.15; // [0:0.05:0.5] + +/* [Tray width] */ + +// Make the tray this much narrower on each side +// for a more comfortable fit in the frame +// (a bigger number here will make the fit looser) +tray_insert_fudge = 0.25; // [0:0.05:0.75] + +/* [Hidden] */ + +tray_width = 58; +tray_length = 85; +tray_depth = 5; +tray_lip_overhang = 10; +inner_wall_thickness = 5; +spacer_depth = 3; +spacer_outer_radius = 3; + +screw_radius = 1.25 + screw_hole_fudge; +screw_head_radius = 3; +screw_head_depth = 2; +pcb_depth = 2; +spacer_center_width = 49; +spacer_center_length = 58; +spacer_from_edge = (tray_width - spacer_center_width) / 2; + +sd_window_width = 40; +floor_window_width = sd_window_width; +floor_window_border = (tray_width - floor_window_width) / 2; +floor_window_length = tray_length - floor_window_border*2 - inner_wall_thickness; + +epsilon = 0.001; + +difference() { + union() { + // the main tray + intersection() { + union() { + translate([tray_insert_fudge, 0, 0]) { + cube([ tray_width - 2*tray_insert_fudge, + tray_length + tray_lip_overhang, + tray_depth]); + } + + difference() { + // the outer part of the curved handle + translate([ tray_width/2, + -tray_length*0.5 + 7.5, + 0]) { + cylinder( + h=tray_depth + spacer_depth + pcb_depth, + r=tray_length*1.5, + center=false, + $fn=360); + } + + // cut away the inside to make it a shell + translate([ tray_width/2, + -tray_length*0.5 + 5.4, + -epsilon]) { + cylinder( + h=tray_depth + spacer_depth + pcb_depth + 2*epsilon, + r=tray_length*1.5, + center=false, + $fn=360); + } + translate([ tray_width, + -tray_length, + -epsilon]) { + cube([ 2*tray_width, + 3*tray_length, + tray_depth + spacer_depth + pcb_depth + 2*epsilon]); + } + translate([ -2*tray_width, + -tray_length, + -epsilon]) { + cube([ 2*tray_width, + 3*tray_length, + tray_depth + spacer_depth + pcb_depth + 2*epsilon]); + } + } + + // reinforce the curved handle + translate([epsilon, tray_length, tray_depth]) { + rotate([-75, 0, 0]) { + cube([tray_width - 2*epsilon, 2*sqrt(2), 6]); + } + } + + } + + // cut away the ears of the tray + // where they jut through the curved handle + translate([ tray_width/2, + -tray_length*0.5 + 7.5, + 0]) { + cylinder( + h=tray_depth + spacer_depth + pcb_depth, + r=tray_length*1.5, + center=false, + $fn=360); + } + } + + // place the 4 spacers + for (a=[ [spacer_from_edge, + spacer_from_edge], + [spacer_from_edge + spacer_center_width, + spacer_from_edge], + [spacer_from_edge, + spacer_from_edge + spacer_center_length], + [spacer_from_edge + spacer_center_width, + spacer_from_edge + spacer_center_length]]) { + translate([a[0], a[1], tray_depth-epsilon]) { + // place the spacer + cylinder( + h=spacer_depth + epsilon, + r=spacer_outer_radius, + center=false, + $fn=360); + + // place a small cone around it + cylinder( + h=2 + epsilon, + r1=spacer_outer_radius + 1, + r2=spacer_outer_radius, + center=false, + $fn=360); + } + } + + // the tray insert edge tabs + for (a=[ tray_insert_fudge, + tray_width - tray_insert_fudge]) { + translate([ a, + 0, + tray_depth/2]) { + intersection() { + rotate([0, 45, 0]) { + translate([-tray_depth/sqrt(2)/2, 0, -tray_depth/sqrt(2)/2]) { + cube([ tray_depth/sqrt(2), + tray_length, + tray_depth/sqrt(2)]); + } + } + translate([-tray_depth/2+0.5, 0, -tray_depth/2]) { + cube([ tray_depth-1, + tray_length, + tray_depth]); + } + } + } + } + } + + // punch a hole in the bottom + translate([ floor_window_border, + floor_window_border, + -epsilon]) { + cube([ floor_window_width, + floor_window_length, + tray_depth + 2*epsilon]); + } + + // drill the 4 screw holes + for (a=[ [spacer_from_edge, + spacer_from_edge], + [spacer_from_edge + spacer_center_width, + spacer_from_edge], + [spacer_from_edge, + spacer_from_edge + spacer_center_length], + [spacer_from_edge + spacer_center_width, + spacer_from_edge + spacer_center_length]]) { + translate([a[0], a[1], -epsilon]) { + // drill the main screw hole + cylinder( + h=tray_depth + spacer_depth + 2*epsilon, + r=screw_radius, + center=false, + $fn=360); + + // drill a recessed hole for the screw head + cylinder( + h=screw_head_depth + epsilon, + r=screw_head_radius, + center=false, + $fn=360); + } + } +} \ No newline at end of file