Rendering these values. This takes a few seconds.
Download STL
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.

MIT

Stackable Mini-ITX NAS case

mick/itx-nas-stack@1.0.0

Remix this
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.

Sign in to report

0reused0 remixes · 0 imports27downloads1.21.1latest

Parameters

from source
04, steps of 1
040, steps of 5
120172, steps of 1
0.31.5, steps of 0.1
410, steps of 0.5
2.44.5, steps of 0.1

Showing the render for these values.

Description

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
Parameters of Stackable Mini-ITX NAS case, with the ranges the source declares
NameDefaultRangeUnitDescription
part"assembly"assembly, board_module, hdd_module, lid, rear_shield
hdd_modules20 to 4 step 1
explode00 to 40 step 5
show_hardwaretrue
board170120 to 172 step 1
board_clear0.50.3 to 1.5 step 0.1
standoff_h6.54 to 10 step 0.5
standoff_hole2.62.4 to 4.5 step 0.1
hole_rear_a6.352 to 40 step 0.01
hole_rear_b163.83130 to 168 step 0.01
hole_side_a10.162 to 40 step 0.01
hole_side_b165.1130 to 168 step 0.01
board_h6040 to 90 step 1
button_d160 to 22 step 0.5
button_x3010 to 160 step 1
button_z3615 to 80 step 1
jack_d120 to 16 step 0.5
jack_side"right"right, left
jack_y14015 to 160 step 1
jack_z3012 to 80 step 1
ventstrue
vent_w21.5 to 4 step 0.5
vent_pitch64 to 12 step 0.5
hdd_w101.669 to 102 step 0.1
hdd_l147100 to 150 step 0.1
hdd_h26.17 to 30 step 0.1
hdd_gap32 to 10 step 0.5
hdd_cable2212 to 30 step 1
hdd_clear0.40 to 1 step 0.1
fan_size800, 40, 60, 80, 92, 120
fan_t2510 to 30 step 1
io_x00 to 170 step 0.5
io_w17010 to 170 step 0.5
io_h205 to 60 step 0.5
wall32.5 to 4 step 0.1
floor_t2.52 to 4 step 0.1
top_t2.52 to 4 step 0.1
corner_r20 to 3 step 0.5
lip_h43 to 6 step 0.5
lip_t1.51.2 to 2 step 0.1
lip_c0.20.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 one

Only what this model came from and what came from it.

Versionmick/itx-nas-stack@1.0.0MITYou are hereVersionmick/itx-nas-stack@1.1.0MITVersionmick/itx-nas-stack@1.2.0MITVersionmick/itx-nas-stack@1.3.0MITVersionmick/itx-nas-stack@1.4.0MITVersionmick/itx-nas-stack@1.5.0MITVersionmick/itx-nas-stack@1.6.0MITVersionmick/itx-nas-stack@1.7.0MITVersionmick/itx-nas-stack@1.8.0MITVersionmick/itx-nas-stack@1.9.0MITVersionmick/itx-nas-stack@1.10.0MITVersionmick/itx-nas-stack@1.11.0MITVersionmick/itx-nas-stack@1.12.0MITVersionmick/itx-nas-stack@1.12.1MITVersionmick/itx-nas-stack@1.13.0MITVersionmick/itx-nas-stack@1.13.1MITVersionmick/itx-nas-stack@1.14.0MITVersionmick/itx-nas-stack@1.15.0MITVersionmick/itx-nas-stack@1.16.0MITVersionmick/itx-nas-stack@1.17.0MITVersionmick/itx-nas-stack@1.17.1MITVersionmick/itx-nas-stack@1.17.2MITVersionmick/itx-nas-stack@1.17.3MITVersionmick/itx-nas-stack@1.17.4MITVersionmick/itx-nas-stack@1.17.5MITVersionmick/itx-nas-stack@1.17.6MITVersionmick/itx-nas-stack@1.17.7MITVersionmick/itx-nas-stack@1.17.8MITVersionmick/itx-nas-stack@1.17.9MITVersionmick/itx-nas-stack@1.17.10MITVersionmick/itx-nas-stack@1.17.11MITVersionmick/itx-nas-stack@1.17.12MITVersionmick/itx-nas-stack@1.17.13MITVersionmick/itx-nas-stack@1.17.14MITVersionmick/itx-nas-stack@1.17.15MITVersionmick/itx-nas-stack@1.17.16MITVersionmick/itx-nas-stack@1.17.17MITVersionmick/itx-nas-stack@1.18.0MITVersionmick/itx-nas-stack@1.19.0MITVersionmick/itx-nas-stack@1.19.1MITVersionmick/itx-nas-stack@1.19.2MITVersionmick/itx-nas-stack@1.20.0MITVersionmick/itx-nas-stack@1.21.0MITVersionmick/itx-nas-stack@1.21.1MIT
Remix, the code changed Import, used unchanged and pinned The version you are on Withdrawn, still recorded

License

Whoever downloads this takes these obligations along
  • mick/itx-nas-stack@1.0.0 is MIT

Source

main.scad is the entrypoint
main.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.

Sign in to comment