refresh
18
README.md
@ -6,7 +6,7 @@
|
||||
- ***Mount Anything:*** Perfect for organizing SBCs, mini PCs, small switches, power hubs, etc.
|
||||
- ***Fully customizable:*** Fully written in OpenSCAD. Everything, from the dimensions of the rack, to the roundness of the corners, can be modified with a simple code change.
|
||||
- ***Printable from home:*** Designed to be printed with conventional FDM printers. Requires minimal supports when printing, and final assembly needs only a few easy-to-source parts.
|
||||
- ***No cage nuts!*** Sliding hex nut design for the front rails allows one to easily mount items without dealing with cage nuts.
|
||||
- ***No cage nuts!*** Sliding hex nut design for the front rails allows one to easily mount items, without dealing with cage nuts.
|
||||
- ***Stackable:*** Individual racks can be easily stacked and fastened together. Mix and match different color and design combinations!
|
||||
|
||||
## Assembly
|
||||
@ -51,9 +51,9 @@ Please see [the assembly README here](./assembly-guide)
|
||||
| [Feet](./rack/print/feet_P.scad) (optional) | 2 |
|
||||
|
||||
|
||||
|
||||
#### Notes:
|
||||
|
||||
- Before printing the actual parts. It's recommended to print this evaluation part: [eval_P](./rack/print/eval_P.scad) to test tolerances. If you find the fits too tight/loose, you can adjust them [here](./config/slack.scad).
|
||||
- Please also adjust [this file](./config/slicer.scad) to match your slicer settings.
|
||||
- Omitted actual plastic for printing. Any conventional 3d printing plastic should do (PLA, PETG, ABS),
|
||||
but beware of PLA's thermal limits. Higher infill is recommended for all parts.
|
||||
- For joining two racks, you will need to print 4 [stackConnectorDuals](./rack/print/stackConnectorDual_P.scad), as well as 8 M3 hex nuts, and 8 M3x12 FHCS.
|
||||
@ -73,18 +73,10 @@ Generate all project files for the `micro` profile:
|
||||
|
||||
`python3 rbuild.py -b all -c micro`
|
||||
|
||||
This will build all the required STLs for a micro rack in the `stl/custom/` directory.
|
||||
This will build all the parts defined in [rack/print](./rack/print), and put the STLs in [stl/micro](./stl/micro).
|
||||
|
||||
For generating a specific part:
|
||||
|
||||
`python3 rbuild.py -b yBar -c micro -t custom`
|
||||
|
||||
Generated stls are put into the `stl/` directories. The actual variable values for different profiles can be found in
|
||||
[rack/profiles.scad](config/rackFrame.scad).
|
||||
|
||||
`rbuild.py` also support an optional `--nightly` flag, which means the build script will use the `openscad-nightly` command, instead of `openscad`.
|
||||
|
||||
We recommend you start by printing the `eval_P.stl` file first, just to determine if the default slack/layer height
|
||||
configurations work for you. If parts are too tight/loose please take a look at
|
||||
[config/slack.scad](config/slack.scad). Please also adjust [config/printing.scad](config/slicer.scad) to match your
|
||||
slicer settings.
|
||||
`rbuild.py` also support an optional `--nightly` flag, which means the build script will use the `openscad-nightly` command, instead of `openscad`.
|
||||
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 325 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
@ -1,8 +0,0 @@
|
||||
include <../../rack/sharedVariables.scad>
|
||||
use <../frontBoxHolder.scad>
|
||||
use <../sideRail.scad>
|
||||
|
||||
|
||||
module boxHolderHelper() {
|
||||
|
||||
}
|
||||
@ -5,12 +5,12 @@ frontBoxHolder(
|
||||
u=2,
|
||||
plateThickness=3,
|
||||
cutoutOffsetX=(rackMountScrewWidth-147)/2,
|
||||
cutoutOffsetY=2,
|
||||
cutoutOffsetY=4.25,
|
||||
cutoutX=147,
|
||||
cutoutY=26,
|
||||
cutoutY=21.5,
|
||||
support=true,
|
||||
supportedZ = 27.5,
|
||||
supportedZ = 26.5,
|
||||
supportWidth=120,
|
||||
supportDepth=5,
|
||||
supportRailBaseThickness=1.25
|
||||
supportRailBaseThickness=1.75
|
||||
);
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
use <../sideRail.scad>
|
||||
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.5, supportedY=101.5, supportedX=159);
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.2, supportedY=101.5, supportedX=159);
|
||||
@ -1,4 +1,4 @@
|
||||
use <../sideRail.scad>
|
||||
|
||||
mirror(v=[1,0,0])
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.5, supportedY=101.5, supportedX=159);
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.2, supportedY=101.5, supportedX=159);
|
||||
@ -3,7 +3,7 @@ include <../config/common.scad>
|
||||
include <../rack/sharedVariables.scad>
|
||||
include <./common.scad>
|
||||
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.5, supportedY=101.5, supportedX=159);
|
||||
sideSupportRailBase(u=2, double=true, top=true, baseThickness=1.5, sideThickness=4, backThickness=2, supportedZ=27.2, supportedY=101.5, supportedX=159);
|
||||
|
||||
// distance between front and back main rail screw mounts
|
||||
sideRailScrewMountDist = yBarDepth - 2*(frontScrewSpacing + railFrontThickness + railSlotToXZ);
|
||||
@ -13,18 +13,22 @@ module sideSupportRailBase(u=2, double=true, top=true, baseThickness=2, sideThic
|
||||
mountBlockHeight = 10;
|
||||
mountBlockDepth = 10;
|
||||
screwMountGlobalDz = screwDiff / 2.0; // vertical distance between local origin and main rail screw mount
|
||||
railLength = max(sideRailScrewMountDist + frontScrewSpacing + mountBlockDepth, supportedY+backThickness);
|
||||
sideRailScrewToMainRailFrontDx = frontScrewSpacing+railFrontThickness;
|
||||
railLength = max(sideRailScrewMountDist + sideRailScrewToMainRailFrontDx + mountBlockDepth/2, supportedY+backThickness);
|
||||
railBaseThickness = baseThickness;
|
||||
railSideThickness = sideThickness;
|
||||
railBaseWidth = 15;
|
||||
railSideHeight = supportedZ + railBaseThickness*2;
|
||||
frontMountPad = frontScrewSpacing;
|
||||
railSideHeight = supportedZ + railBaseThickness*2 + overhangSlack;
|
||||
|
||||
frontMountPad = (sideRailScrewToMainRailFrontDx-mountBlockDepth/2)-xySlack;
|
||||
|
||||
applyMainRailMounts()
|
||||
sideSupportRailBase();
|
||||
|
||||
module applyMainRailMounts() {
|
||||
echo(frontScrewSpacing);
|
||||
echo(sideRailScrewToMainRailFrontDx);
|
||||
|
||||
module applyMainRailMounts() {
|
||||
mountBlockExtension = (railSupportsDx - supportedX)/2 - railSideThickness;
|
||||
assert(mountBlockExtension >= 10);
|
||||
|
||||
|
||||
45
rbuild.py
@ -7,24 +7,27 @@ import os
|
||||
PATH_TO_OPENSCAD = '/usr/bin/openscad'
|
||||
PATH_TO_OPENSCAD_NIGHTLY = '/snap/bin/openscad-nightly'
|
||||
|
||||
|
||||
# For actual dimensions, please see profiles.scad.
|
||||
class BuildSizeConfig:
|
||||
NANO = 'nano'
|
||||
MINI = 'mini'
|
||||
MICRO = 'micro'
|
||||
|
||||
RACK_BUILD_DIR = './rack/print'
|
||||
RACK_MOUNT_BUILD_DIR = './rack-mount/print'
|
||||
|
||||
BUILD_PARENT_DIR = './stl'
|
||||
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
RACK_BUILD_DIR = os.path.join(FILE_DIR, 'rack/print')
|
||||
RACK_MOUNT_BUILD_DIR = os.path.join(FILE_DIR, 'rack-mount/print')
|
||||
BUILD_PARENT_DIR = os.path.join(FILE_DIR, 'stl')
|
||||
|
||||
RACK_BUILD_TARGET_SUB_DIR = 'rack'
|
||||
RACK_MOUNT_BUILD_TARGET_SUB_DIR = 'rack-mount'
|
||||
|
||||
ASSEMBLY_GIF_DIR = './rack/assembly'
|
||||
ASSEMBLY_GIF_BUILD_DIR = './assembly-guide/gifs'
|
||||
ASSEMBLY_GIF_TEMP_DIR = ASSEMBLY_GIF_BUILD_DIR + '/tmp'
|
||||
BUILD_GIF_FROM_PNG_SCRIPT = './misc/animate.sh'
|
||||
ASSEMBLY_GIF_DIR = os.path.join(FILE_DIR, 'rack/assembly')
|
||||
ASSEMBLY_GIF_BUILD_DIR = os.path.join(FILE_DIR, 'assembly-guide/gifs')
|
||||
ASSEMBLY_GIF_TEMP_DIR = os.path.join(ASSEMBLY_GIF_BUILD_DIR, 'tmp')
|
||||
BUILD_GIF_FROM_PNG_SCRIPT = os.path.join(FILE_DIR, 'misc/animate.sh')
|
||||
|
||||
ASSEMBLY_STEPS = [
|
||||
('slideHexNutsIntoYBar.scad', 24),
|
||||
@ -44,10 +47,11 @@ ASSEMBLY_STEPS = [
|
||||
('attachXYPlates.scad', 16)
|
||||
]
|
||||
|
||||
def main():
|
||||
|
||||
def main():
|
||||
if not assertOpenscadExists():
|
||||
print("Could not find OpenSCAD binary. Please make sure it's configured in rbuild.py. Currently only Darwin and Linux have been tested to work.")
|
||||
print(
|
||||
"Could not find OpenSCAD binary. Please make sure it's configured in rbuild.py. Currently only Darwin and Linux have been tested to work.")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='rbuild',
|
||||
@ -101,7 +105,6 @@ def main():
|
||||
|
||||
|
||||
def run_build(args):
|
||||
|
||||
build_var = args.b
|
||||
config_var = args.c
|
||||
target_var = args.t
|
||||
@ -111,11 +114,11 @@ def run_build(args):
|
||||
|
||||
if (build_var is not None) == (build_gifs is True):
|
||||
print("Please either provide the build (-b) variable, or the build-gifs option (--build-assembly-gifs)")
|
||||
quit()
|
||||
return
|
||||
|
||||
if build_gifs:
|
||||
build_assembly_gifs(config_var, dz, nightly)
|
||||
quit()
|
||||
return
|
||||
|
||||
if target_var != "":
|
||||
final_target_directory_name = target_var
|
||||
@ -123,7 +126,8 @@ def run_build(args):
|
||||
final_target_directory_name = config_var
|
||||
|
||||
rackBuildDirFull = os.path.join(BUILD_PARENT_DIR, final_target_directory_name, RACK_BUILD_TARGET_SUB_DIR)
|
||||
rackMountBuildDirFull = os.path.join(BUILD_PARENT_DIR, final_target_directory_name, RACK_MOUNT_BUILD_TARGET_SUB_DIR)
|
||||
rackMountBuildDirFull = os.path.join(BUILD_PARENT_DIR, final_target_directory_name,
|
||||
RACK_MOUNT_BUILD_TARGET_SUB_DIR)
|
||||
|
||||
if not os.path.exists(rackBuildDirFull):
|
||||
os.makedirs(rackBuildDirFull)
|
||||
@ -136,7 +140,8 @@ def run_build(args):
|
||||
build_single(RACK_BUILD_DIR, rackBuildDirFull, dir_file, config_var, dz, nightly)
|
||||
|
||||
for dir_file in os.listdir(RACK_MOUNT_BUILD_DIR):
|
||||
build_single(RACK_MOUNT_BUILD_DIR, rackMountBuildDirFull, dir_file, config_var, dz, nightly)
|
||||
build_single(RACK_MOUNT_BUILD_DIR, rackMountBuildDirFull, dir_file, config_var, dz,
|
||||
nightly)
|
||||
return
|
||||
|
||||
filename_rack = find_rack(build_var)
|
||||
@ -152,11 +157,13 @@ def run_build(args):
|
||||
if filename_rack_mount:
|
||||
build_single(RACK_MOUNT_BUILD_DIR, rackMountBuildDirFull, filename_rack, config_var, dz, nightly)
|
||||
|
||||
|
||||
def build_single(build_dir, target_dir, filename, config, dz, nightly):
|
||||
print('Building:', filename, 'from', build_dir, 'to', target_dir)
|
||||
openscad_args = construct_openscad_args(build_dir, target_dir, filename, config, dz)
|
||||
run_openscad(openscad_args, nightly)
|
||||
|
||||
|
||||
def build_assembly_gifs(config, dz, nightly):
|
||||
print('Building assembly-gifs. Source Dir:', ASSEMBLY_GIF_DIR, '| Target:', ASSEMBLY_GIF_BUILD_DIR)
|
||||
|
||||
@ -168,11 +175,10 @@ def build_assembly_gifs(config, dz, nightly):
|
||||
run_openscad(openscad_args, nightly)
|
||||
build_gif_from_png(fileName)
|
||||
|
||||
|
||||
def build_gif_from_png(fileName):
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
script_path = os.path.join(script_dir, BUILD_GIF_FROM_PNG_SCRIPT)
|
||||
try:
|
||||
subprocess.run(["bash", script_path, fileName], check=True)
|
||||
subprocess.run(["bash", BUILD_GIF_FROM_PNG_SCRIPT, fileName], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error calling shell script: {e}")
|
||||
|
||||
@ -209,7 +215,6 @@ def construct_openscad_animation_args(build_dir, target_dir, filename, config, d
|
||||
return openscad_args
|
||||
|
||||
|
||||
|
||||
def find_rack(filename):
|
||||
return find_scad_file(RACK_BUILD_DIR, filename)
|
||||
|
||||
@ -236,7 +241,6 @@ def find_scad_file(directory, filename):
|
||||
|
||||
|
||||
def run_openscad(options, nightly):
|
||||
|
||||
if nightly:
|
||||
command = [PATH_TO_OPENSCAD_NIGHTLY, '--enable', 'all']
|
||||
else:
|
||||
@ -250,8 +254,11 @@ def run_openscad(options, nightly):
|
||||
print('OpenSCAD command not found! '
|
||||
'Please make sure that you have the OpenSCAD binary configured in rbuild.py.'
|
||||
'(Currently needs Linux/Mac for this)')
|
||||
|
||||
|
||||
def assertOpenscadExists():
|
||||
return os.path.exists(PATH_TO_OPENSCAD)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||