print-bench

sbc-case — vented Raspberry Pi 4 case with fan mount

A hardware-rich single-board-computer case (Raspberry Pi 4 primary target) that is deliberately the reference / stress-test design for the assembly-instructions feature (issue #158): it ships an assembly.conf declaring real vitamin: entries — board, inserts, three screw types, washers, fan — so it exercises the NopSCADlib vitamin path, the BOM collection pass, and the GPL-3.0 product-page disclosure no existing design carries.

Overview

Product shot

Product Hero Studio render 1 / 10

Product shot

A two-piece FDM case for the Raspberry Pi 4 (and other NopSCADlib-catalogued SBCs) with a 40 mm intake fan, heat-set inserts at every repeated-assembly point, and cable access through open skirted edges — no connectors ever fight a tight hole. The standoff pattern is generated from the board's own mounting hole list at build time, so pointing board at another PCB re-places the standoffs automatically.

The Raspberry Pi 4 and 40 mm fan are shown for fit — both are purchased separately (see the bill of materials below).

Shown in the case's own charcoal, so the board reads dark; the color notch view lower down shows the green board and gold header pins seated just below the notch lip.

What you get

  • base — the tray: floor, skirted cable edges, four board standoffs (generated from pcb_screw_positions), four through-bored lid-screw posts (~95 × 76 × 26 mm)
  • lid — the flat top: register lip (notched around the lid-screw posts), Ø37 fan aperture, four insert bosses on the inner face, lid-screw holes (~95 × 76 × 2.5 mm plate + 7 mm bosses)
  • sbc-case-coupon — two crops of the same case corner that nest; print it first to tune the fits (~33 × 76 × 26 mm)

Plus the hardware on the bill of materials (see ASSEMBLY.md): 8 × M3 heat-set inserts, 4 × M2.5 screws, 4 × M3 cap screws, 4 × M3 × 20 dome screws + washers, one 40 mm 5 V fan, and four adhesive rubber feet. The screws come out of one standard M2.5 / M3 assortment; add one bag of M3 heat-set inserts, that single fan, and a strip of adhesive feetfour carts, not five separate hardware orders.

  • Material: PETG or ASA
  • Layer height: 0.2 mm
  • Infill: 15 %, gyroid
  • Supports: none needed — both parts are support-free by design
  • Vents: leave supports off — each of the four +Y-wall slots is a 12 mm tunnel whose ceiling bridges only the 2.0 mm wall thickness, so it comes out clean even in PETG; a little ceiling droop inside the slots is normal and faces down into the tunnel — you only notice it from vent height, not from across the desk. ASA — the page's other supports-off material — bridges a touch fuzzier here than PETG; same call, leave supports off, the droop still faces down into the tunnel. Auto-supports here just weld into the tunnels.
  • Orientation: base floor-down as modeled; lid outer-face-down (the rendered part="lid" orientation) — the register lip and fan bosses print as standing features. Print the coupon first (with a brim) — see below.
  • Seam: set it to Back (or a scarf seam). The stock Aligned seam stacks a vertical ridge on the base's show walls, and one stack lands inside the cavity right where the lid lip registers.
  • Plate (PETG): print PETG on a textured sheet — it welds to smooth PEI.
  • Enclosure (ASA): ASA wants an enclosure — the flat 94.5 × 76.2 mm lid and the long open skirt edges lift on a cold chamber. No enclosure → use PETG. And coupon in the material you'll ship: ASA prints the register fit tighter than a PETG coupon suggests (its shrink eats the 0.25 mm fit_clearance budget), so bump fit_clearance a hair before reaching for sandpaper.
  • Lid first layer: enable elephant-foot compensation for the lid. It prints outer-face-down, so its show face is the bed face (the base already carries a 0.6 mm bed chamfer; the lid plate does not). Printing the coupon, which nests the lid and base crops on one plate? A global elephant-foot setting is fine — the base crop's fit is a vertical cavity wall the compensation never touches, and its floor-and-skirt footprint hides the 0.1–0.2 mm shave.
  • Feet: stick four adhesive rubber feet on the floor — the case sits on a flat base, and feet keep it from walking when you plug a cable in one-handed.

The as-printed pose — iso, top, front and bottom-iso of the sliced parts:

Parameters

ParameterDefaultWhat it does
boardRPI4NopSCADlib board type — standoffs follow its hole list
fit_clearance0.25 mmlid register lip vs cavity wall; tune on the coupon
standoff_h5 mmboard standoff height (4–6 per the brief)
interior_h24 mminterior height; sized over the tallest RPI4 connector
fan_typefan40x11fan vitamin; aperture and boss pitch follow it
fan_center(−10, 0) mmfan position; biased toward the SoC
board_clr0.75 mmboard-to-wall clearance
wall / floor_t / lid_t2.0 / 2.0 / 2.5 mmshell thicknesses

All parameters are at the top of sbc-case.scad in Customizer sections; override on the command line with -D 'fit_clearance=0.3'.

Assembly & use

Full bill of materials and step-by-step instructions: ASSEMBLY.md and the exploded view below. The short version: melt the inserts in flush with the post top (see the exploded view), drop the board on (it self-locates on the standoffs), screw it down, seat the lid, bolt the fan on blowing into the case.

If a fit is off, tune it on the coupon (NOTES.md, "Print this first") and reprint only the affected part — the coupon is cropped from the same geometry, so what you feel there is what the full parts do.

Living with it

  • Swap the micro-SD without opening the case. The −X edge is open, so the card — and every cable — should come out with the lid on and every screw untouched — the first real print verifies the card clears the restored rim (the planned field test in NOTES). The 2 a.m. reflash needs no tools.
  • GPIO stays reachable. A notch in the +Y wall clears the 2×20 header, so jumper wires reach the pins with the lid on.
  • Serviceable where it counts. The heat-set inserts sit at the two joints you ever revisit — the lid and the fan — so opening the case or swapping the fan (a sleeve-bearing 40 mm fan is typically a 4–6 year part) never chews a plastic thread. The board screws are the ones you touch once. One catch the fan adds: it lives on the lid, but its lead plugs into the board — so unplug that lead at the header before you lift the lid, or a set-aside lid hangs by two wires.
  • Keep it breathing. The fan blows in through an open-skirted case, so it is an intake for dust too — on an always-on build, blow the fan and the vent row out every few months.

Licensing

This design incorporates parts of NopSCADlib, which is licensed under GPL-3.0: the board, fan, insert and screw definitions it reads at build time and renders in its assembly documentation. The exported case geometry is therefore a GPL-3.0 combined work — you may print and share it freely under those terms, keeping the license and attribution intact. This isolation is deliberate (see docs/licensing.md): designs that do not use NopSCADlib in this repo are unaffected.

Workbench

View in 3D

Inspect the real geometry — drag to rotate, scroll to zoom. The model is rendered from this design's own source at its default settings, right in your browser; nothing is uploaded.

Make it fit

This design has 17 tunable parameters. Change them and render your own STL — OpenSCAD runs in your browser, so nothing is uploaded and nothing is installed.

An STL you configure here is ungated. The files this project ships have each passed a printability check and a PrusaSlicer test-slice; your variant has not. Treat it as a starting point, and print the fit coupon first if the design has one.

History

History of work together

  1. 2026-08-29 commit · git

    render.sh: emit the --render form the installed OpenSCAD build accepts (fixes the render camera opt) (#408)

    Shai Perednik shai
  2. 2026-08-29 commit · git

    Fix teardrop_hole() point orientation to +Z as documented; pin it with a mates manifest (#424)

    Shai Perednik shai
  3. 2026-08-25 commit · git

    sbc-case: reviewer act-now list from #371 (vent re-web + product-page pass) (#397)

    Shai Perednik shai
  4. 2026-08-24 decision · PM.md

    Vent re-web: five 12 mm slots @ 11.5 mm pitch → four @ 15 mm pitch

    0.5 mm overlap fused them into one 58 mm bridge that sags in PETG (Jane, PR #371); 4 @ 15 gives 3 mm webs and real 12 mm bridges at the same span, re-gated 100/100 (NOTES 13)

    Cass cass
  5. 2026-08-24 decision · PM.md

    Round-2 product-page pass (Vera triage of PR #371, merged): product-populated shot, notch camera, print-settings + serviceability + care lines, hero caption

    both reviewers passed at head; act-now was textual + one geometry (vents). Stepped bore, lip chamfer, printed foot, ribbon measurement, exploded size, ports camera → backlog B4–B9

    Cass cass
  6. 2026-08-24 decision · PM.md

    Round 2b (PR #397 pm-triage): notch camera widened dist 170→230 pre-freeze, vents "supports off / PETG droop" line, ASA fits clause, fan-life "typically"

    both reviewers passed again; Vera act-now'd four copy/framing touches. notch widened inside its first review round — the freeze attaches to a shot that has survived a round, so widening now is one line vs a notch-wide companion after merge (a considered interpretation, recorded); SD-swap ergonomics → B10 field-test

    Cass cass
  7. 2026-08-24 decision · PM.md

    B0 — floating skirt (round 2c, PR #397): center = true on the +X/−X/−Y open-edge cuts centred them on skirt_top, floating the wall 16.75 mm over the bed (sbc-case.scad open-edge cuts; NOTES decision 16). Inherited from merged #371 — main carried it until this PR.

    Drik blocked, Vera upheld it as an N4 violation. Found by Drik's round-2b finger-check, missed by every gate and by Jane. Fix = keep center = true (needed for the X/Y straddle — deleting it, as Drik and Vera both proposed, mis-cuts) and lift the Z so the cut floor rests on skirt_top; re-verified skirt restored, base 100/100. Second #69-class escape (7-body rounded_box was the first) → DoD gains a first-real-print row

    Cass cass
  8. 2026-08-24 decision · PM.md

    Round 3 (PR #397 pm-triage): both reviewers pass at head post-B0 (Drik's block cleared, Jane re-signed after making the wall-to-bed check permanent); act-now was page currency + honesty only — the contact-sheet conf row, the false "hidden once the lid is on" droop clause, the SD-swap interim wording, the product-populated caption, the coupon dimension, the ASSEMBLY step-1 trim

    sbc-case.scad frozen (nothing act-now is geometry). The ownerless previews/contact-sheet.png (byte-identical to base 978c9ba, no cameras.conf row so no regen path — still showed the pre-B0 floating skirt) is the third #69-class escape on this design (7-body frame, floating skirt, ownerless exhibit); fix = the bare contact-sheet row so regen owns it. Backlog re-rank: B7 above B5/B6, B8 up a nudge; the multi-color populated render declined as a pipeline property, not this design's to change

    Cass cass
  9. 2026-08-24 decision · PM.md

    Round 4 (PR #397 pm-triage): both reviewers pass at head 42b8bea — the sign-off round round-3 predicted; every round-3 act-now item verified landed. Act-now was two text lines + a free PR-body edit — the README flush clause (short version), the NOTES print-time sync (~5h/2.5h → the gate's 2h46m/1h38m), and the PR body's stale "only geometry change is the vent re-web" line

    sbc-case.scad frozen — the freeze held through rounds 3 and 4 (both page-only), so the geometry verification stamped at 2c/3 is what ships. B10 re-scoped to card + four front-edge plugs (Drik: the RPI4 vitamin draws them top-side, no render witnesses overmold-vs-rim, DOA at minute zero); B4 up (prose is the flush guard in two places now; the stepped bore is the durable fix); B7 holds. Vera did not block a merge on the two passes at this head — the merge is the human's shape call

    Cass cass
  10. 2026-08-24 decision · PM.md

    Round 5 (PR #397 pm-triage): both reviewers pass again at 47bd7ce (2nd consecutive both-pass; geometry frozen since 2c). Act-now = three assembly.conf BOM strings — screw lengths x6/x10/x20 read as quantities beside the Qty column → ×6 mm/×10 mm/×20 mm

    The BOM is this design's N2 deliverable ("assemble exactly as the BOM says"), the strings are ours (authored, not vendored/generator-emitted), so the fix is in-scope and cheap; regen re-emits ASSEMBLY.md. Re-rank: B7 above B4 (Drik's recoverability principle — a sunken insert walks back with a soldering iron; a scraped card / blocked ribbon socket don't; B4 keeps its round-4 why). New B11 — fan-aperture finger guard (Drik [customer-sense], queued: N4 proof + airflow cost checked when built). Freeze held rounds 3–5, page-only throughout. Vera did not block a merge on the two passes at this head

    Cass cass
  11. 2026-08-24 decision · PM.md

    Round 6 (PR #397 pm-triage, head 21325fc): Drik passes; Jane's round at this head did not complete (retry stamp jane: failure during a total LLM-provider outage — her latest pass 47bd7ce stands, one text-only commit behind). Act-now = two N2-surface strings — the README hardware line (the 8 heat-set inserts are a third cart: "three carts, not five", not "one assortment plus a fan") and the assembly.conf insert BOM wording ("4 base lid-screw posts, 4 lid fan bosses"); regen re-emits ASSEMBLY.md

    sbc-case.scad untouched — freeze held rounds 3–6, page-only since 2c. Declined the "dome or cap" BOM change (a hunch never drives a BOM change under N2; the dome rationale recorded in NOTES instead). B8 demote declined (premise disputed by its originator, rank holds, resolves on stranger-assembly evidence). Keep-guards: the SD-swap should asterisk stays until B10 logs; ASSEMBLY steps 1–6 stay verbatim (fan-last avoids the wire pinch). The reviewer-signoff currency call on Jane's one-commit-behind pass is the gate's, not the PM's; Vera did not block a merge

    Cass cass
  12. 2026-08-24 decision · PM.md

    Round 7 (PR #397 pm-triage, head 5cada8f): both reviewers pass — Jane re-ran fresh at 5cada8f (providers recovered, her round-6 stale-signoff cleared), Drik at 9771d3f (one regen-emit behind). Act-now = three text-only strings — the vent line names the 2.0 mm wall span (12 mm is the tunnel length; "~12 mm bridge" could scare a reader into flipping supports on, the failure it guards against — Jane); the ASSEMBLY step-7 fan-lead routing clause (out the GPIO notch to the fan header / 5 V + GND — step 7 had stopped at the mechanical bolt-on, "the gates measure everything except where the electricity goes" — Drik); feet on the README bill ("three carts" → "four carts, not five" — the page's own anti-walk measure wasn't billed)

    sbc-case.scad untouched — freeze held rounds 3–7, page-only since 2c. Declined: assembled-with-fan height (its originator withdrew it); the ASSEMBLY regen-lag (resolved at head — 5cada8f re-emitted the insert row). Queued: step-1 FLUSH reinforces B4; the feet ASSEMBLY row is B6 (a glue-on dot isn't an N2 assembly vitamin — the printed-foot parameter is the durable billable choice). B10 widened again (fan-wire landing + 1 a.m. noise log). notch-camera QA closed by Vera's own eyeball. Vera did not block a merge; the currency call on Drik's one-behind pass is reviewer-signoff's

    Cass cass
  13. 2026-08-24 decision · PM.md

    Round 8 (PR #397 pm-triage, head 5db3e7c): both reviewers pass, fresh at the same head — Jane a formal APPROVE, Drik pass; the PR's first all-confirmation round (every finding a [saw-it]/[used-it] verification or queue-tier keep-guard, zero act-now)

    sbc-case.scad frozen — freeze held rounds 3–8, page-only since 2c. Keep-guards into the charter: the regen-owned contact-sheet row is the print-pose detector for not-touching-the-bed defects (never dropped for render time); the step-1 FLUSH prose survives B4's stepped bore. B9's cell gains the ports footnote — a future connectors-clear-the-rim claim takes a new ports-rim entry, never a reframe. No re-rank

    Cass cass
  14. 2026-08-24 decision · PM.md

    Round 9 (PR #397 pm-triage, head 5db3e7c → push 86a25c2): both reviewers pass; Vera re-triaged the findings that landed after the round-8 triage (Drik's fuller round-8 + Jane's re-run) and ruled three text-only act-nows — (1) the fan-lead tether (the round-7 step-7 routing clause created it: leads to the board, fan on the lid), so step 7 + the README "Serviceable" bullet now say unplug the fan lead at the header before lifting the lid; (2) ASA vent-ceiling clause (ASA bridges the 2 mm ceiling fuzzier than PETG — same "reader mistakes a normal artifact for a defect" class as round 7's vent-span line); (3) coupon global elephant-foot is fine (the base crop's fit is a vertical face the comp never touches)

    sbc-case.scad untouched — freeze held rounds 3–9, page-only since 2c. Queued: the "quieter at night" wording → B10 (the fan40x11 vitamin carries no wire count; the 1 a.m. noise log settles it); the tether's reach-with-slack lived question → B10. Standing rule (Vera): after this batch the page is retired to B10 — page-only findings queue behind the first real print by default, unless they touch a non-negotiable or page honesty. B10 → top of the open rows. Vera did not block a merge

    Cass cass
  15. 2026-08-24 decision · PM.md

    Round 10 (PR #397 pm-triage, head 99fdc73 = CI regen commit-back on push 8ebc344): both reviewers pass again (Jane fresh at 8ebc344, Drik re-run). One act-now — the notch caption honesty fix: the alt text "header reaching through" (README) + the matching previews/CAMERAS.md phrase claimed the 2×20 pins reach through the notch, but the pins top at 16.9 mm while gpio_notch_bottom = 17.5 (sbc-case.scad:77), so they sit ~0.6 mm below the notch lip, behind solid wall — the notch clears a jumper/Dupont housing (19.4 mm, over the 17.5 lip) with the lid on, not the pins. Corroborated: Drik's own §6 misread the exhibit ("pins visibly reach through") — the caption misleading a careful reviewer

    The page-honesty exception the round-9 retirement rule reserves ("unless they touch page honesty"), filed as one by Jane — same class as round 3's false "hidden once the lid is on" clause. Camera stays frozen (don't reframe); sbc-case.scad frozen (geometry sound, only the words were wrong). Queued: shouldered-insert-tip → B4; occupied-notch guard → B7; 2 a.m. by-feel framing → B10; populated-charcoal is intentional (keep-guard). ASSEMBLY.md regen-lag resolved at head. Vera did not block a merge

    Cass cass
  16. 2026-08-24 decision · PM.md

    Round 11 (PR #397 pm-triage, head a220364): both reviewers pass — Jane fresh at a220364, Drik at 99fdc73 (one text-commit behind; currency is reviewer-signoff's). One act-now — a one-word honesty fix: the "Living with it" SD-swap bullet cited "(the field test in NOTES)" as the verification, but NOTES has no ## Field test log yet (that entry is B10, pending) — a stranger following the parenthetical finds an engineering log, not a test → "the planned field test in NOTES" (README). The reserved page-honesty exception again (round-9 retirement rule), same class as round 10's caption / round 3's "hidden once the lid is on"

    sbc-case.scad frozen (geometry unchanged since 2c). Keep-guard: ports.png and top.png stay committed but off-page, both intentional (ports = B9's reserved evidence image — a connectors-clear-the-rim claim takes a new ports-rim entry, never a reframe; top = carried by the contact-sheet's own top view). Drik's "header reaches through" read (his §, reviewed at 99fdc73 before the caption fix) is the third such misread — corrected by evidence (pins ~0.6 mm below the notch lip); the jumper-exit promise it rode is true. Second consecutive both-pass round; the page is retired to B10, and this one word is the last thing the page owed before the print. Vera did not block a merge

    Cass cass
  17. 2026-08-24 decision · PM.md

    Round 12 (PR #397 pm-triage, head 6dcebba): both reviewers pass, fresh at the same head (Drik round 11, Jane round 12 formal APPROVE; both fuse=none) — the PR's first zero-act-now round since round 8, and this time it holds: the round-9 retirement rule blocks any further page act-now (only B10, a physical print, remains)

    sbc-case.scad frozen. Two queue-tier filings: Jane's lid-seating note (the lid seats soft over the 55 mm GPIO-notch span — press over the posts, not the notch; mid-notch flex could whiten a PETG lip edge over ~50 lid-offs) → B10; Drik's exploded-view insert-vs-screw legibility (intentional, the BOM cross-read is the answer) → B8 keep-guard. iso.png byte-checked valid at head (Jane's reader refusal was tool-side). Vera did not block a merge

    Cass cass
  18. 2026-08-24 decision · PM.md

    Round 13 (PR #397 pm-triage, head 6dcebba): both reviewers pass again (Drik round 13, Jane fresh full pass). One act-now — the product-populated caption straggler, the page-honesty exception's third invocation: the italic caption (README) still read "gold header pins through the wall", the round-3 wording that outlived round 10's fix of the same false claim in the notch alt-text + CAMERAS.md — the page contradicted its own corrected exhibit one screen apart, and Drik's image tool made the same misread unprompted → "gold header pins seated just below the notch lip"

    sbc-case.scad frozen (geometry unchanged since 2c); zero preview bytes. Bundled with the carried round-12/13 bookkeeping in one push (Vera's economy: sign-offs reset once, not twice). Queued: the "2 a.m. reflash needs no tools" tagline coupled to B10 (revisit it in the same commit B10 logs its field test); Jane's B11 print-pose constraint — the guard must be a co-planar grille in the lid plate, never a bar across the Ø37 aperture (unprintable both ways); B4 gains that the lid fan-boss bores have their own 1.2 mm web floor (natural melt-in stop — the no-shoulder risk is the base posts only). Vera did not block a merge

    Cass cass
  19. 2026-08-24 commit · git

    Document and enforce vitamin wrapper convention in assembly.sh (#413)

    Shai Perednik shai
  20. 2026-08-24 commit · git

    Add the missing product-manager characters to the People page (#381)

    Shai Perednik shai
  21. 2026-08-24 commit · git

    Add design: sbc-case — hardware-rich Raspberry Pi 4 case (assembly-docs stress test) (#371)

    Shai Perednik shai
  22. 2026-08-23 decision · PM.md

    Fan = fan40x11 (catalog)

    brief's 40×10 assumption explicitly swappable; 40×11 is the vendored catalog's 40 mm fan

    Cass cass
  23. 2026-08-23 decision · PM.md

    Board fastener = M2.5 cap screws into printed pilots

    brief claimed no M2.5 insert exists — F1BM2p5 does exist but is 5.8 mm long, taller than the 5 mm standoff; measurement over brief

    Cass cass
  24. 2026-08-23 decision · PM.md

    Port strategy = three open skirted edges

    brief's own time-boxed default (Open question 2); modeled cutouts → B1

    Cass cass
  25. 2026-08-23 decision · PM.md

    Fan in the lid, intake, biased to the SoC

    brief assumption (Open question 3); side-wall fan never ranked

    Cass cass
  26. 2026-08-23 decision · PM.md

    Fan stays in the reference BOM

    brief Open question 4, assumed yes

    Cass cass
  27. 2026-08-23 decision · PM.md

    assembly.conf vitamin lines name design-file wrapper modules, not raw constants

    assembly.sh's generated preview uses the design file — variables don't cross use, so pcb(RPI4) aborts; measured, filed as #368

    Cass cass
  28. 2026-08-23 decision · PM.md

    Base shell drawn origin-centred (translate around rounded_box)

    rounded_box is corner-anchored; un-translated it split the base into 7 disjoint bodies that every presence-only gate passed (printcheck 92 "fine if intentional", clean slice, empty fitchecks) — caught only by measuring the export, issue #69's class; fixed and re-measured 1 body, 100/100

    Cass cass