SmolCube — 3D-printed FreeCAD case for the smolboi ORIGIMAGIC Aurora mainboard
  • Python 95.7%
  • OpenSCAD 2.9%
  • Shell 1.4%
Find a file
Ben Reed 379143d163
Rev I: L-bracket corner legs replacing Ø20 pedestals
The old Ø20 coffee-table pedestals blocked M3 fan-screw access —
counterbore centres sat only 2.1 mm from the pedestal centres.
Replaced with L-bracket corner legs: 5 mm thick walls extending the
shroud downward at each corner, R3 filleted outer corners matching
the shroud, arms tapering 15→8 mm, tilted 6° outward via
Part.makeLoft between matching L-profile wires. 8 mm driver-envelope
screw access verified zero intersection at all four positions.
Desk-to-mesh 17.7 mm (was 25.4), total stack 177.5 (was 185.2).

Design reviewed by codex: recommended LEG_H=13 mm, the loft approach
over rotation (flat desk face, no pivot-axis issues), and keeping the
arm taper. Ben chose the corner-becomes-leg style, 15-20 mm clearance,
and a slight outward tilt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Notjr1z31YhQHCDHHRhkYW
2026-09-03 08:33:52 -03:00
docs Rev H base joint fixes: chamfer + grip-tab BOOL_EPS overlaps 2026-09-03 08:21:49 -03:00
images Record JIG-B readings and photos; drop the rev F test-fit photos 2026-09-02 19:05:03 -03:00
print Docs: JIG-B test-jig section, Ben slices in Bambu Studio, drop JIG-A 3mf files 2026-09-02 16:05:25 -03:00
renders Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
stl Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
.gitignore Add headless Bambu Studio slicing and rev E print files 2026-09-02 08:57:36 -03:00
assembly_front.png Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
assembly_full.png Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
base.stl Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
build_review_page.py Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
build_review_page_revC_20260901-2240.py Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
build_review_page_revE_20260902-0035.py Rev F: printability chamfers, rounded ridge and ends, lip/tab clip fix 2026-09-02 09:25:24 -03:00
build_review_page_revG_20260902-1852.py Docs: rev H in README/CLAUDE.md, review page rev H section and signed extents 2026-09-02 19:05:03 -03:00
case_body.stl Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
CLAUDE.md Docs: rev H in README/CLAUDE.md, review page rev H section and signed extents 2026-09-02 19:05:03 -03:00
export_stl.py Export and slice the base parts alongside the halves 2026-09-02 11:10:48 -03:00
front_view.png Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
README.md Docs: rev H in README/CLAUDE.md, review page rev H section and signed extents 2026-09-02 19:05:03 -03:00
rear_view.png Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
render_views.py Rev F: M3 fan inserts, no Oculink slot, base shroud with mesh cover 2026-09-02 11:10:48 -03:00
roof.stl Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
smolboi_case.scad Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
smolboi_case_poc.FCStd Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
smolboi_pcb_model.FCStd Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolboi_pcb_model.py Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolcube_base.FCStd Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
smolcube_base.py Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
smolcube_base_revF_20260902-1124.py Rev G cleanup pass: leg pedestals, boss fillets, clearances, PLA Matte Black 2026-09-02 14:46:46 -03:00
smolcube_base_revG_20260902-2030.py Rev H base joint fixes: chamfer + grip-tab BOOL_EPS overlaps 2026-09-03 08:21:49 -03:00
smolcube_base_revH_20260903-0822.py Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
smolcube_concept.FCStd Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
smolcube_concept.py Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolcube_concept_revC_20260901-2242.py Import SmolCube case project (rev C archived, rev D house draft) 2026-09-01 23:59:54 -03:00
smolcube_concept_revE_20260902-0035.py Rev F: printability chamfers, rounded ridge and ends, lip/tab clip fix 2026-09-02 09:25:24 -03:00
smolcube_concept_revF_20260902-1124.py Rev G cleanup pass: leg pedestals, boss fillets, clearances, PLA Matte Black 2026-09-02 14:46:46 -03:00
smolcube_concept_revG_20260902-1852.py Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolcube_exploded.FCStd Rev I: L-bracket corner legs replacing Ø20 pedestals 2026-09-03 08:33:52 -03:00
smolcube_params.py Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolcube_params_revF_20260902-1124.py Rev G cleanup pass: leg pedestals, boss fillets, clearances, PLA Matte Black 2026-09-02 14:46:46 -03:00
smolcube_params_revG_20260902-1852.py Rev H: JIG-B numbers — straddling I/O, two-sided rear pocket, button flexure 2026-09-02 19:05:03 -03:00
smolcube_test_jig.FCStd Add JIG-B test jig: caliper-datum rear plate replaces the engraved scale 2026-09-02 16:05:25 -03:00
smolcube_test_jig.py Add JIG-B test jig: caliper-datum rear plate replaces the engraved scale 2026-09-02 16:05:25 -03:00
smolcube_test_jig_JIG-A_20260902-1549.py Add JIG-B test jig: caliper-datum rear plate replaces the engraved scale 2026-09-02 16:05:25 -03:00

SmolCube — case for the ORIGIMAGIC Aurora (Ryzen 7 7840HS) mainboard

Two-piece clamshell for the bare smolboi board, printed on a Bambu A1 in PLA Matte Black (220/55). Rev H (2026-09-02, "JIG-B numbers"): the first revision built on measured rear-connector positions — the printed test jig put the rear row +3 mm along the edge and showed the I/O straddles the board (USB-C and both Oculink ports on the heatsink side, dual USB-A and RJ45 crossing it), so the rear pocket continues 7 mm past the seam into the hood, the hood's front wall gets notches too, the board moved 2 mm toward the front wall and the power button is a flexing tab. 128 × 128 box body to an eave at Z 52, 55° gable roof to a R18 rounded ridge at Z 126.5, R8 outlines on the connector faces, R3 on the vertical corners and eaves, floor edges sharp, eight vent slots at the peak (70 % of the fan annulus); 120 mm Noctua bolted under the floor pushing up (Ø116 bore, M3 brass insert bosses with R2 fillets and screw relief, long M3 screws through the base plate), two-part base below it (shroud around the fan with four pedestal-bonded tapered tilted legs

Rev history: A (board offset, open plenum) → B/B2 (centred, hourglass duct) → C (body is the hourglass, fan on top) → D (fan below, gable roof, ridge vent; eave 118, ridge 158, 37.6°) → E (ridge down to 126.5, 55° roof, eave 52) → F (printability chamfers, rounded form, lip/tab fix, base shroud) → G (codex-reviewed cleanup: leg pedestals, boss fillets, screw relief, lip clearance, mesh recess clearance, flange chamfers, wider EST margins, consistent BOOL_EPS/FIT_XY/FIT_Z, PLA Matte Black) → H (JIG-B readings: rear row +3, measured heights, straddling I/O, two-sided pocket, copper as 4.4 gap + 17 block, BOARD_X0 4.0, button flexure, front heatsink-side USB-C/Oculink, MH_X_FRONT 4.5). Rev C is kept as smolcube_concept_revC_20260901-2242.py; rev F as *_revF_20260902-1124.py; rev G as *_revG_20260902-1852.py. The March-2026 OpenSCAD "SmolHouse" (smolboi_case.scad, *.stl) is the roof's ancestor; only its numbers are still used.

Files

File Purpose
smolcube_params.py Every dimension in one place. EST marks pixel-scaled estimates that need a caliper.
smolboi_pcb_model.py PCB mock-up (board, heatsinks, RAM/SSD/M.2, 11 connectors, cable stub). build_pcb(doc, placement) is importable.
smolcube_concept.py Half A tray + Half B hood + fan + standoff placeholders; assembled and exploded documents; layout, airflow, interference, overhang (print orientation, faces > 45° past vertical) and solid-count reports.
smolcube_base.py Two-part base: shroud around the fan continuing the case walls (plate with M3 through-holes, skirt, four tapered legs) and a slide-in mesh cover. build_base(doc, z_offset, cover_pull) is imported by the concept script. Own document smolcube_base.FCStd.
render_views.py GUI render of PNG views into renders/. SMOLCUBE_DOCS=<doc> selects one document.
build_review_page.py Generates the review HTML (renders embedded). Rev-specific prose — edit each revision.
print/slice.shprint/*.3mf Headless Bambu Studio slice of the *_print.stl files for the A1 (0.20 mm, PLA Matte Black 220/55 textured plate, 4 walls, 15 % infill, tree supports on the plate). Presets in print/presets/ are generated by presets/flatten_preset.py (resolves inherits and the machine include templates — the CLI ignores both, which silently dropped the A1's purge/flow-reset/calibration/levelling start gcode from every 3mf before 2026-09-02 evening); print/check_gcode.sh asserts the resolved start sequence, 256×256 bed, filament ID and temps after each slice.
print/presets/ The three flattened Bambu presets the CLI is given: smolcube_machine.json (stock Bambu Lab A1 0.4 nozzle with its inherits chain and five include gcode templates resolved — must keep the stock name and from: system), smolcube_filament.json (stock Bambu PLA Matte @BBL A1, GFA01, bed 55 instead of 65), smolcube_process.json (0.20 mm Standard derived: 4 walls, 15 %, tree supports on the plate, textured plate). Regenerate with `presets/flatten_preset.py <machine
print/check_gcode.sh Post-slice assertions on a 3mf: A1 start block, M221 S100, flow-dynamics cal, G29.2/G29.1, T1000, 256×256 bed, GFA01, 220/55, purge before layer 1. Both slice scripts call it; a failing 3mf is not printable.
export_stl.pystl/ freecad -c export_stl.py < /dev/null writes stl/smolcube_{tray,hood,base_frame,base_cover}.stl (+ _print copies) (binary, case frame, 0.05 mm deflection) from smolcube_concept.FCStd; asserts one solid per half.
smolcube_test_jig.pystl/smolcube_test_jig_* Measuring test jig (JIG-B) — see "Test jig" below; docs/jig/jig-b-readings.md is the readings sheet. Ben slices the _print.stl files in Bambu Studio (print/slice_jig.sh is the headless fallback).
images/ Caliper photos (2026-03-31) and the JIG-B photos (jig-b_*.png, 2026-09-02), the source of truth.
docs/session-prompts/ /next-session-prompt output for resuming across sessions.

Build

freecad -c smolboi_pcb_model.py < /dev/null      # smolboi_pcb_model.FCStd
freecad -c smolcube_concept.py  < /dev/null      # smolcube_concept.FCStd + smolcube_exploded.FCStd
for d in smolcube_concept smolcube_exploded; do
  SMOLCUBE_DOCS=$d DISPLAY=:0 QT_QPA_PLATFORM=xcb timeout 200 freecad render_views.py < /dev/null
done
python3 build_review_page.py /path/to/smolcube_review.html

< /dev/null matters: freecad -c otherwise waits on stdin after the script. Run renders in the foreground (background jobs have been killed externally). The build must report Interference … none and solids=1 for both halves.

Frame

X left→right (front I/O wall at 0, rear I/O at 128), Y front→back (tray face at 0, hood face at 128), Z up (floor at 0, fan below it, eave at 52.2, ridge at 126.5). Board front edge X 4.0, component face at Y=60.45, seam at Y=62.15, board Z 12114. Case-bottom end of the board is the HDMI end. Connector Y extents in smolcube_params.py are signed (positive on the component face, negative past the heatsink face); every notch spans that extent and stays open toward the seam. No Oculink cable slot since rev F (external adapter has its own port).

Printing

Each half goes on the bed exterior wall down (its big Y face): posts, ribs, floor and gable ends print vertical. The roof slope's overhang angle equals its pitch in this orientation: rev D's 37.6° printed unsupported, rev E's 55° needs supports under the roof exterior (45° is the support-free limit; open question on the review page). Seam-face down would make the outer wall a 123 mm unsupported ceiling.

The build prints an overhang report for both halves in this orientation. Rev F (after the first rev E print showed drooping bridges) chamfers everything it listed at 41° (CHAMFER = 1.15 rise/run): under the rear I/O pocket block, the hood closure wall, the ridge tabs and the registration lip. Target for every rev: nothing in the report but the roof slopes, bridges under 10 mm (vent slot walls, cable slot) and the M4 insert holes. Rev F also found that rev D/E clipped the tray at the seam after fusing the lip and tabs, so neither existed on the printed part; the tray is now clipped at seam + tab reach. Rounding rule: edges that are vertical or lean less than the roof in the print (ridge, connector-face outlines) may take any radius; edges on the bed (vertical corners, eaves, floor front/back) stay small or they become first-layer overhangs.

Slicing (headless Bambu Studio)

REV=revG ./print/slice.sh (case parts) and ./print/slice_jig.sh (jig) run the extracted Bambu Studio AppImage (~/Applications/squashfs-root) against the three presets in print/presets/, one part per 3mf, then print/check_gcode.sh on each.

Why the presets are flattened: the CLI applies only a preset file's own keys. It walks neither inherits nor the machine profile's include list (the five … template *_gcode.json files that hold the A1's start/end/change-filament gcode). Given the stock machine JSON by path, every 3mf sliced before the evening of 2026-09-02 (rev EG and the first JIG-A) therefore started with a bare G28 / G1 Z5 — no purge, no M221 S100 flow reset, no flow-dynamics calibration, no bed levelling, no textured-plate Z offset — and declared a 200×200 bed. The first JIG-A print barely extruded because of it. flatten_preset.py resolves both mechanisms; the machine copy has to keep the stock name and from: system or the CLI answers "process not compatible with printer" (error 3002). The filament preset was previously a hand-edited PETG file that still carried filament_id GFG99 (Generic PETG) — regenerate, never hand-edit. The A1 stock sequence spells things unexpectedly (M104 S220 with the M109 waits at calibration temps, G29.2 S1 + G29.4 for levelling), which is what the checker's patterns encode.

What the photos and the jig settled (the old plan had these wrong)

  • The I/O straddles the board (rev H; rev BG wrongly had everything on the component face): HDMI, DP, DC jack, the front USB-As, audio and button stand on the component face; USB-C and both Oculink ports (the "slim" port under the DP is the board's own Oculink; there is a second one under the audio/button on the front) sit wholly on the heatsink side; the dual USB-A and RJ45 cross the board plane. Evidence: the JIG-B tops are only 810 mm above the face, and the March "Rear connectors" photo shows the PCB edge running between the two USB-A ports.
  • The I/O edges are 102 mm long and 105 mm apart. Holes: 96 mm apart along the I/O edges, 83.1 mm front↔rear, ≈4.5 from the front edge.
  • The copper heatsink base stands ≈4.4 mm off the board (CPU package; the sunk ports live in that gap) and its fins reach ≈21.4 below the underside; it overhangs the rear board edge by ~10 mm, so the rear I/O is a recessed pocket 15.5 mm inside the right wall that straddles the seam. Below the board the pocket opens onto the copper gap — the fins are the closure there.
  • Rear row: HDMI z0 8.0 … DC jack z1 94.8 (JIG-B; +3.0 on the pixel-scaled guess).
  • Real connector protrusions are ≈2.53 (USB-A), 1.5 (audio), 1.0 (button), not the 6 the early revs modelled — the "board 34 mm too deep" of the rev F fit.

Still estimates (EST in smolcube_params.py)

Extents of the rear ports below the board (±1), the front heatsink-side USB-C/Oculink positions (±1.5), button/audio ≤1 mm centring, front hole offset (±0.5 — read the jig crosshairs), copper Z extent / overhang, aluminium heatsink footprint, SODIMM stack height, Oculink socket position.

Test jig (JIG-B, 2026-09-02)

The rev F test fit put the rear connectors ~5 mm off along the edge and ~2 mm off in height, and the board 34 mm too deep front-to-back. Rather than iterate the case, smolcube_test_jig.py builds a three-piece measuring fixture (freecad -c smolcube_test_jig.py < /dev/nullstl/smolcube_test_jig_{base,rear,bridge}_print.stl, already in print orientation; Ben slices them in Bambu Studio). JIG-A's engraved 0.6 mm scale would have been invisible on matte-black PLA, so JIG-B (codex-reviewed, two design rounds + a code review) reads with a caliper from hard datum faces; the marks are only a coarse sanity check. Preview: renders/test_jig_JIG-B_preview.png / _outer_face.png.

  • Base — the board lies flat, component face up, on four Ø6 × 22 posts (the copper heatsink hangs below with 3 mm nominal to the table). No pins: each post top carries a crosshair you see through the Ø3.8 hole, so a wrong MH_X_FRONT shows as an offset instead of jamming. The Y=0 rail lip has zero clearance (0.3 on the far side) — bias the board against it; the front wall (SCAD-derived connectors, 1.0 mm cutouts with crosshairs) is the X datum. The 8 mm rails extend past the PCB edge and carry an L-pocket at each rear end for the plate's feet.
  • Rear plate — prints inner face down, everything grows outboard. One window over the full PCB width (Y 0102) up to 4 mm above the tallest nominal connector; open below the board (Y 498) for the copper overhang. Jamb end faces + 12 mm datum pads give two flat Y datum faces at 6 / 108 (≈ 6.2 / 107.8 once the plate is biased into its pocket clearance — calibrate the face-to-lip offset, don't assume 6.0); a top flange makes the plate top a 14.5 mm-deep Z datum; a clean 4 mm band above the window is the seat for X (protrusion) step readings. Enclosed through-slots every 5 mm (longer every 10) and raised numerals 1090 on the outer face are the visual check. Each pad has an open calibration well landing on the rail stub (post height) so the flange-to-board distance is measured, not assumed. Push the plate forward (inner face on the rail ends = PCB rear edge, X=105) and toward Y.
  • Bridge — loose 22.5 × 10 × 4 bar that rests on the plate top and cantilevers over the board so the depth rod can land on a connector body; it cannot see the lower member of a stacked pair (inside-jaw gap for those).
  • Readings and formulas: docs/jig/jig-b-readings.md (calibrate c_low, L, A, T first; Y0 = R_low c_low, Y1 = L c_low R_high, H = A t_pcb D, S = T + g). Ben's 2026-09-02 readings are recorded at the top of that file and applied in rev H. He used the caliper beam end on the datum faces with the depth rod as the probe (no bridge, no calibration pass). Lessons for a JIG-C, if one is ever needed: POST_H ≥ 25 (the copper touched the table at 22), and a window below the board too, so the ports' lower extents can be read — the front wall hid the two heatsink-side front ports entirely.
  • The build asserts: one valid solid per part, every rear connector shifted ±5 mm (Y) / +3 mm (Z) clears the plate, plate ∩ base = 0, numeral gaps ≥ 1.5 mm; the interference script verifies zero PCB ↔ jig intersection. JIG-A is archived as smolcube_test_jig_JIG-A_20260902-1549.py.