$fa=5; $fs=0.4; epsilon = 0.3; // used when making sure we're proud of curved edges which might not // be rendered exactly. orthoEpsilon = 0.01; // to make sure we're proud of orthogonal edges which should be precise sizeZ = 19; wall = 2; outerRadius = 4.5; // be careful that you have space for the board lidHeight = outerRadius-wall; // how much of the inner box should be in the lid. lipHeight = 3; // how much below the external shape of the box should the internal lip go down lipWidth = 2; lipGap = 0.2; holeSlop = 0.3; wallSlop = 0.0; boardSizeX = 48.5; boardSizeY = 90; boardHoleSpacingX = 42.5; boardHoleSpacingY = 83.7; boardHeight = 5; boardThickness = 1; boardHoleDiameter = 1.5; noThreadDiameter = 3; boardCylinderDiameter = 7; screwHeadDiameter = 7; screwHeadConeDepth = 3; screwLen = 19; screwHeadCylinderDepth = sizeZ + wall - screwLen; extraCylinderScrewHeadDiameter = 11; extraCylinderScrewHeadDepth = screwHeadCylinderDepth + screwHeadConeDepth; boardSpaceTop = 3; boardSpaceRight = 23; boardSpaceBottom = 3; boardSpaceLeft = 3; boardHoleOffsetX = (boardSizeX - boardHoleSpacingX) / 2; boardHoleOffsetY = (boardSizeY - boardHoleSpacingY) / 2; sizeX = boardSizeX + boardSpaceRight + boardSpaceLeft; // internal size of X sizeY = boardSizeY + boardSpaceTop + boardSpaceBottom; // internal size of Y edgeLeft = wall; edgeRight = wall+sizeX; edgeBottom = wall; edgeTop = wall+sizeY; edgeLid = wall+sizeZ-lidHeight; // outside dimensions of assembled box outerDimensionX = sizeX+wall*2; outerDimensionY = sizeY+wall*2; outerDimensionZ = sizeZ+wall*2; lidHeightExternal = lidHeight + wall; boxHeightExternal = outerDimensionZ - lidHeightExternal; module roundedBox(xMin, xMax, yMin, yMax, zMin, zMax, radius) { r = radius <= 0 ? 0.01 : radius; hull() { for (x = [xMin+r, xMax-r]) { for (y = [yMin+r, yMax-r]) { for (z = [zMin+r, zMax-r]) { translate([x,y,z]) sphere(r); } } } } } module outerBox() { roundedBox(0, outerDimensionX, 0, outerDimensionY, 0, outerDimensionZ, outerRadius); } module innerBoxSub() { roundedBox(wall-wallSlop, sizeX+wall+wallSlop, wall-wallSlop, sizeY+wall+wallSlop, wall-wallSlop, sizeZ+wall+wallSlop, outerRadius-wall); } module innerBoxGap() { roundedBox(wall+lipGap+wallSlop, sizeX+wall-lipGap-wallSlop, wall+lipGap+wallSlop, sizeY+wall-lipGap-wallSlop, wall+lipGap, sizeZ+wall-lipGap, outerRadius-wall-lipGap); } module baseShape(gap=0) { difference() { outerBox(); if (gap) { innerBoxGap(); } else { innerBoxSub(); } } } module baseShapeLid() { difference() { baseShape(); translate([-epsilon, -epsilon, -epsilon]) cube([sizeX+wall*2+epsilon*2, sizeY+wall*2+epsilon*2, epsilon+edgeLid]); } } module baseShapeBox(gap=0) { difference() { baseShape(gap); translate([-epsilon, -epsilon, boxHeightExternal]) cube([outerDimensionX + epsilon * 2, outerDimensionY + epsilon * 2, lidHeightExternal + epsilon]); } } module lidNoScrewHoles() { difference() { intersection() { outerBox(); union() { baseShapeLid(); translate([wall,wall,edgeLid-lipHeight]) cube([sizeX, sizeY, lipHeight+outerRadius]); } } baseShapeBox(1); roundedBox(wall+lipWidth, wall+sizeX-lipWidth, wall+lipWidth, wall+sizeY-lipWidth, wall, wall+sizeZ, outerRadius-wall-lipWidth); } for (x=[edgeLeft + boardSpaceLeft + boardHoleOffsetX, edgeRight - boardSpaceRight - boardHoleOffsetX]) { for (y = [edgeBottom + boardSpaceBottom + boardHoleOffsetY, edgeTop - boardSpaceTop - boardHoleOffsetY]) { translate([x, y, wall + boardHeight + boardThickness]) cylinder(r=boardCylinderDiameter/2, h=sizeZ - boardHeight - boardThickness + epsilon); translate([x, y, outerDimensionZ - extraCylinderScrewHeadDepth]) cylinder(r = extraCylinderScrewHeadDiameter / 2, h = extraCylinderScrewHeadDepth - wall + epsilon); } } } module lid() { difference() { lidNoScrewHoles(); for (x=[edgeLeft + boardSpaceLeft + boardHoleOffsetX, edgeRight - boardSpaceRight - boardHoleOffsetX]) { for (y = [edgeBottom + boardSpaceBottom + boardHoleOffsetY, edgeTop - boardSpaceTop - boardHoleOffsetY]) { translate([x, y, 0]) cylinder(r=noThreadDiameter/2+holeSlop, h=sizeZ + wall * 2 + epsilon); translate([x, y, wall*2+sizeZ-screwHeadConeDepth-screwHeadCylinderDepth]) cylinder(r1=0, r2=(screwHeadDiameter)/2+holeSlop, h=screwHeadConeDepth+orthoEpsilon); translate([x, y, wall*2+sizeZ-screwHeadCylinderDepth]) cylinder(r = (screwHeadDiameter)/2+holeSlop, h=screwHeadCylinderDepth+epsilon); } } } } module box() { baseShapeBox(); for (x=[edgeLeft + boardSpaceLeft + boardHoleOffsetX, edgeRight - boardSpaceRight - boardHoleOffsetX]) { for (y = [edgeBottom + boardSpaceBottom + boardHoleOffsetY, edgeTop - boardSpaceTop - boardHoleOffsetY]) { echo ([x,y]); translate([x,y,wall-epsilon]) difference() { cylinder(r=boardCylinderDiameter/2, h=boardHeight+epsilon); translate([0,0,-epsilon]) cylinder(r=boardHoleDiameter/2+holeSlop, h=boardHeight+epsilon*3); } } } } module strapHolder(r,w,d) { difference() { union() { translate([-epsilon, 0, 0]) cube([r+epsilon, w + r * 4, d]); translate([0, r * 2, 0]) cube([r * 2, w, d]); translate([r, r*2, 0]) cylinder(r=r, h=d); translate([r, r*2+w, 0]) cylinder(r=r, h=d); } translate([r, 0, -epsilon]) cylinder(r=r, h=d+epsilon*2); translate([r, r*4+w, -epsilon]) cylinder(r=r, h=d+epsilon*2); translate([-epsilon*2, r*2, -epsilon]) cube([r+epsilon*2, w, d+epsilon*2]); } } module boxWithCutouts() { wireHoleWidth = 38; wireHoleHeight = 2; chargerBoardX = 20; chargerBoardY = 24; chargerBoardZ = 3; portHoleX = 11; portHoleZ = 9; portHoleZOffset = -0.4; portHoleXOffset = (chargerBoardX - portHoleX) / 2; keypadConnectorHoleX = 23; keypadConnectorHoleZ = 1.5; keypadConnectorOffsetX = (boardSizeX - keypadConnectorHoleX) / 2 + wall + boardSpaceLeft; difference() { box(); // wires translate([-0.5, (outerDimensionY-wireHoleWidth) / 2, boxHeightExternal - wireHoleHeight]) cube([3, wireHoleWidth, wireHoleHeight+epsilon]); // usb charger // cut out a space in the curved edge so that the charger can fit there. translate([outerDimensionX - wall - 1 - chargerBoardX, outerDimensionY - wall - 1 - chargerBoardY, wall]) cube([chargerBoardX, chargerBoardY, chargerBaordZ]); // and space for the plug to go in translate([outerDimensionX - wall - 1 - chargerBoardX + portHoleXOffset, outerDimensionY - wall - 1 - epsilon, wall + portHoleZOffset]) cube([portHoleX, wall + 1 + epsilon * 2, portHoleZ]); // see the LEDs on the charger translate([outerDimensionX - wall - 5, outerDimensionY - wall - 1 - 20, wall]) cube([10+wall, 8, 2]); //keypad connector wires translate([keypadConnectorOffsetX, -0.5, boxHeightExternal - keypadConnectorHoleZ]) cube([keypadConnectorHoleX, 3, keypadConnectorHoleZ+epsilon]); } r = 2; w = 15; d = boxHeightExternal - outerRadius; shLen = w + r * 4; translate([outerDimensionX, outerRadius+2, outerRadius]) strapHolder(r, w, d); translate([outerDimensionX, outerDimensionY - outerRadius-2 - shLen, outerRadius]) strapHolder(r, w, d); } module lidWithCutouts() { wireHoleWidth = 38; wireHoleHeight = 10; switchRadius = 9.75; keypadConnectorHoleX = 23; keypadConnectorOffsetX = (boardSizeX - keypadConnectorHoleX) / 2 + wall + boardSpaceLeft; difference() { lid(); // wires translate([-0.5, (outerDimensionY-wireHoleWidth) / 2, boxHeightExternal - wireHoleHeight]) cube([10, wireHoleWidth, wireHoleHeight]); // switch translate([outerDimensionX - outerRadius - switchRadius, outerDimensionY - outerRadius - switchRadius, outerDimensionZ - 15]) cylinder(r=switchRadius, h=20); translate([outerDimensionX - outerRadius - switchRadius*2 - 1, outerDimensionY - outerRadius - switchRadius - 1, outerDimensionZ - 15]) cube([2,2,20]); translate([outerDimensionX - outerRadius - switchRadius - 2, outerDimensionY - outerRadius - switchRadius*2 - 1, outerDimensionZ - 15]) cube([4, switchRadius*2+2, 20]); translate([outerDimensionX - outerRadius - switchRadius - 2, outerDimensionY - outerRadius - switchRadius*2 - 1 - 2, outerDimensionZ - 15]) cube([4, switchRadius*2+2+4, 15 - wall]); // keypad connector wires translate([keypadConnectorOffsetX, 0, boxHeightExternal - wireHoleHeight]) cube([keypadConnectorHoleX, 10, wireHoleHeight]); } } rotate([180,0,0]) translate([-outerDimensionX/2, -outerDimensionY/2, -outerDimensionZ/2]) lidWithCutouts(); //boxWithCutouts();