Read this model as text
A stackable case for a Mini-ITX NAS board, sized so every part prints on a 180 mm bed (Bambu A1 mini). The stack is a base module that carries the board, one identical drive module per flat 3.5" disk, and a lid with a fan that pulls air up through the whole stack. Modules locate on each other with a lip and pocket, so adding a disk later is printing one more drive module and dropping it in.
The image shows the solid OpenSCAD renders at part assembly · hdd_modules 2 · explode 0 · show_hardware true · board 170 · board_clear 0.5 · standoff_h 6.5 · standoff_hole 2.6 · hole_rear_a 6.35 · hole_rear_b 163.83 · hole_side_a 10.16 · hole_side_b 165.1 · board_h 60 · button_d 16 · button_x 30 · button_z 36 · jack_d 12 · jack_side right · jack_y 140 · jack_z 30 · vents true · vent_w 2 · vent_pitch 6 · hdd_w 101.6 · hdd_l 147 · hdd_h 26.1 · hdd_gap 3 · hdd_cable 22 · hdd_clear 0.4 · fan_size 80 · fan_t 25 · io_x 0 · io_w 170 · io_h 20 · wall 3 · floor_t 2.5 · top_t 2.5 · corner_r 2 · lip_h 4 · lip_t 1.5 · lip_c 0.2 · color_case Silver.
Stackable Mini-ITX NAS case
mick/itx-nas-stack@1.0.0
Report
For a legal or policy problem: a license violation, personal data, somebody else’s work republished as their own. A model that prints badly is a comment, not a report.
Parameters
from sourceDescription
What is this
A stackable case for a Mini-ITX NAS board, sized so every part prints on a 180 mm bed (Bambu A1 mini). The stack is a base module that carries the board, one identical drive module per flat 3.5" disk, and a lid with a fan that pulls air up through the whole stack. Modules locate on each other with a lip and pocket, so adding a disk later is printing one more drive module and dropping it in.
What you use it for
A slim, clean NAS around a 170 × 170 board with a pico PSU: DC barrel jack in a side wall, a 16 mm vandal switch in the front, the rear I/O open above the board with a flat rear shield that slides down into grooves. Each drive module takes one 3.5" disk lying flat, connector to the rear, no screws: four corner guides keep it in place and the floor is slotted so the fan draws air past it. A rear cutout in every floor passes the SATA cables down to the board.
Design notes
The footprint is board + clearance + two walls, 177 mm by default, which is why the walls are three millimetres and the case is not wider. Every module has a pocket ring under its floor and a lip ring on top; the lip is the inner half of the wall so the outside stays flush. The floor sits on a rib grid so the pocket is a two millimetre bridge and nothing else is unsupported. The board module has no rear wall above the board, because the ports on these NAS boards run edge to edge; its rear lip is therefore carried by the rear shield instead, and small stops in every pocket block the stack from sliding rearward. The lid has a thicker wall so its pocket has an inner face without a floor; it prints upside down, the modules print upright, the shield prints flat.
Parameters
part picks the assembly or one printable part. hole_rear_a/b and hole_side_a/b are the four standoff positions measured from the rear and left edges of the board; the defaults are the Mini-ITX spec, check them on your board. board_h sets how tall the board module is. button_d/x/z and jack_d/side/y/z place the switch and DC jack. hdd_* size the drive pocket; hdd_cable is the room behind the disk for the plugs. fan_size picks the lid fan (0 for a plain lid). io_x/io_w/io_h cut one opening in the rear shield. wall, lip_h, lip_t and lip_c tune the fit between modules.
Use with your agent
{
"mcpServers": {
"remixcad": {
"type": "http",
"url": "https://remix-cad.vercel.app/api/mcp"
}
}
}get_model("mick/itx-nas-stack@1.0.0")Returns the manifest, the source, the import closure and the license obligations. Full MCP setup.
Parameter table
Parsed from the Customizer annotations in main.scad| Name | Default | Range | Unit | Description |
|---|---|---|---|---|
| part | "assembly" | assembly, board_module, hdd_module, lid, rear_shield | ||
| hdd_modules | 2 | 0 to 4 step 1 | ||
| explode | 0 | 0 to 40 step 5 | ||
| show_hardware | true | |||
| board | 170 | 120 to 172 step 1 | ||
| board_clear | 0.5 | 0.3 to 1.5 step 0.1 | ||
| standoff_h | 6.5 | 4 to 10 step 0.5 | ||
| standoff_hole | 2.6 | 2.4 to 4.5 step 0.1 | ||
| hole_rear_a | 6.35 | 2 to 40 step 0.01 | ||
| hole_rear_b | 163.83 | 130 to 168 step 0.01 | ||
| hole_side_a | 10.16 | 2 to 40 step 0.01 | ||
| hole_side_b | 165.1 | 130 to 168 step 0.01 | ||
| board_h | 60 | 40 to 90 step 1 | ||
| button_d | 16 | 0 to 22 step 0.5 | ||
| button_x | 30 | 10 to 160 step 1 | ||
| button_z | 36 | 15 to 80 step 1 | ||
| jack_d | 12 | 0 to 16 step 0.5 | ||
| jack_side | "right" | right, left | ||
| jack_y | 140 | 15 to 160 step 1 | ||
| jack_z | 30 | 12 to 80 step 1 | ||
| vents | true | |||
| vent_w | 2 | 1.5 to 4 step 0.5 | ||
| vent_pitch | 6 | 4 to 12 step 0.5 | ||
| hdd_w | 101.6 | 69 to 102 step 0.1 | ||
| hdd_l | 147 | 100 to 150 step 0.1 | ||
| hdd_h | 26.1 | 7 to 30 step 0.1 | ||
| hdd_gap | 3 | 2 to 10 step 0.5 | ||
| hdd_cable | 22 | 12 to 30 step 1 | ||
| hdd_clear | 0.4 | 0 to 1 step 0.1 | ||
| fan_size | 80 | 0, 40, 60, 80, 92, 120 | ||
| fan_t | 25 | 10 to 30 step 1 | ||
| io_x | 0 | 0 to 170 step 0.5 | ||
| io_w | 170 | 10 to 170 step 0.5 | ||
| io_h | 20 | 5 to 60 step 0.5 | ||
| wall | 3 | 2.5 to 4 step 0.1 | ||
| floor_t | 2.5 | 2 to 4 step 0.1 | ||
| top_t | 2.5 | 2 to 4 step 0.1 | ||
| corner_r | 2 | 0 to 3 step 0.5 | ||
| lip_h | 4 | 3 to 6 step 0.5 | ||
| lip_t | 1.5 | 1.2 to 2 step 0.1 | ||
| lip_c | 0.2 | 0.1 to 0.4 step 0.05 | ||
| color_case | "Silver" | Silver, Gainsboro, White, DimGray, Black, SlateGray, FireBrick, SteelBlue, ForestGreen, Goldenrod |
Lineage
0 models depend on this oneOnly what this model came from and what came from it.
License
Whoever downloads this takes these obligations along- mick/itx-nas-stack@1.0.0 is MIT
Source
main.scad is the entrypointmain.scad
// Stackable Mini-ITX NAS case for a small printer (180 mm bed).
// A base module carries the board, each drive module carries one flat 3.5" disk,
// and a fan lid closes the stack and pulls air up through it. Modules print
// upright as one piece, the lid prints upside down, the rear shield flat.
/* [Parts] */
// The whole stack, or one part in print orientation.
part = "assembly"; // [assembly, board_module, hdd_module, lid, rear_shield]
// Drive modules shown in the assembly.
hdd_modules = 2; // [0:1:4]
// Pull the stack apart in the assembly view.
explode = 0; // [0:5:40]
// Show the board and the drives as ghosts in the assembly.
show_hardware = true;
/* [Board] */
// Board size, 170 for Mini-ITX.
board = 170; // [120:1:172]
// Gap between the board edge and the wall, each side.
board_clear = 0.5; // [0.3:0.1:1.5]
// Height of the printed standoffs under the board.
standoff_h = 6.5; // [4:0.5:10]
// Screw hole in the standoffs, 2.6 for a self tapping M3.
standoff_hole = 2.6; // [2.4:0.1:4.5]
// Mounting holes, measured from the rear (I/O) edge of the board.
hole_rear_a = 6.35; // [2:0.01:40]
hole_rear_b = 163.83; // [130:0.01:168]
// Mounting holes, measured from the left edge of the board, seen from the front.
hole_side_a = 10.16; // [2:0.01:40]
hole_side_b = 165.1; // [130:0.01:168]
// Height of the board module. Everything on the board must fit under it.
board_h = 60; // [40:1:90]
/* [Front and sides] */
// Thread diameter of the power button, 16 for the common vandal switch. 0 for none.
button_d = 16; // [0:0.5:22]
// Button position from the left, and height from the module bottom.
button_x = 30; // [10:1:160]
button_z = 36; // [15:1:80]
// Panel mount DC jack hole, 0 for none.
jack_d = 12; // [0:0.5:16]
// Which side wall the jack sits in.
jack_side = "right"; // [right, left]
// Jack position from the rear, and height from the module bottom.
jack_y = 140; // [15:1:160]
jack_z = 30; // [12:1:80]
// Vertical vent slots in the side walls.
vents = true;
vent_w = 2; // [1.5:0.5:4]
vent_pitch = 6; // [4:0.5:12]
/* [Drives] */
// Size of the disk, 3.5" by default.
hdd_w = 101.6; // [69:0.1:102]
hdd_l = 147; // [100:0.1:150]
hdd_h = 26.1; // [7:0.1:30]
// Room above the disk.
hdd_gap = 3; // [2:0.5:10]
// Room behind the disk for the SATA plugs.
hdd_cable = 22; // [12:1:30]
// Play around the disk in its guides.
hdd_clear = 0.4; // [0:0.1:1]
/* [Lid] */
// Fan in the lid, 0 for a plain lid.
fan_size = 80; // [0, 40, 60, 80, 92, 120]
// Thickness of the fan.
fan_t = 25; // [10:1:30]
/* [Rear shield] */
// One opening in the rear shield: from the left edge of the board, its width, and its height from the shield bottom.
io_x = 0; // [0:0.5:170]
io_w = 170; // [10:0.5:170]
io_h = 20; // [5:0.5:60]
/* [Case] */
wall = 3; // [2.5:0.1:4]
floor_t = 2.5; // [2:0.1:4]
top_t = 2.5; // [2:0.1:4]
// Rounding of the vertical outer corners.
corner_r = 2; // [0:0.5:3]
// Height and thickness of the stacking lip, and its play in the pocket.
lip_h = 4; // [3:0.5:6]
lip_t = 1.5; // [1.2:0.1:2]
lip_c = 0.2; // [0.1:0.05:0.4]
/* [Colour] */
// Display only, the STL carries no colour.
color_case = "Silver"; // [Silver, Gainsboro, White, DimGray, Black, SlateGray, FireBrick, SteelBlue, ForestGreen, Goldenrod]
/* [Hidden] */
$fn = 48;
pcb_t = 1.6;
S = board + 2 * (board_clear + wall);
skirt_t = wall - lip_t - lip_c;
rib_in = wall + lip_c + 0.1;
rib_t = 1.6;
rib_pitch = 18;
stop_len = 6;
stop_end = wall + stop_len;
groove_d = 1.0;
shield_t = 1.6;
z0 = lip_h + floor_t;
board_z = z0 + standoff_h;
rear_top = board_z - 0.5;
hdd_mod_h = z0 + hdd_h + hdd_gap;
lid_wall = rib_in + rib_t;
lid_h = top_t + (fan_size > 0 ? fan_t + 2 : 5);
fan_hole = lookup(fan_size, [[40, 32], [60, 50], [80, 71.5], [92, 82.5], [120, 105]]);
// ---- 2D outlines ----
module outer2d() {
offset(r = corner_r) offset(delta = -corner_r) square([S, S]);
}
module inset2d(k) {
offset(r = -k) outer2d();
}
module ring2d(a, b) {
difference() { inset2d(a); inset2d(b); }
}
// Rib grid under the floor.
module ribs2d() {
for (p = [rib_pitch : rib_pitch : S - rib_pitch / 2]) {
translate([p - rib_t / 2, 0]) square([rib_t, S]);
translate([0, p - rib_t / 2]) square([S, rib_t]);
}
}
// Blocks in the rear corners of the pocket: they stop the stack sliding rearward.
module stops2d() {
for (m = [0, 1]) translate([m ? S : 0, 0]) mirror([m, 0])
translate([skirt_t - 0.05, skirt_t - 0.05]) {
square([rib_in - skirt_t + 0.6, stop_end - skirt_t + 0.05]);
square([stop_end - skirt_t + 0.05, rib_in - skirt_t + 0.6]);
}
}
// Lip ring on top of a module, kept clear of the rear corner stops.
module lip2d(wt) {
difference() {
ring2d(wt - lip_t, wt);
for (m = [0, 1]) translate([m ? S : 0, 0]) mirror([m, 0])
square([stop_end + 0.3, stop_end + 0.3]);
}
}
// ---- Shared shell ----
// Walls, a floor on a rib grid with the pocket ring under it, corner stops and the lip.
module shell(h, wt, has_floor, has_lip) {
union() {
difference() {
linear_extrude(h) outer2d();
// cavity
translate([0, 0, has_floor ? z0 : -1]) linear_extrude(h + lip_h + 2) inset2d(wt);
// pocket ring for the lip of the module below
translate([0, 0, -1]) linear_extrude(lip_h + 1) ring2d(skirt_t, rib_in);
// hollow under the floor, leaving the rib grid
if (has_floor)
translate([0, 0, -1]) linear_extrude(lip_h + 1)
difference() { inset2d(rib_in + rib_t); ribs2d(); }
}
linear_extrude(lip_h) stops2d();
if (has_lip) translate([0, 0, h]) linear_extrude(lip_h - 0.3) lip2d(wt);
}
}
module floor_slots(y_from, y_to) {
for (y = [y_from : 9 : y_to])
translate([wall + 10, y, lip_h]) cube([S - 2 * wall - 20, 3, floor_t + 1]);
}
module side_vents(h, with_jack) {
for (m = [0, 1], y = [wall + 18 : vent_pitch : S - wall - 18])
if (!(with_jack && jack_d > 0 && ((jack_side == "right") == (m == 1)) && abs(y - jack_y) < jack_d / 2 + 5))
translate([m ? S - wall - 1 : -1, y, 8]) cube([wall + 2, vent_w, h - 16]);
}
// ---- Board module ----
module standoffs() {
for (hx = [hole_side_a, hole_side_b], hy = [hole_rear_a, hole_rear_b])
translate([wall + board_clear + hx, wall + board_clear + hy, z0 - 0.01])
difference() {
cylinder(d = 7, h = standoff_h + 0.01);
translate([0, 0, 1]) cylinder(d = standoff_hole, h = standoff_h + 1);
}
}
module board_module() {
difference() {
union() {
difference() {
shell(board_h, wall, true, true);
floor_slots(wall + 12, S - wall - 12);
}
standoffs();
}
// rear open above the board, the ports run edge to edge
translate([wall - 0.01, -1, rear_top])
cube([S - 2 * wall + 0.02, wall + 1.01, board_h + lip_h + 2]);
// grooves the rear shield slides down into
for (m = [0, 1])
translate([m ? S - wall - 0.01 : wall - groove_d, 1.0, rear_top])
cube([groove_d + 0.01, shield_t + 0.2, board_h + lip_h + 2]);
if (button_d > 0)
translate([button_x, S - wall - 1, button_z]) rotate([-90, 0, 0])
cylinder(d = button_d + 0.3, h = wall + 2);
if (jack_d > 0)
translate([jack_side == "right" ? S - wall - 1 : -1, jack_y, jack_z]) rotate([0, 90, 0])
cylinder(d = jack_d + 0.3, h = wall + 2);
if (vents) side_vents(board_h, true);
}
}
// ---- Drive module ----
module hdd_module() {
hx0 = (S - hdd_w) / 2 - hdd_clear;
hy0 = wall + hdd_cable - hdd_clear;
gt = 1.5; gl = 12; gh = 4;
difference() {
union() {
difference() {
shell(hdd_mod_h, wall, true, true);
floor_slots(wall + hdd_cable + 6, S - wall - 10);
}
// four corner guides around the disk
for (sx = [0, 1], sy = [0, 1])
translate([sx ? hx0 + hdd_w + 2 * hdd_clear : hx0,
sy ? hy0 + hdd_l + 2 * hdd_clear : hy0, z0 - 0.01])
mirror([sx, 0, 0]) mirror([0, sy, 0]) translate([-gt, -gt, 0]) {
cube([gl + gt, gt, gh]);
cube([gt, gl + gt, gh]);
}
}
// cable pass-through behind the disk
translate([S / 2 - 55, wall - 0.01, -1]) cube([110, hdd_cable - 3, z0 + 2]);
if (vents) side_vents(hdd_mod_h, false);
}
}
// ---- Lid ----
module fan_cut2d() {
difference() {
circle(d = fan_size - 2);
circle(r = 5);
for (r = [8 : 5 : fan_size / 2])
difference() { circle(r = r + 2); circle(r = r); }
for (a = [0, 45, 90, 135]) rotate(a) square([fan_size, 2.5], center = true);
}
for (sx = [-1, 1], sy = [-1, 1])
translate([sx * fan_hole / 2, sy * fan_hole / 2]) circle(d = 4.3);
}
module lid() {
difference() {
union() {
shell(lid_h, lid_wall, false, false);
translate([0, 0, lid_h - top_t]) linear_extrude(top_t) outer2d();
}
if (fan_size > 0)
translate([S / 2, S / 2, lid_h - top_t - 1]) linear_extrude(top_t + 2) fan_cut2d();
}
}
// ---- Rear shield ----
// Modelled flat: x along the case width, y up the case, z inward.
module rear_shield() {
sw = S - 2 * wall + 2 * groove_d - 0.3;
sh = board_h - rear_top;
bx = groove_d + board_clear - 0.15;
lip_in = stop_len + groove_d + 0.15;
difference() {
union() {
cube([sw, sh, shield_t]);
// the rear lip of the board module lives on the shield
translate([lip_in, sh - 0.01, 0.3])
cube([sw - 2 * lip_in, lip_h - 0.3 + 0.01, shield_t - 0.3]);
}
translate([bx + io_x, -1, -1]) cube([io_w, io_h + 1, shield_t + 2]);
}
}
// ---- Ghost hardware ----
module ghost_board() {
color("DarkGreen", 0.5)
translate([wall + board_clear, wall + board_clear, board_z]) cube([board, board, pcb_t]);
}
module ghost_hdd() {
color("Silver", 0.5)
translate([(S - hdd_w) / 2, wall + hdd_cable, z0]) cube([hdd_w, hdd_l, hdd_h]);
}
// ---- Layout ----
module assembly() {
color(color_case) board_module();
color(color_case)
translate([wall - groove_d + 0.15, 1.1 + shield_t, rear_top]) rotate([90, 0, 0]) rear_shield();
if (show_hardware) ghost_board();
if (hdd_modules > 0)
for (i = [0 : hdd_modules - 1])
translate([0, 0, board_h + i * hdd_mod_h + (i + 1) * explode]) {
color(color_case) hdd_module();
if (show_hardware) ghost_hdd();
}
translate([0, 0, board_h + hdd_modules * hdd_mod_h + (hdd_modules + 1) * explode])
color(color_case) lid();
}
if (part == "board_module") color(color_case) board_module();
else if (part == "hdd_module") color(color_case) hdd_module();
else if (part == "lid") color(color_case) translate([0, S, lid_h]) rotate([180, 0, 0]) lid();
else if (part == "rear_shield") color(color_case) rear_shield();
else assembly();
Comments
Context, not a score. Comments never move a model up or down the results.No comments yet
Printed it? A note about your material and the clearance you landed on saves the next person a failed print.
