From f43804582dac53a19c9e126560cefac2798dbd5e Mon Sep 17 00:00:00 2001 From: ThorstenB Date: Tue, 17 May 2022 00:06:31 +0200 Subject: [PATCH] Added OpenSCAD files. --- scad/components/baseLib.scad | 124 +++++++++++ scad/components/buttons.scad | 18 ++ scad/components/front.scad | 200 ++++++++++++++++++ scad/components/main.scad | 200 ++++++++++++++++++ scad/components/pcb.scad | 44 ++++ scad/components/rear.scad | 91 ++++++++ scad/components/top_and_bottom.scad | 312 ++++++++++++++++++++++++++++ scad/main.scad | 2 +- 8 files changed, 990 insertions(+), 1 deletion(-) create mode 100644 scad/components/baseLib.scad create mode 100644 scad/components/buttons.scad create mode 100644 scad/components/front.scad create mode 100644 scad/components/main.scad create mode 100644 scad/components/pcb.scad create mode 100644 scad/components/rear.scad create mode 100644 scad/components/top_and_bottom.scad diff --git a/scad/components/baseLib.scad b/scad/components/baseLib.scad new file mode 100644 index 0000000..3c10724 --- /dev/null +++ b/scad/components/baseLib.scad @@ -0,0 +1,124 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +module torus(r1, r2) +{ + rotate_extrude(convexity = 10) + translate([r2 + r1, 0, 0]) + circle(r=r2); +} + +module round_cylinder(fillet, r, h) +{ + for(z=[fillet, h-fillet]) + torus(r-2*fillet, fillet, center=[0,0,z]); + cylinder(r=r-fillet, h); + translate([0,0,fillet]) cylinder(r=r, h=h-2*fillet); +} + +// Make a rectangle with rounded corners of given radius. +// The maximum outer dimensions exactly match the given width/height. +module roundedCube(Width, Height, Depth, CornerRadius) +{ + $fn=36; + union() + { + for (x=[0,Width-CornerRadius*2]) + for (y=[0,Height-CornerRadius*2]) + translate([x+CornerRadius, y+CornerRadius, 0]) cylinder(r=CornerRadius, h=Depth); + translate([CornerRadius,0,0]) cube([Width - 2*CornerRadius, Height, Depth]); + translate([0,CornerRadius,0]) cube([Width, Height - 2*CornerRadius, Depth]); + } +} + +module cylinderLedge(r, length) +{ + $fn=36; + translate([0, 0, -length/2]) + intersection() + { + cylinder(r=r, length); + cube([r, r, length]); + } +} + +module rcube(size=[30, 20, 10], radius=[3, 2, 1], center=true) +{ + hull() { + translate( center ? [0,0,0] : size/2 ) { + cube(size-2*radius+[2*radius[0],0,0],center=true); + cube(size-2*radius+[0,2*radius[1],0],center=true); + cube(size-2*radius+[0,0,2*radius[2]],center=true); + + for(x = [-0.5,0.5], y = [-0.5,0.5], z = [-0.5,0.5]) + translate([x * ( size[0] - 2*radius[0]), + y * ( size[1] - 2*radius[1]), + z * ( size[2] - 2*radius[2])]) + scale([radius[0], radius[1], radius[2]]) + if (quality == "high") sphere(1.0,$fn=4*4*2); else sphere(1.0,$fn=4); + } + } +} + +module hole(diameter, depth) +{ + $fn = 36; + cylinder(r=diameter/2, h=depth); +} + +module wall(width, height, WallThickness) +{ + translate([-width/2,-WallThickness/2,-height]) cube([width, WallThickness, height]); +} + +module prism(l, w, h) +{ + polyhedron( + points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]], + faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]] + ); +} + +module post(height, diameter, post_fillet_r, screw_hole_diameter, screw_hole_depth) +{ + $fn = 36; + difference() { + union() { + cylinder(r=diameter/2, h=height); + difference() { + cylinder(r=diameter/2 + post_fillet_r, h=post_fillet_r); + translate([0,0,post_fillet_r]) torus(diameter/2, post_fillet_r*1.1); + } + } + if (screw_hole_diameter>0) + { + translate([0,0,height-screw_hole_depth]) + hole(screw_hole_diameter, screw_hole_depth); + } + } +} + +module screwPost(ZDepth, PostDiameter, ScrewDiameter, ScrewHoleDepth, FilletR) +{ + if (PostDiameter>0.0) + post(ZDepth, PostDiameter, FilletR, ScrewDiameter, ScrewHoleDepth); +} + + +module outerBox(SkinWidth, XWidth, YHeight, ZDepth, CornerRadius) +{ + translate([-SkinWidth, -SkinWidth, 0]) roundedCube(XWidth+SkinWidth*2, YHeight+SkinWidth*2, ZDepth, CornerRadius); +} + +module box(SkinWidth, XWidth, YHeight, ZDepth, CornerRadius) +{ + // main shell + difference() { + outerBox(SkinWidth, XWidth, YHeight, ZDepth, CornerRadius); + //rcube(size=[XWidth+SkinWidth*2, YHeight+SkinWidth*2, ZDepth+SkinWidth*2], radius=[2, 2, 2], center=false); + if (SkinWidth>0) + roundedCube(XWidth, YHeight, ZDepth, CornerRadius); + //rcube(size=[XWidth, YHeight, ZDepth], radius=[2, 2, 2], center=false); + } +} + diff --git a/scad/components/buttons.scad b/scad/components/buttons.scad new file mode 100644 index 0000000..1ca4cdb --- /dev/null +++ b/scad/components/buttons.scad @@ -0,0 +1,18 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +// single button +module button() +{ + // front part of the button + translate([-ButtonXWidth/2, -ButtonYHeight/2, 0]) + roundedCube(ButtonXWidth, ButtonYHeight, ButonZDepth, ButtonCorners); + + // slightly larger frame around the buttons rear, to keep the button from falling through the panel + translate([-(ButtonXWidth+ButtonLedgeWidth)/2, -(ButtonYHeight+ButtonLedgeWidth)/2, 0]) + roundedCube(ButtonXWidth+ButtonLedgeWidth, ButtonYHeight+ButtonLedgeWidth, SKIN, ButtonCorners); + + // connector for the button's peg + rotate([0,180,0]) screwPost(ButtonPegDepth, ButtonPegDiameter+2, ButtonPegDiameter, ButtonPegDepth, 1); +} + diff --git a/scad/components/front.scad b/scad/components/front.scad new file mode 100644 index 0000000..b22e4b3 --- /dev/null +++ b/scad/components/front.scad @@ -0,0 +1,200 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + + +// front panel hole for a rectangular button +module buttonCutout() +{ + // add 1mm spacing, so the button moves freely (results in 0.5mm clearance on each side) + buttonXWidth = ButtonXWidth+0.5; + buttonYHeight = ButtonYHeight+0.5; + + translate([-buttonXWidth/2, -buttonYHeight/2, 0]) + roundedCube(buttonXWidth, buttonYHeight, SKIN, ButtonCorners); +} + +// test / unused +module buttonFrame() +{ + XWidth = ButtonXWidth+6; + YHeight = ButtonYHeight+6; + + difference() + { + translate([-XWidth/2, -YHeight/2, 0]) + roundedCube(XWidth, YHeight, SKIN, ButtonCorners); + buttonCutout(); + } +} + +// various holes for the front panel +module panelCutouts() +{ + // LCD display + translate([BoxX_Width-DisplayXDistance, BoxY_Height-DisplayYDistance, 0]) + { + translate([-DisplayWidth, -DisplayHeight, 0]) + roundedCube(DisplayWidth, DisplayHeight, SKIN*2, DisplayCornerRadius); + } + + // SD Slot + translate([BoxX_Width-SDSlotXDistance, SDSlotYDistance, 0]) + { + translate([-SDSlotWidth, 0, 0]) + roundedCube(SDSlotWidth, SDSlotHeight, SKIN, SDSlotCorners); + } + + // LEDs + translate([BoxX_Width-LedXOfs, LedYOfs, 0]) hole(LedDiameter, SKIN); + if (LedCount>1) + translate([BoxX_Width-LedXOfs, LedYOfs-LedYDistance, 0]) hole(LedDiameter, SKIN); + + // buttons + for (x = [0 : 1: 0]) + { + for (y = [0 : 1: 0]) + { + translate([ButtonXOfs+ButtonXWidth/2+(ButtonXSpacing)*x, + BoxY_Height-ButtonYHeight/2-ButtonYOfs-(ButtonYSpacing)*y, 0]) + buttonCutout(); + } + } + + for (x = [1 : 1: 1]) + { + for (y = [0 : 1: 2]) + { + translate([ButtonXOfs+ButtonXWidth/2+(ButtonXSpacing)*x, + BoxY_Height-ButtonYHeight/2-ButtonYOfs-(ButtonYSpacing)*y, 0]) + buttonCutout(); + } + } +} + +// pegs to hold the display +module displayPegs() +{ + translate([BoxX_Width-DisplayXDistance, BoxY_Height-DisplayYDistance-DisplayHeight/2+DisplayPegYDistance/2-DisplayPegYOffset, SKIN]) + { + translate([-DisplayWidth/2+DisplayPegXDistance/2, 0, 0]) + screwPost(DisplayPegHeight, DisplayPegDiameter, 0, 0, 0); + translate([-DisplayWidth/2+DisplayPegXDistance/2, -DisplayPegYDistance,0]) + screwPost(DisplayPegHeight, DisplayPegDiameter, 0, 0, 0); + translate([-DisplayWidth/2-DisplayPegXDistance/2, 0,0]) + screwPost(DisplayPegHeight, DisplayPegDiameter, 0, 0, 0); + translate([-DisplayWidth/2-DisplayPegXDistance/2, -DisplayPegYDistance,0]) + screwPost(DisplayPegHeight, DisplayPegDiameter, 0, 0, 0); + } +} + +// screw posts and supports of the button panel +module buttonPanelSupports() +{ + XOfs = ButtonXOfs+ButtonXWidth/2+ButtonRaster*3/2; + YOfs = BoxY_Height-ButtonYHeight/2-ButtonYOfs; + for (x = [-1 : 1: -1]) + for (y = [0 : 2: 2]) + { + echo("Button Panel mount (x/y):",ButtonXSpacing*x, ButtonYSpacing*y); + translate([XOfs+ButtonXSpacing*x, YOfs-ButtonYSpacing*y, SKIN]) + screwPost(ButtonScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ButtonScrewPostHeight, 0.5); + } + for (x = [2 : 1: 2]) + for (y = [0 : 2: 2]) + { + echo("Button Panel mount (x/y):",ButtonXSpacing*x, ButtonYSpacing*y); + translate([XOfs+ButtonXSpacing*x, YOfs-ButtonYSpacing*y, SKIN]) + screwPost(ButtonScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ButtonScrewPostHeight, 0.5); + } +} + +module baseFrontPanel() +{ + roundedCube(BoxX_Width-2*FrontPanelGap, BoxY_Height-FrontPanelGap, SKIN, Box_Corner_Radius); +} + +module displayFrame() +{ + // LCD display + translate([BoxX_Width-DisplayXDistance, BoxY_Height-DisplayYDistance, 0]) + { + translate([-DisplayWidth-1, -DisplayHeight-1, SKIN]) + roundedCube(DisplayWidth+2, DisplayHeight+2, DisplayFrameZHeight, DisplayCornerRadius); + } +} + +// front panel +module makeFront() +{ + translate([FrontPanelGap, 0, FrontPanelZOffset]) + { + displayPegs(); + difference() + { + union() + { + baseFrontPanel(); + displayFrame(); + } + panelCutouts(); + } + + // screw posts for the button panel + buttonPanelSupports(); + + difference() + { + translate([Box_Corner_Radius, 0, SKIN]) cube([BoxX_Width-Box_Corner_Radius*2, SKIN, FrontPanelZLedge]); + + // hole + translate([FrontGapX/2, -SKIN , FrontPanelZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // hole + translate([BoxX_Width-FrontGapX/2, -SKIN, FrontPanelZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + } + + // LED sockets + translate([BoxX_Width-LedXOfs, LedYOfs, SKIN]) + screwPost(LedSocketHeight, LedDiameter+2, LedDiameter, LedSocketHeight, 0); + if (LedCount>1) + translate([BoxX_Width-LedXOfs, LedYOfs-LedYDistance, SKIN]) + screwPost(LedSocketHeight, LedDiameter+2, LedDiameter, LedSocketHeight, 0); + + // support triangles + SupportWidth = FrontPanelZLedge; + // left + translate([Box_Corner_Radius, SKIN, SupportWidth+SKIN]) rotate([270, 0, 0]) prism(SKIN, SupportWidth, SupportWidth); + // right + translate([BoxX_Width-Box_Corner_Radius-SKIN, SKIN, SupportWidth+SKIN]) rotate([270, 0, 0]) prism(SKIN, SupportWidth, SupportWidth); + + // screw posts + translate([FrontGapX/2, SKIN , FrontPanelZLedge/2]) + { + rotate([270, 0, 0]) screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, 0); + translate([-ScrewPostDiameter/2, 0, -FrontPanelZLedge/2+SKIN]) cube([ScrewPostHeight, ScrewPostDiameter, FrontPanelZLedge/3-SKIN]); + } + + translate([BoxX_Width-FrontGapX/2, SKIN, FrontPanelZLedge/2]) + { + rotate([270, 0, 0]) screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, 0); + translate([-ScrewPostDiameter/2, 0, -FrontPanelZLedge/2+SKIN]) cube([ScrewPostHeight, ScrewPostDiameter, FrontPanelZLedge/3-SKIN]); + } + } + + // front screw posts for FloppyEmu PCB + PcbFrontMountHeight = PcbYOfs-SKIN; + translate([BoxX_Width/2-PcbXWidth/2+PcbScrewDistance+PcbXOfs, SKIN , PcbZOfs+PcbScrewDistance]) + { + rotate([270, 0, 0]) screwPost(PcbFrontMountHeight, PcbScrewPostDiameter, PcbScrewDiameter, PcbFrontMountHeight, 0); + translate([-PcbScrewPostDiameter/2,0,-PcbScrewPostDiameter/2]) cube([PcbScrewPostDiameter, PcbFrontMountHeight, PcbScrewPostDiameter/4]); + } + + translate([BoxX_Width/2+PcbXWidth/2-PcbScrewDistance+PcbXOfs, SKIN, PcbZOfs+PcbScrewDistance]) + { + rotate([270, 0, 0]) screwPost(PcbFrontMountHeight, PcbScrewPostDiameter, PcbScrewDiameter, PcbFrontMountHeight, 0); + translate([-PcbScrewPostDiameter/2,0,-PcbScrewPostDiameter/2]) cube([PcbScrewPostDiameter, PcbFrontMountHeight, PcbScrewPostDiameter/4]); + } +} + diff --git a/scad/components/main.scad b/scad/components/main.scad new file mode 100644 index 0000000..c6c3ba2 --- /dev/null +++ b/scad/components/main.scad @@ -0,0 +1,200 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +// Radius of the corners +Box_Corner_Radius = 3.0*ScalingFactor; // [0: 10] + +// Box skin width +SKIN = 1.0; // [1: 0.1: 5] + +/* [Hidden] */ + +// Width (X) of the box +BoxX_Width = 155*ScalingFactor; // [60: 145] + +// Height (Y) of the box +BoxY_Height = 90*ScalingFactor; // [10: 100] + +// Depth (Z) of the box +BoxZ_Depth = 210*ScalingFactor; // [10: 200] + +// Width of the ventilation cut +VentilationWidth = 4*ScalingFactor; +VentilationYHeight = 30*ScalingFactor; +VentilationXLength = 15*ScalingFactor; + +// Distance of the first ventilation cut from the rear +VentilationZOffset = 17*ScalingFactor; + +// Distance between the ventilation cuts +VentilationZDistance = 9.2*ScalingFactor; + +// Number of ventilation cuts +VentilationCount = 9; + +// Top grooves +TopGrooveXWidth = 110*ScalingFactor; +TopGrooveZLength = 80*ScalingFactor; +TopGrooveYDepth = 2;//*ScalingFactor; +TopGrooveCornerRadius = 6*ScalingFactor; + +// Rear wall +RearWallGap = 1.5; +RearWallCableGapHeight = 3; +RearWallCableGapWidth = 27+3; //26mm cable width +RearWallCableGapXOffset = 10+2*ScalingFactor; +RearWallZLedge = 17; +RearWallZOffset = 2.5; + +// Strain Relief +CableStrainReliefYOffset = BoxY_Height-20; // distance from the top of the box + +// Front panel +FrontPanelGap = 0.5; +FrontPanelZLedge = 10; +FrontPanelZOffset = 2; + +// Distance of the gap at the front (from the left/right edge) +FrontGapX = 21; +FrontGapWidth = 0.8; +FrontGapY = FrontPanelZOffset+5; + +// Screws +ScrewDiameter = 3; +ScrewPostDiameter = ScrewDiameter+2; +ScrewPostHeight = 5; +ScrewPostFillet = 2; + +// Display +DisplayWidth = 30+1+1; // active area: 29.42 +DisplayHeight = 15+1+2; // active area: 14.7 +DisplayXDistance = 15*ScalingFactor; // distance from the left of the panel +DisplayYDistance = 3.5+9*ScalingFactor; // distance from the top of the panel +DisplayCornerRadius = 2; +DisplayPegXDistance = 31-0.2; +DisplayPegYDistance = 28.5; +DisplayPegYOffset = 2.25;//-4.5; // 7.35-2=5.35 from the top, 3.25 The display mounting ports are not centered, but have an offset (shifted towards the bottom). + +// frame around the display opening +DisplayFrameZHeight = 0.5; + +// Pegs to hold the display +DisplayPegHeight = 4+1; +DisplayPegDiameter = 3; // 3.5 + +// SDSlot +SDSlotWidth = 12.5; // 12 +SDSlotHeight = 3; +SDSlotCorners = 1; +SDSlotXDistance = BoxX_Width/2-SDSlotWidth/2; // distance from the left of the panel +SDSlotYDistance = 10; // distance from the bottom of the panel to the bottom of the SD slot + +// Square Buttons +ButtonYHeight = (ScalingFactor<1) ? 8.5 : 10; +ButtonXWidth = (ScalingFactor<1) ? 8.5 : 10; +ButonZDepth = SKIN*2+1.5; +ButtonCorners = 1; +ButtonLedgeWidth = 2-0.5; +ButtonPegDiameter = 3.5+0.2-0.1; +ButtonPegDepth = 2.5; + +ButtonXOfs = 15*ScalingFactor; // distance from the right edge +ButtonYOfs = 9*ScalingFactor;//9*ScalingFactor; // distance from the top +ButtonRaster = 2.54; // 0.01" +ButtonXSpacing = ButtonRaster*5; // multiple of 2.54mm => allow micro switches to be soldered on prototype raster PCBs +ButtonYSpacing = ButtonRaster*5; // multiple of 2.54mm => allow micro switches to be soldered on prototype raster PCBs + +ButtonScrewPostHeight = 9-0.5; + +// LEDs +LedDiameter = (ScalingFactor<1) ? 3.4-0.1 : 5; // 3.4 +LedSocketHeight = 1.5; +LedYDistance = (ScalingFactor<1) ? LedDiameter+3 : LedDiameter+3.5; // distance between the two LEDs +LedXOfs = (ScalingFactor<1) ? 22 : 20+LedDiameter/2; // distance from the left edge +LedYOfs = (ScalingFactor<1) ? 16 : 20; + +// FloppyEmu PCB +PcbXWidth = 45-1; +PcbYHeight = 2; +PcbZDepth = 100.0; +PcbXOfs = -0.75+0.5; // from the center. SD Slot on FloppyEmu is not quite centered +PcbYOfs = SDSlotYDistance-PcbYHeight+1; +PcbZOfs = FrontPanelZOffset+SKIN+0.5; +PcbScrewDiameter = 3; +PcbScrewPostDiameter = PcbScrewDiameter+3; +PcbScrewDistance = 2.5; // distance from the PCB edges + +include ; +include ; +include ; +include ; +include ; +include ; + +module main() +{ + if (part == "test") + { + intersection() + { + makeTop(); + translate([-10,BoxY_Height-22,BoxZ_Depth-32]) cube([40, 40, 32]); + } + } + else + if (part == "button") + { + for (x = [0,1]) + for (y = [0,1]) + translate([x*ButtonXSpacing, y*ButtonYSpacing, -ButonZDepth]) button(); + translate([-15, 20, 0]) rotate([0,0,90]) makeCableStrainRelief(); + translate([30, -15, 0]) makeRearPcbFeet(); + } + else + if (part == "rear") + { + makeRear(); + } + else + if (part == "front") + { + makeFront(); + } + else + if (part == "top") { + makeTop(); + } + else + if (part == "topBottom") { + makeTop(); + translate([0, -ComponentSeparation, 0 ]) makeBottom(); + } + else + if (part == "bottom") { + makeBottom(); + } + else + if (part == "all") { + makeTop(); + translate([0, -ComponentSeparation, 0 ]) makeBottom(); + translate([0, 0, ComponentSeparation ]) makeRear(); + translate([0, 0, -ComponentSeparation ]) makeFront(); + } + else + if ((part == "noFront")||(part == "noRear")||(part=="noBottom")||(part=="frontBottom")) + { + if (part != "frontBottom") makeTop(); + if (part != "noBottom") makeBottom(); + if ((part != "noRear")&&(part != "frontBottom")) makeRear(); + if (part != "noFront") makeFront(); + } + else + if (part == "prototype") { + boxScrewPosts(); + } else { + // nothing! + } + if ((showPcb == "yes")&&(part != "button")) makePcb(); +} + +main(); diff --git a/scad/components/pcb.scad b/scad/components/pcb.scad new file mode 100644 index 0000000..8d9347e --- /dev/null +++ b/scad/components/pcb.scad @@ -0,0 +1,44 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +// floppy emu PCB +module makePcb() +{ + translate([BoxX_Width/2 - PcbXWidth/2+PcbXOfs, PcbYOfs, PcbZOfs]) + { + difference() + { + // PCB + cube([PcbXWidth, PcbYHeight, PcbZDepth]); + // front holes + translate([PcbScrewDistance,0,PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, PcbYHeight); + translate([PcbXWidth-PcbScrewDistance,0,PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, PcbYHeight); + // rear holes + translate([PcbScrewDistance,0,PcbZDepth-PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, PcbYHeight); + translate([PcbXWidth-PcbScrewDistance,0,PcbZDepth-PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, PcbYHeight); + } + } +} + +module makeRearPcbFeet(center) +{ + //translate([BoxX_Width/2 - PcbXWidth/2+PcbXOfs, PcbYOfs, PcbZDepth+PcbZOfs]) + rotate([90,0,90]) + { + difference() + { + translate([-PcbScrewDiameter/2, -SKIN, -PcbScrewDistance-PcbScrewDiameter]) + cube([PcbXWidth+PcbScrewDiameter, SKIN, PcbScrewDiameter*2]); + // holes + translate([PcbScrewDistance,-SKIN,-PcbScrewDistance]) rotate([270, 0, 0]) + hole(PcbScrewDiameter, PcbYHeight); + translate([PcbXWidth-PcbScrewDistance,-SKIN,-PcbScrewDistance]) rotate([270, 0, 0]) + hole(PcbScrewDiameter, PcbYHeight); + } + FeetYHeight = PcbYOfs-SKIN; + translate([PcbScrewDistance,-SKIN,-PcbScrewDistance]) rotate([90, 0, 0]) + screwPost(FeetYHeight, PcbScrewDiameter*2, PcbScrewDiameter, FeetYHeight, 0); + translate([PcbXWidth-PcbScrewDistance,-SKIN,-PcbScrewDistance]) rotate([90, 0, 0]) + screwPost(FeetYHeight, PcbScrewDiameter*2, PcbScrewDiameter, FeetYHeight, 0); + } +} diff --git a/scad/components/rear.scad b/scad/components/rear.scad new file mode 100644 index 0000000..cd86b56 --- /dev/null +++ b/scad/components/rear.scad @@ -0,0 +1,91 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +// rear panel +module makeRear() +{ + Width = BoxX_Width-2*RearWallGap; + difference() + { + translate([RearWallGap, 0, BoxZ_Depth-RearWallZOffset-RearWallZLedge]) + rotate([90, 0, 90]) + { + difference() + { + roundedCube(BoxY_Height, RearWallZLedge, Width, Box_Corner_Radius); + translate([SKIN, -SKIN, 0]) roundedCube(BoxY_Height, RearWallZLedge, Width, Box_Corner_Radius); + translate([BoxY_Height-RearWallGap-RearWallCableGapHeight,RearWallZLedge-SKIN*2,0]) + cube([RearWallCableGapHeight+10, SKIN*2, Width]); + } + } + + // hole + translate([FrontGapX/2, -SKIN , BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // hole + translate([BoxX_Width-FrontGapX/2, -SKIN , BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + } + + // long edge, top of rear wall + translate([RearWallGap, BoxY_Height-RearWallGap-RearWallCableGapHeight-5, BoxZ_Depth-RearWallZOffset-SKIN]) + roundedCube(BoxX_Width-RearWallCableGapWidth-RearWallCableGapXOffset, RearWallCableGapHeight+5, SKIN, Box_Corner_Radius); + + // short edge, top of rear wall + translate([BoxX_Width-RearWallGap-RearWallCableGapXOffset, BoxY_Height-RearWallGap-RearWallCableGapHeight-5, BoxZ_Depth-RearWallZOffset-SKIN]) + roundedCube(RearWallCableGapXOffset, RearWallCableGapHeight+5, SKIN, Box_Corner_Radius); + + // screw posts + translate([FrontGapX/2, SKIN , BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, ScrewPostFillet); + + translate([BoxX_Width-FrontGapX/2, SKIN , BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, ScrewPostFillet); + + // cable strain relief + translate([BoxX_Width-RearWallGap-RearWallCableGapXOffset+ScrewPostDiameter, BoxY_Height-CableStrainReliefYOffset, BoxZ_Depth-RearWallZOffset-SKIN]) + rotate([180,0,0]) screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, ScrewPostFillet); + translate([BoxX_Width-RearWallGap-RearWallCableGapXOffset-RearWallCableGapWidth, BoxY_Height-CableStrainReliefYOffset, BoxZ_Depth-RearWallZOffset-SKIN]) + rotate([180,0,0]) screwPost(ScrewPostHeight, ScrewPostDiameter, ScrewDiameter, ScrewPostHeight, ScrewPostFillet); + StrainReliefHeight=2; + CableThickness = 0; + translate([BoxX_Width-RearWallGap-RearWallCableGapXOffset-RearWallCableGapWidth+ScrewPostDiameter/2, BoxY_Height-CableStrainReliefYOffset-StrainReliefHeight/2, BoxZ_Depth-RearWallZOffset-SKIN-ScrewPostHeight+CableThickness]) + cube([RearWallCableGapWidth, StrainReliefHeight, ScrewPostHeight-CableThickness]); // 0.5mm to allow a bit space for the cable + + // support triangles for stability + SupportWidth = RearWallZLedge-SKIN*2; + // left + translate([RearWallGap+Box_Corner_Radius, SKIN, BoxZ_Depth-RearWallZOffset-SupportWidth-SKIN]) + rotate([270, 180, 0]) prism(SKIN, SupportWidth, SupportWidth); + // right + translate([BoxX_Width-Box_Corner_Radius-SKIN, SKIN, BoxZ_Depth-RearWallZOffset-SupportWidth-SKIN]) + rotate([270, 180, 0]) prism(SKIN, SupportWidth, SupportWidth); + // center + translate([(BoxX_Width-Box_Corner_Radius-SKIN)/2, SKIN, BoxZ_Depth-RearWallZOffset-SupportWidth-SKIN]) + rotate([270, 180, 0]) prism(SKIN, SupportWidth, SupportWidth); +} + +module makeCableStrainRelief() +{ + StrainReliefHeight=4+SKIN; + + difference() + { + union() + { + // cable strain relief + translate([ScrewPostDiameter, 0, 0]) + rotate([180,0,0]) screwPost(StrainReliefHeight, ScrewPostDiameter, ScrewDiameter, StrainReliefHeight, 0); + translate([-RearWallCableGapWidth, 0, 0]) + rotate([180,0,0]) screwPost(StrainReliefHeight, ScrewPostDiameter, ScrewDiameter, StrainReliefHeight, 0); + + translate([-RearWallCableGapWidth+ScrewPostDiameter/3, -(StrainReliefHeight-SKIN)/2, -(StrainReliefHeight-SKIN)]) + cube([RearWallCableGapWidth+1, (StrainReliefHeight-SKIN), StrainReliefHeight-SKIN]); // 0.5mm to allow a bit space for the cable + translate([-RearWallCableGapWidth,-ScrewPostDiameter/2,-SKIN]) + cube([RearWallCableGapWidth+ScrewPostDiameter, ScrewPostDiameter, SKIN]); + } + translate([ScrewPostDiameter,0,-SKIN]) hole(ScrewDiameter, SKIN); + translate([-RearWallCableGapWidth,0,-SKIN]) hole(ScrewDiameter, SKIN); + } +} diff --git a/scad/components/top_and_bottom.scad b/scad/components/top_and_bottom.scad new file mode 100644 index 0000000..d96c31c --- /dev/null +++ b/scad/components/top_and_bottom.scad @@ -0,0 +1,312 @@ +// Copyright 2022 Thorsten Brehm +// brehmt (at) gmail dot com + +module imprint(x,y,z) +{ + Font = "Liberation Sans:style=Bold"; + FontSize = 3; + translate([x,y,z]) + rotate([270,0,0]) + { + linear_extrude(height = 0.4) { text(IMPRINT, font = Font, size = FontSize, halign="center", valign="bottom"); } + } +} + + +// little mounting point, so the base plate does not drop when glueing to the top shell +module makeGlueHelper(Width) +{ + SupportAngle = 35; + intersection() + { + union() + { + translate([-20, 0, 0]) + rotate([0, 0, SupportAngle]) + translate([-20,-25,-Width/2]) + cube([40, 50, Width]); + } + translate([0,0,-Width/2]) cube([40,50,Width]); + } +} + +// little mounting points to help when glueing the base to the top shell +module makeGlueMountingPoints() +{ + SupportWidth = 5; // 10mm + //SupportXOffset = -36.9; + SupportXOffset1 = -28;//34.9; + //SupportXOffset2 = SupportXOffset1+32.1; + SupportXOffset2 = 32.1; + // support for bottom curved edges + SupportYOffset = 0; + + FrontZ = FrontPanelZLedge+FrontPanelZOffset+SKIN*5; // front position + RearZ = BoxZ_Depth-RearWallZLedge-RearWallZOffset-5*SKIN; // rear position + intersection() + { + union() + { + for (z=[FrontZ, RearZ, (FrontZ+RearZ)/2]) + { + // right + translate([0, SupportYOffset, z]) makeGlueHelper(SupportWidth); + + // left + translate([BoxX_Width, SupportYOffset, z]) rotate([0, 180, 0]) makeGlueHelper(SupportWidth); + } + } + // cut away anything which is outside the shell + outerBox(SKIN, BoxX_Width, BoxY_Height, BoxZ_Depth, Box_Corner_Radius); + } +} + +module makeTopStiffeners() +{ + SupportWidth = 15; + // front right + translate([0, BoxY_Height-SupportWidth, FrontPanelZOffset+SKIN*2+15]) rotate([0,90,0]) prism(SKIN, SupportWidth, SupportWidth); + // front left + translate([BoxX_Width, BoxY_Height-SupportWidth, FrontPanelZOffset+SKIN+15]) rotate([0,270,0]) prism(SKIN, SupportWidth, 10); + // center right + translate([0, BoxY_Height-SupportWidth, BoxZ_Depth/2]) rotate([0,90,0]) prism(SKIN, SupportWidth, SupportWidth); + // center left + translate([BoxX_Width, BoxY_Height-SupportWidth, BoxZ_Depth/2-SKIN]) rotate([0,270,0]) prism(SKIN, SupportWidth, SupportWidth); + // rear right + translate([0, BoxY_Height-SupportWidth, BoxZ_Depth-RearWallZOffset-SKIN-3-2]) rotate([0,90,0]) prism(SKIN, SupportWidth, SupportWidth); + // rear left + translate([BoxX_Width, BoxY_Height-SupportWidth, BoxZ_Depth-RearWallZOffset-3-SKIN*2-2]) rotate([0,270,0]) prism(SKIN, SupportWidth, SupportWidth); +} + +module roundedEdge() +{ + translate([-SKIN, 0, 30]) rotate([270, 0, 0]) roundedCube(2, 30, SKIN, 1); +} + +module roundCorner() +{ + $fn=24; + Radius = TopGrooveCornerRadius/2; + translate([0,1,0]) rotate([270, 0, 0]) + translate([-Radius-TopGrooveYDepth, -Radius-TopGrooveYDepth, 0]) + intersection() + { + torus(Radius, TopGrooveYDepth); + cube([Radius*2, Radius*2, TopGrooveYDepth]); + } +} + +module makeRoundGapEdges() +{ + // front left + translate([FrontGapX-FrontGapWidth/2-1, -SKIN, 0]) roundedEdge(); + translate([FrontGapX+FrontGapWidth/2+1, -SKIN, 0]) roundedEdge(); + + // front right + translate([BoxX_Width-FrontGapX-FrontGapWidth/2-1, -SKIN, 0]) roundedEdge(); + translate([BoxX_Width-FrontGapX+FrontGapWidth/2+1, -SKIN, 0]) roundedEdge(); + + // rear left + translate([FrontGapX-FrontGapWidth/2-1, -SKIN, BoxZ_Depth-30]) roundedEdge(); + + // rear right + translate([BoxX_Width-FrontGapX+FrontGapWidth/2+1, -SKIN, BoxZ_Depth-30]) roundedEdge(); +} + +module makeGaps() +{ + // front gaps + translate([FrontGapX-FrontGapWidth/2-1, -SKIN, 0]) cube([FrontGapWidth+2, SKIN*2, FrontGapY]); + translate([BoxX_Width-FrontGapX-FrontGapWidth/2-1, -SKIN, 0]) cube([FrontGapWidth+2, SKIN*2, FrontGapY]); + + // rear gap + translate([FrontGapX-FrontGapWidth/2-1, -SKIN, BoxZ_Depth-10]) cube([BoxX_Width-2*FrontGapX+FrontGapWidth+2, SKIN*2, 10]); +} + +module ventilationCut() +{ + translate([-SKIN, BoxY_Height-VentilationYHeight,BoxZ_Depth-VentilationZOffset]) rotate([0, 90, 0]) + roundedCube(VentilationWidth, VentilationYHeight, SKIN, VentilationWidth/2); + + translate([BoxX_Width, BoxY_Height-VentilationYHeight,BoxZ_Depth-VentilationZOffset]) rotate([0, 90, 0]) + roundedCube(VentilationWidth, VentilationYHeight, SKIN, VentilationWidth/2); + + translate([-SKIN-VentilationYHeight+VentilationXLength, BoxY_Height+SKIN,BoxZ_Depth-VentilationZOffset]) rotate([90, 90, 0]) + roundedCube(VentilationWidth, VentilationYHeight, SKIN+5, VentilationWidth/2); + + translate([BoxX_Width-VentilationXLength, BoxY_Height+SKIN,BoxZ_Depth-VentilationZOffset]) rotate([90, 90, 0]) + roundedCube(VentilationWidth, VentilationYHeight, SKIN+5, VentilationWidth/2); +} + +module makeVentilation() +{ + for (z = [0 : 1: VentilationCount-1]) + { + translate([0, 0, -z*VentilationZDistance]) ventilationCut(); + } +} + + +module makeScrewHoles() +{ + // front hole + translate([FrontGapX/2, -SKIN, FrontPanelZOffset+FrontPanelZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // front hole + translate([BoxX_Width-FrontGapX/2, -SKIN, FrontPanelZOffset+FrontPanelZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // rear hole + translate([FrontGapX/2, -SKIN , BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // rear hole + translate([BoxX_Width-FrontGapX/2, -SKIN, BoxZ_Depth-RearWallZOffset-RearWallZLedge/2]) rotate([270, 0, 0]) + hole(ScrewDiameter, 10); + + // rear PCB holes + translate([BoxX_Width/2-PcbXWidth/2, 0, PcbZOfs]) + { + translate([PcbScrewDistance, -SKIN,PcbZDepth-PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, SKIN); + translate([PcbXWidth-PcbScrewDistance, -SKIN,PcbZDepth-PcbScrewDistance]) rotate([270, 0, 0]) hole(PcbScrewDiameter, SKIN); + } +} + +GrooveSupportZOffset = (BoxZ_Depth-2*TopGrooveZLength)/3; +GrooveXOfs = BoxX_Width/2-(TopGrooveXWidth+4)/2; + +module makeTopGroovesSupport() +{ + translate([GrooveXOfs, BoxY_Height-TopGrooveYDepth, GrooveSupportZOffset+TopGrooveZLength+2]) rotate([270, 0, 0]) + roundedCube(TopGrooveXWidth+4, TopGrooveZLength+4, TopGrooveYDepth, TopGrooveCornerRadius); + + translate([GrooveXOfs, BoxY_Height-TopGrooveYDepth, GrooveSupportZOffset*2+TopGrooveZLength*2+2]) rotate([270, 0, 0]) + roundedCube(TopGrooveXWidth+4, TopGrooveZLength+4, TopGrooveYDepth, TopGrooveCornerRadius); +} + +module makeTopGroovesEdges() +{ + for (z=[GrooveSupportZOffset,2*GrooveSupportZOffset+TopGrooveZLength]) + translate([GrooveXOfs+2, BoxY_Height-TopGrooveYDepth+SKIN, z]) + { + // right edge + translate([0, 0, 4+TopGrooveZLength/2-5]) + cylinderLedge(TopGrooveYDepth, TopGrooveZLength); + + // left edge + translate([TopGrooveXWidth, 0, 4+TopGrooveZLength/2-5]) + rotate([0, 180, 0]) cylinderLedge(TopGrooveYDepth, TopGrooveZLength); + + // front edge + translate([TopGrooveXWidth/2, 0, 0]) + rotate([0, 270, 0]) cylinderLedge(TopGrooveYDepth, TopGrooveXWidth); + + // rear edge + translate([TopGrooveXWidth/2, 0, TopGrooveZLength]) + rotate([0, 90, 0]) cylinderLedge(TopGrooveYDepth, TopGrooveXWidth); + + // rear right corner + translate([TopGrooveXWidth, -1, 0]) roundCorner(); + + // rear left corner + translate([0, -1, 0]) rotate([0, 90, 0]) roundCorner(); + + // front right corner + translate([TopGrooveXWidth, -1, TopGrooveZLength]) rotate([0, 270, 0]) roundCorner(); + + // front left corner + translate([0, -1, TopGrooveZLength]) rotate([0, 180, 0]) roundCorner(); + + } +} + +module makeTopGrooves() +{ + GrooveZOffset = (BoxZ_Depth-2*TopGrooveZLength)/3; + translate([BoxX_Width/2-TopGrooveXWidth/2, BoxY_Height-TopGrooveYDepth+SKIN, GrooveZOffset+TopGrooveZLength]) rotate([270, 0, 0]) + roundedCube(TopGrooveXWidth, TopGrooveZLength, TopGrooveYDepth, TopGrooveCornerRadius); + + translate([BoxX_Width/2-TopGrooveXWidth/2, BoxY_Height-TopGrooveYDepth+SKIN, GrooveZOffset*2+TopGrooveZLength*2]) rotate([270, 0, 0]) + roundedCube(TopGrooveXWidth, TopGrooveZLength, TopGrooveYDepth, TopGrooveCornerRadius); +} + +// little supports to prevent the front and rear panels from bending into the case +module makeTopPanelSupports() +{ + Distance = 10; // distance of the supports from the left/right edge of the box + SupportWidth = 3; // x,z size of the supports + SupportYHeight = 4; // y size of the supports (cube) + // front left + translate([BoxX_Width-5, BoxY_Height-SupportYHeight, FrontPanelZOffset+SKIN]) + cube([SupportWidth, SupportYHeight, SupportWidth]); + // front right + translate([18, BoxY_Height-SupportYHeight, FrontPanelZOffset+SKIN]) + cube([SupportWidth, SupportYHeight, SupportWidth]); + // front center + translate([BoxX_Width/2-4, BoxY_Height-SupportYHeight, FrontPanelZOffset+SKIN]) + cube([SupportWidth, SupportYHeight, SupportWidth]); + + // rear + translate([BoxX_Width-RearWallCableGapXOffset/2-SupportWidth, + BoxY_Height-SupportYHeight, BoxZ_Depth-RearWallZOffset-SKIN-SupportWidth]) + cube([SupportWidth, SupportYHeight, SupportWidth]); + translate([Distance, BoxY_Height-SupportYHeight, BoxZ_Depth-RearWallZOffset-SKIN-SupportWidth]) + cube([SupportWidth, SupportYHeight, SupportWidth]); +} + + +module makeThing() +{ + union() + { + difference() + { + union() + { + box(SKIN, BoxX_Width, BoxY_Height, BoxZ_Depth, Box_Corner_Radius); + makeTopGroovesSupport(); + makeGlueMountingPoints(); + } + makeGaps(); + makeVentilation(); + makeTopGrooves(); + makeScrewHoles(); + + if (prototype == "yes") + { + removePrototypeAreas(); + } + } + makeRoundGapEdges(); + makeTopGroovesEdges(); + makeTopPanelSupports(); + makeTopStiffeners(); + } +} + +module makeMask() +{ + XOffs = 3.5*ScalingFactor; + translate([XOffs/2, -BoxY_Height, 0]) cube([BoxX_Width-XOffs, BoxY_Height+SKIN, BoxZ_Depth]); +} + +module makeTop() +{ + difference() + { + makeThing(); + makeMask(); + } +} + +module makeBottom() +{ + intersection() + { + makeThing(); + makeMask(); + } + imprint(BoxX_Width/2,0,BoxZ_Depth-RearWallZLedge-RearWallZOffset); +} + diff --git a/scad/main.scad b/scad/main.scad index d094149..11778ab 100644 --- a/scad/main.scad +++ b/scad/main.scad @@ -18,7 +18,7 @@ LEDs = "one"; // [one:One / Activity LED,two:Both / Activity and Power LEDs] /* [Internals] */ // Base board imprint -IMPRINT = "MADE IN GERMANY"; // 20 +IMPRINT = "MADE IN GERMANY"; // 30 // Distance of the components (when showing 'exploded' elements) SEPARATION = 30; // [0: 1: 100]