Raspberry Pi Case
A two-part case for the Raspberry Pi 4 (and 3B+), Pi 5 or Pi Zero. The board drops onto four standoffs, held by pins through its mounting holes or by M2.5 self-tapping screws, and the walls have a cutout for every connector of the chosen board: USB-C, the micro HDMI pair, audio, the USB stacks and Ethernet, plus the microSD slot under the board. The lid has an opening over the GPIO header, ventilation slots and an inner lip that presses into the tray. The Pi 4 case is 91 x 62 x 28 mm; the print layout with the lid beside the tray is 91 x 132 x 26 mm. Both parts print open side up with no supports (the tall USB and Ethernet cutouts run up to the rim and are closed by the lid). Wall thickness, lip clearance and the vents are parameters, and a closed view shows the lid on the case.
# Raspberry Pi Case - two-part case for the Pi 4, Pi 5 or Pi Zero with every port cut out
#
# The board drops onto four standoffs (pins through the mounting holes, or holes
# for M2.5 self-tapping screws) inside a tray; the port cutouts in the walls, the
# SD card slot and the GPIO opening in the lid come from a table per board. The
# lid has an inner lip that presses into the tray and ventilation slots. Both parts
# print open side up with no supports: the tall USB and Ethernet cutouts are open
# to the rim, so no bridges are needed, and the lid closes them.
#
# Board origin: x along the long edge from the SD card end, y across from the
# USB-C / HDMI edge. The case is built around that origin.
@param choices:["pi4", "pi5", "zero"] group:"Board" desc:"Raspberry Pi 4 (and 3B+), Pi 5, or Zero / Zero 2 W"
$board = "pi4"
@param choices:["pins", "screws"] group:"Board" desc:"Pins through the mounting holes, or holes for M2.5 self-tapping screws"
$fix = "pins"
@param 1.6..3 step:0.2 group:"Case" desc:"Wall thickness (mm)"
$wall = 2
@param 0.1..0.3 step:0.05 group:"Case" desc:"Lid lip clearance per side (mm, smaller is tighter)"
$fit = 0.15
@param group:"Case" desc:"Ventilation slots in the lid"
$vents = true
@param choices:["print", "closed"] group:"Output" desc:"Print layout, or the lid on the case"
$view = "print"
$is4 = $board == "pi4"
$is5 = $board == "pi5"
$zero = $board == "zero"
# --- board table ---------------------------------------------------------
$L = if $zero then 65 else 85
$W = if $zero then 30 else 56
$hx = 58 # hole spacing along X (all three boards)
$hy = if $zero then 23 else 49
$hole_off = 3.5
$pcb_t = 1.5
$tall = if $zero then 9 else 16 # tallest part above the board (USB stack / GPIO header)
$gpio_x = 32.5 # GPIO header centre (40 pins, 50.8 long)
$gpio_y = $W - 3.5
$clr = 1 # board to inner wall
$floor_t = 2
$so_h = 4 # standoff height; the SD card slot is in that space
$z_b = $floor_t + $so_h # board underside
$z_t = $z_b + $pcb_t # board top
$h_in = $so_h + $pcb_t + $tall + 2
$rim = $floor_t + $h_in # top of the tray walls
$in_w = $L + 2 * $clr
$in_d = $W + 2 * $clr
$out_w = $in_w + 2 * $wall
$out_d = $in_d + 2 * $wall
$cx = $L / 2
$cy = $W / 2
$corner = 3
def rr($w, $h, $r) = rect ($w - 2 * $r) ($h - 2 * $r) | offset $r
# --- port cutouts: a box through a wall. $h is the connector height above the board;
# --- tall ones are opened up to the rim ---------------------------------------
$pad = 1.5 # clearance each side of a connector
def cut_h($h) = if $h >= 10 then ($rim + 1 - ($z_t - 0.5)) else ($h + 2 * $pad)
# wall at y = 0 (USB-C / HDMI side): centre x, width, height
def front($x, $w, $h) = box ($w + 2 * $pad) ($wall + 2 * $clr + 0.2) cut_h($h) center:(true, true, false) | translate $x (-$clr - $wall / 2) ($z_t - 0.5)
# wall at x = L (USB / Ethernet end)
def end($y, $w, $h) = box ($wall + 2 * $clr + 0.2) ($w + 2 * $pad) cut_h($h) center:(true, true, false) | translate ($L + $clr + $wall / 2) $y ($z_t - 0.5)
# SD card slot in the x = 0 wall, under the board
def sd($y) = box ($wall + 2 * $clr + 0.2) 16 ($so_h - 0.5) center:(true, true, false) | translate (-$clr - $wall / 2) $y ($floor_t + 0.5)
$cuts = if $is4 then union [front(11.2, 9, 3.5), front(26, 7.5, 3.5), front(39.5, 7.5, 3.5), front(53.5, 7, 6.5),
end(9, 13.5, 16), end(27, 13.5, 16), end(45.75, 16, 13.5), sd(28)]
else if $is5 then union [front(11.2, 9, 3.5), front(25.8, 7.5, 3.5), front(39.2, 7.5, 3.5),
end(10.25, 16, 13.5), end(29, 13.5, 16), end(47, 13.5, 16), sd(28)]
else union [front(12.4, 11.5, 3.5), front(41.4, 8, 3), front(54, 8, 3), sd(16.9)]
# --- tray ---------------------------------------------------------------
$outer = rr($out_w, $out_d, $corner) | extrude $rim | translate $cx $cy 0
$inner = rr($in_w, $in_d, $corner - $wall) | extrude $h_in | translate $cx $cy $floor_t
$shell = $outer - $inner
$pin_d = 2.4 # through a 2.75 mm hole
$screw_d = 2.2 # M2.5 self-tapping
def at_holes($s) = union [$s | translate ($hole_off + $i % 2 * $hx) ($hole_off + floor($i / 2) * $hy) 0 for $i in range(4)]
$so1 = cylinder 3 $so_h | floor | translate 0 0 $floor_t
$standoffs = at_holes($so1)
$pin1 = cylinder ($pin_d / 2) ($pcb_t + 0.5) | floor | faces >Z | chamfer 0.5 | translate 0 0 $z_b
$pins = at_holes($pin1)
$sh1 = cylinder ($screw_d / 2) ($so_h + 1) | floor | translate 0 0 ($floor_t - 0.4)
$screw_holes = at_holes($sh1)
$tray0 = $shell + $standoffs
$tray1 = if $fix == "pins" then ($tray0 + $pins) else ($tray0 - $screw_holes)
$tray = $tray1 - $cuts
assert $tray.solids == 1
assert $tray.bbox.x ~ $out_w
assert $tray.bbox.y ~ $out_d
assert $tray.bbox.z ~ $rim
assert $rim > $z_t + $tall # the lid clears the tallest connector
print "case outer (mm)" [$out_w, $out_d, $rim + 2]
# --- lid: plate, inner lip, GPIO opening, vent slots. Built lid-side down (print pose) --
$lid_t = 2
$lip_h = 2
$lid_plate = rr($out_w, $out_d, $corner) | extrude $lid_t
$lip_out = rr($in_w - 2 * $fit, $in_d - 2 * $fit, $corner - $wall) | extrude $lip_h | translate 0 0 $lid_t
$lip_in = rr($in_w - 2 * $fit - 2 * $wall, $in_d - 2 * $fit - 2 * $wall, 1) | extrude ($lip_h + 1) | translate 0 0 $lid_t
$lip = $lip_out - $lip_in
$gpio = box 55 9 ($lid_t + 2) center:(true, true, false) | translate ($gpio_x - $cx) ($gpio_y - $cy) -1
$n_vent = if $zero then 4 else 8
$vent_len = $W - 20
$vent = union [box 2 $vent_len ($lid_t + 2) center:(true, true, false) | translate (-$cx + 12 + $i * 4) (-4) -1 for $i in range($n_vent)]
$lid0 = ($lid_plate + $lip) - $gpio
$lid = if $vents then ($lid0 - $vent) else $lid0
assert $lid.solids == 1
assert $lid.bbox.z ~ ($lid_t + $lip_h)
# --- closed view: lid flipped onto the tray ----------------------------------------
$lid_on = $lid | rotate 180 0 0 | translate $cx $cy ($rim + $lid_t)
assert clear($tray, $lid_on)
$closed = union [$tray, $lid_on]
assert $closed.bbox.z ~ ($rim + $lid_t)
$print = union [$tray, $lid | translate $cx ($cy + $out_d + 8) 0]
assert $print.solids == 2
if $view == "closed" then $closed else $print