From 86f94ed2b866e9f905ab4b5ded96e67a8f86527c Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 28 Aug 2014 23:47:34 -0700 Subject: [PATCH] Initial checkin. Still needs work. --- .gitignore | 3 + GDMUtils.scad | 832 ++++++++++++++++++++++++++++++++ Makefile | 22 + TODO.md | 12 + cap_parts.scad | 23 + config.scad | 32 ++ drive_gear_parts.scad | 62 +++ full_assembly.scad | 142 ++++++ joiners.scad | 144 ++++++ motor_mount_plate_parts.scad | 42 ++ publicDomainGearV1.1.scad | 177 +++++++ rail_with_motor_mount_part.scad | 111 +++++ rails_90deg_joint_part.scad | 126 +++++ rails_end_part.scad | 67 +++ rails_part.scad | 81 ++++ roller_parts.scad | 40 ++ sled_end_parts.scad | 63 +++ slider_sled.scad | 121 +++++ support_leg_part.scad | 40 ++ xy_joiner_parts.scad | 77 +++ xy_slider_part.scad | 19 + z_platform_joint_part.scad | 48 ++ z_sled_part.scad | 18 + 23 files changed, 2302 insertions(+) create mode 100644 .gitignore create mode 100644 GDMUtils.scad create mode 100644 Makefile create mode 100644 TODO.md create mode 100644 cap_parts.scad create mode 100644 config.scad create mode 100644 drive_gear_parts.scad create mode 100644 full_assembly.scad create mode 100644 joiners.scad create mode 100644 motor_mount_plate_parts.scad create mode 100644 publicDomainGearV1.1.scad create mode 100644 rail_with_motor_mount_part.scad create mode 100644 rails_90deg_joint_part.scad create mode 100644 rails_end_part.scad create mode 100644 rails_part.scad create mode 100644 roller_parts.scad create mode 100644 sled_end_parts.scad create mode 100644 slider_sled.scad create mode 100644 support_leg_part.scad create mode 100644 xy_joiner_parts.scad create mode 100644 xy_slider_part.scad create mode 100644 z_platform_joint_part.scad create mode 100644 z_sled_part.scad diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50eecc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.deps +*.stl + diff --git a/GDMUtils.scad b/GDMUtils.scad new file mode 100644 index 0000000..c17fd2b --- /dev/null +++ b/GDMUtils.scad @@ -0,0 +1,832 @@ +// Convenience modules. + + +// Rotates children around the Z axis by the given number of degrees. +// Example: +// xrot(90) cylinder(h=10, r=2, center=true); +module xrot(a=0) +{ + rotate([a, 0, 0]) + children(); +} + + +// Rotates children around the Y axis by the given number of degrees. +// Example: +// yrot(90) cylinder(h=10, r=2, center=true); +module yrot(a=0) +{ + rotate([0, a, 0]) + children(); +} + + +// Rotates children around the Z axis by the given number of degrees. +// Example: +// zrot(90) cube(size=[9,1,4], center=true); +module zrot(a=0) +{ + rotate([0, 0, a]) + children(); +} + + + +module skew_along_x(yang=0, zang=0) +{ + multmatrix(m = [ + [1, 0, 0, 0], + [sin(yang), 1, 0, 0], + [sin(zang), 0, 1, 0], + [0, 0, 0, 1] + ]) { + children(); + } +} + + + +module skew_along_y(xang=0, zang=0) +{ + multmatrix(m = [ + [1, sin(xang), 0, 0], + [0, 1, 0, 0], + [0, sin(zang), 1, 0], + [0, 0, 0, 1] + ]) { + children(); + } +} + + + +module skew_along_z(xang=0, yang=0) +{ + multmatrix(m = [ + [1, 0, sin(xang), 0], + [0, 1, sin(yang), 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]) { + children(); + } +} + + + +module mirror_copy(v=[0,0,1]) +{ + union() { + children(); + mirror(v) children(); + } +} + + +// Given a number of euller angles, rotates copies of the given children to each of those angles. +// Example: +// rot_copies(rots=[[0,0,0],[45,0,0],[0,45,120],[90,-45,270]]) +// translate([6,0,0]) cube(size=[9,1,4], center=true); +module rot_copies(rots=[[0,0,0]]) +{ + for (rot = rots) + rotate(rot) + children(); +} + + + +// Given an array of angles, rotates copies of the children to each of those angles around the X axis. +// Example: +// xrot_copies(rots=[0,15,30,60,120,240]) translate([0,6,0]) cube(size=[4,9,1], center=true); +module xrot_copies(rots=[0]) +{ + for (a = rots) + rotate([a, 0, 0]) + children(); +} + + +// Given an array of angles, rotates copies of the children to each of those angles around the Y axis. +// Example: +// yrot_copies(rots=[0,15,30,60,120,240]) translate([6,0,0]) cube(size=[9,4,1], center=true); +module yrot_copies(rots=[0]) +{ + for (a = rots) + rotate([0, a, 0]) + children(); +} + + +// Given an array of angles, rotates copies of the children to each of those angles around the Z axis. +// Example: +// zrot_copies(rots=[0,15,30,60,120,240]) translate([6,0,0]) cube(size=[9,1,4], center=true); +module zrot_copies(rots=[0]) +{ + for (a = rots) + rotate([0, 0, a]) + children(); +} + + +// Makes copies of the given children at each of the given offsets. +// offsets = array of XYZ offset vectors. Default [[0,0,0]] +// Example: +// translate_copies([[-5,-5,0], [5,-5,0], [0,-5,7], [0,5,0]]) +// sphere(r=3,center=true); +module translate_copies(offsets=[[0,0,0]]) +{ + for (off = offsets) + translate(off) + children(); +} + + +// Makes a 3D XYZ grid of duplicate children. +// xa = array or range of X-axis values to offset by. (Default: [0]) +// ya = array or range of Y-axis values to offset by. (Default: [0]) +// za = array or range of Z-axis values to offset by. (Default: [0]) +// Examples: +// grid_of(xa=[0,2,3,5],ya=[3:5],za=[-4:2:6]) +// sphere(r=1,center=true); +// grid_of(ya=[-6:3:6],za=[4,7]) +// sphere(r=1,center=true); +module grid_of(xa=[0], ya=[0], za=[0]) +{ + for (xoff = xa) + for (yoff = ya) + for (zoff = za) + translate([xoff,yoff,zoff]) + children(); +} + + +// Evenly distributes n duplicate children around a circle on the XY plane. +// n = number of copies to distribute around the circle. (Default: 6) +// r = radius of circle (Default: 1) +// rx = radius of ellipse on X axis. Used instead of r. +// ry = radius of ellipse on Y axis. Used instead of r. +// d = diameter of circle. (Default: 2) +// dx = diameter of ellipse on X axis. Used instead of d. +// dy = diameter of ellipse on Y axis. Used instead of d. +// rot = whether to rotate the copied children. (Default: false) +// sa = starting angle. (Default: 0.0) +// ea = ending angle. Will distribute copies CCW from sa to ea. (Default: 360.0) +// Examples: +// circle_of(d=8,n=5) +// cube(size=[3,1,1],center=true); +// circle_of(r=10,n=12,rot=true) +// cube(size=[3,1,1],center=true); +// circle_of(rx=15,ry=10,n=12,rot=true) +// cube(size=[3,1,1],center=true); +// circle_of(r=10,n=5,rot=true,sa=30.0,ea=150.0) +// cube(size=[3,1,1],center=true); +// +module circle_of( + n=6, + r=1, rx=undef, ry=undef, + d=undef, dx=undef, dy=undef, + sa=0.0, ea=360.0, + rot=false +) { + r = (d == undef)?r:(d/2.0); + rx = (dx == undef)?rx:(dx/2.0); + ry = (dy == undef)?rx:(dy/2.0); + rx = (rx == undef)?r:rx; + ry = (ry == undef)?r:ry; + sa = ((sa % 360.0) + 360.0) % 360.0; // make 0 < ang < 360 + ea = ((ea % 360.0) + 360.0) % 360.0; // make 0 < ang < 360 + n = (abs(ea-sa)<0.01)?(n+1):n; + delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1); + for (ang = [sa:delt:(sa+delt*(n-1))]) + if (abs(abs(ang-sa)-360.0) > 0.01) + translate([cos(ang)*rx,sin(ang)*ry,0]) + rotate([0,0,rot?atan2(sin(ang)*ry,cos(ang)*rx):0]) + children(); +} + + + +// Evenly distributes n duplicate children along an XYZ line. +// p1 = starting point of line. (Default: [0,0,0]) +// p2 = ending point of line. (Default: [10,0,0]) +// n = number of copies to distribute along the line. (Default: 6) +// Examples: +// line_of(p1=[0,0,0], p2=[-10,15,20], n=5) +// cube(size=[3,1,1],center=true); +// +module line_of(p1=[0,0,0], p2=[10,0,0], n=6) +{ + delta = (p2 - p1) / (n-1); + for (i = [0:n-1]) + translate([delta[0]*i,delta[1]*i,delta[2]*i]) + children(); +} + + +// Makes a cube with rounded edges and corners. +// size = size of cube [X,Y,Z]. (Default: [1,1,1]) +// r = radius of edge/corner rounding. (Default: 0.25) +// Examples: +// rcube(size=[9,4,1], r=0.333, center=true, $fn=24); +// rcube(size=[5,7,3], r=1); +module rcube(size=[1,1,1], r=0.25, center=false, $fn=undef) +{ + $fn = ($fn==undef)?max(18,floor(180/asin(1/r)/2)*2):$fn; + xoff=abs(size[0])/2-r; + yoff=abs(size[1])/2-r; + zoff=abs(size[2])/2-r; + offset = center?[0,0,0]:size/2; + translate(offset) { + union() { + grid_of([-xoff,xoff],[-yoff,yoff],[-zoff,zoff]) + sphere(r=r,center=true,$fn=$fn); + grid_of(xa=[-xoff,xoff],ya=[-yoff,yoff]) + cylinder(r=r,h=zoff*2,center=true,$fn=$fn); + grid_of(xa=[-xoff,xoff],za=[-zoff,zoff]) + rotate([90,0,0]) + cylinder(r=r,h=yoff*2,center=true,$fn=$fn); + grid_of(ya=[-yoff,yoff],za=[-zoff,zoff]) + rotate([90,0,0]) + rotate([0,90,0]) + cylinder(r=r,h=xoff*2,center=true,$fn=$fn); + cube(size=[xoff*2,yoff*2,size[2]], center=true); + cube(size=[xoff*2,size[1],zoff*2], center=true); + cube(size=[size[0],yoff*2,zoff*2], center=true); + } + } +} + + + +// Makes a cube with rounded vertical edges. +// size = size of cube [X,Y,Z]. (Default: [1,1,1]) +// r = radius of edge/corner rounding. (Default: 0.25) +// Examples: +// rrect(size=[9,4,1], r=1, center=true); +// rrect(size=[5,7,3], r=1, $fn=24); +module rrect(size=[1,1,1], r=0.25, center=false, $fn=undef) +{ + $fn = ($fn==undef)?max(18,floor(180/asin(1/r)/2)*2):$fn; + xoff=abs(size[0])/2-r; + yoff=abs(size[1])/2-r; + offset = center?[0,0,0]:size/2; + translate(offset) { + union(){ + grid_of([-xoff,xoff],[-yoff,yoff]) + cylinder(r=r,h=size[2],center=true,$fn=$fn); + cube(size=[xoff*2,size[1],size[2]], center=true); + cube(size=[size[0],yoff*2,size[2]], center=true); + } + } +} + + + +// Makes a cube with chamfered edges. +// size = size of cube [X,Y,Z]. (Default: [1,1,1]) +// chamfer = chamfer inset along axis. (Default: 0.25) +module chamfcube( + size=[1,1,1], + chamfer=0.25 +) { + ch_width = sqrt(2)*chamfer; + ch_offset = 1; + difference() { + cube(size=size, center=true); + for (xs = [-1,1]) { + for (ys = [-1,1]) { + translate([0,xs*size[1]/2,ys*size[2]/2]) { + rotate(a=[45,0,0]) + cube(size=[size[0]+0.1,ch_width,ch_width], center=true); + } + translate([xs*size[0]/2,0,ys*size[2]/2]) { + rotate(a=[0,45,0]) + cube(size=[ch_width,size[1]+0.1,ch_width], center=true); + } + translate([xs*size[0]/2,ys*size[1]/2],0) { + rotate(a=[0,0,45]) + cube(size=[ch_width,ch_width,size[2]+0.1], center=true); + } + } + } + } +} + + + +// Makes a teardrop shape in the XZ plane. Useful for 3D printable holes. +// r = radius of circular part of teardrop. (Default: 1) +// h = thickness of teardrop. (Default: 1) +// Example: +// teardrop(r=3,h=2); +module teardrop(r=1, h=1, $fn=undef) +{ + $fn = ($fn==undef)?max(12,floor(180/asin(1/r)/2)*2):$fn; + rotate([90,0,0]) rotate([0,0,45]) union() { + translate([r/2,r/2,0]) + cube(size=[r,r,h], center=true); + cylinder(h=h, r=r, center=true); + } +} + + + +// Makes a simple threadless screw, useful for making screwholes. +// screwsize = diameter of threaded part of screw. +// screwlen = length of threaded part of screw. +// headsize = diameter of the screw head. +// headlen = length of the screw head. +// Example: +// screw(screwsize=3,screwlen=10,headsize=6,headlen=3); +module screw(screwsize=3,screwlen=10,headsize=6,headlen=3,$fn=undef) +{ + $fn = ($fn==undef)?max(8,floor(180/asin(2/screwsize)/2)*2):$fn; + translate([0,0,-(screwlen)/2]) + cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=$fn); + translate([0,0,(headlen)/2]) + cylinder(r=headsize/2, h=headlen, center=true, $fn=$fn*2); +} + + +function get_metric_bolt_head_size(size) = lookup(size, [ + [ 4.0, 7.0], + [ 5.0, 8.0], + [ 6.0, 10.0], + [ 7.0, 11.0], + [ 8.0, 13.0], + [10.0, 16.0], + [12.0, 18.0], + [14.0, 21.0], + [16.0, 24.0], + [18.0, 27.0], + [20.0, 30.0] + ]); + + +function get_metric_nut_size(size) = lookup(size, [ + [ 2.0, 4.0], + [ 2.5, 5.0], + [ 3.0, 5.5], + [ 4.0, 7.0], + [ 5.0, 8.0], + [ 6.0, 10.0], + [ 7.0, 11.0], + [ 8.0, 13.0], + [10.0, 17.0], + [12.0, 19.0], + [14.0, 22.0], + [16.0, 24.0], + [18.0, 27.0], + [20.0, 30.0], + ]); + + +function get_metric_nut_thickness(size) = lookup(size, [ + [ 2.0, 1.6], + [ 2.5, 2.0], + [ 3.0, 2.4], + [ 4.0, 3.2], + [ 5.0, 4.0], + [ 6.0, 5.0], + [ 7.0, 5.5], + [ 8.0, 6.5], + [10.0, 8.0], + [12.0, 10.0], + [14.0, 11.0], + [16.0, 13.0], + [18.0, 15.0], + [20.0, 16.0] + ]); + + + +// Makes an unthreaded model of a standard nut for a standard metric screw. +// size = standard metric screw size in mm. (Default: 3) +// hole = include an unthreaded hole in the nut. (Default: true) +// Example: +// metric_nut(size=8, hole=true); +// metric_nut(size=3, hole=false); +module metric_nut(size=3, hole=true, $fn=undef) +{ + $fn = ($fn==undef)?max(8,floor(180/asin(2/size)/2)*2):$fn; + radius = get_metric_nut_size(size)/2/cos(30); + thick = get_metric_nut_thickness(size); + translate([0,0,thick/2]) difference() { + cylinder(r=radius, h=thick, center=true, $fn=6); + if (hole == true) + cylinder(r=size/2, h=thick+0.5, center=true, $fn=$fn); + } +} + + + +function get_lmXuu_bearing_diam(size) = lookup(size, [ + [ 4.0, 8.0], + [ 5.0, 10.0], + [ 6.0, 12.0], + [ 8.0, 15.0], + [ 10.0, 19.0], + [ 12.0, 21.0], + [ 13.0, 23.0], + [ 16.0, 28.0], + [ 20.0, 32.0], + [ 25.0, 40.0], + [ 30.0, 45.0], + [ 35.0, 52.0], + [ 40.0, 60.0], + [ 50.0, 80.0], + [ 60.0, 90.0], + [ 80.0, 120.0], + [100.0, 150.0] + ]); + + +function get_lmXuu_bearing_length(size) = lookup(size, [ + [ 4.0, 12.0], + [ 5.0, 15.0], + [ 6.0, 19.0], + [ 8.0, 24.0], + [ 10.0, 29.0], + [ 12.0, 30.0], + [ 13.0, 32.0], + [ 16.0, 37.0], + [ 20.0, 42.0], + [ 25.0, 59.0], + [ 30.0, 64.0], + [ 35.0, 70.0], + [ 40.0, 80.0], + [ 50.0, 100.0], + [ 60.0, 110.0], + [ 80.0, 140.0], + [100.0, 175.0] + ]); + + +// Creates a model of a clamp to hold a given linear bearing cartridge. +// d = Diameter of linear bearing. (Default: 15) +// l = Length of linear bearing. (Default: 24) +// tab = Clamp tab height. (Default: 7) +// tabwall = Clamp Tab thickness. (Default: 5) +// wall = Wall thickness of clamp housing. (Default: 3) +// gap = Gap in clamp. (Default: 5) +// screwsize = Size of screw to use to tighten clamp. (Default: 3) +module linear_bearing_housing(d=15,l=24,tab=7,gap=5,wall=3,tabwall=5,screwsize=3) +{ + od = d+2*wall; + ogap = gap+2*tabwall; + tabh = tab/2+od/2*sqrt(2)-ogap/2; + translate([0,0,od/2]) difference() { + union() { + rotate([0,0,90]) + teardrop(r=od/2,h=l); + translate([0,0,tabh]) + cube(size=[l,ogap,tab+0.05], center=true); + translate([0,0,-od/4]) + cube(size=[l,od,od/2], center=true); + } + rotate([0,0,90]) + teardrop(r=d/2,h=l+0.05); + translate([0,0,(d*sqrt(2)+tab)/2]) + cube(size=[l+0.05,gap,d+tab], center=true); + translate([0,0,tabh]) { + translate([0,-ogap/2+2-0.05,0]) + rotate([90,0,0]) + screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10); + translate([0,ogap/2+0.05,0]) + rotate([90,0,0]) + metric_nut(size=screwsize,hole=false); + } + } +} + + +module lmXuu_housing(size=8,tab=7,gap=5,wall=3,tabwall=5,screwsize=3) +{ + d = get_lmXuu_bearing_diam(size); + l = get_lmXuu_bearing_length(size); + linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize); +} +//lmXuu_housing(size=8); +//lmXuu_housing(size=10); + + +// Makes a hollow tube with the given size and wall thickness. +// h = height of tube. (Default: 1) +// r = Outer radius of tube. (Default: 1) +// r1 = Outer radius of bottom of tube. (Default: value of r) +// r2 = Outer radius of top of tube. (Default: value of r) +// wall = horizontal thickness of tube wall. (Default 0.5) +// Example: +// tube(h=3, r=4, wall=1, center=true); +// tube(h=6, r=4, wall=2, $fn=6); +// tube(h=3, r1=5, r2=7, wall=2, center=true); +module tube(h=1, r=1, r1=undef, r2=undef, wall=0.5, center=false, $fn=undef) +{ + r1 = (r1==undef)? r : r1; + r2 = (r2==undef)? r : r2; + $fn = ($fn==undef)?max(12,floor(180/asin(2/max(r1,r2))/2)*2):$fn; + difference() { + cylinder(h=h, r1=r1, r2=r2, center=center, $fn=$fn); + cylinder(h=h+0.03, r1=r1-wall, r2=r2-wall, center=center, $fn=$fn); + } +} + + + +// Example: +// narrowing_strut(w=10, l=100, wall=3, ang=30); +module narrowing_strut(w=10, l=100, wall=5, ang=30) +{ + difference() { + translate([0, 0, wall]) union () { + translate([0, 0, -wall/2]) + cube(size=[w, l, wall], center=true); + scale([1, 1, 1/tan(ang)]) yrot(45) + cube(size=[w/sqrt(2), l, w/sqrt(2)], center=true); + } + translate([0, 0, -w]) + cube(size=[w+1, l+1, w*2], center=true); + } +} +//!narrowing_strut(); + + +// Makes a wall which thins to a smaller width in the center, +// with angled supports to prevent critical overhangs. +// Example: +// thinning_wall(h=50, l=100, thick=4, ang=30, strut=5, wall=2); +module thinning_wall(h=50, l=100, thick=4, ang=30, strut=5, wall=2) +{ + union() { + xrot_copies([0, 180]) { + translate([0, 0, -h/2]) + narrowing_strut(w=thick, l=l, wall=strut, ang=ang); + translate([0, -l/2, 0]) + xrot(-90) narrowing_strut(w=thick, l=h, wall=strut, ang=ang); + } + cube(size=[wall, l-1, h-1], center=true); + } +} +//!thinning_wall(); + + +// Example: +// sparse_strut(h=40, l=120, thick=4, maxang=30, strut=5, max_bridge=20); +module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge = 20) +{ + + zoff = h/2 - strut/2; + yoff = l/2 - strut/2; + + maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang); + maxz = 2 * maxhyp * cos(maxang); + + zreps = ceil(2*zoff/maxz); + zstep = 2*zoff / zreps; + + hyp = zstep/2 / cos(maxang); + maxy = min(2 * hyp * sin(maxang), max_bridge+strut); + + yreps = ceil(2*yoff/maxy); + ystep = 2*yoff / yreps; + + ang = atan(ystep/zstep); + len = zstep / cos(ang); + + union() { + grid_of(za=[-zoff, zoff]) + cube(size=[thick, l, strut], center=true); + grid_of(ya=[-yoff, yoff]) + cube(size=[thick, strut, h], center=true); + grid_of(ya=[-yoff+ystep/2:ystep:yoff], za=[-zoff+zstep/2:zstep:zoff]) { + xrot( ang) cube(size=[thick, strut, len], center=true); + xrot(-ang) cube(size=[thick, strut, len], center=true); + } + } +} + + +module torus(or=1, ir=0.5) +{ + rotate_extrude(convexity = 10) + translate([(or-ir)/2+ir, 0, 0]) + circle(r = (or-ir)/2, $fs=1); +} +//!torus(or=30, ir=10); + + + +module nema11_stepper(h=24, shaft=5, shaft_len=20) +{ + motor_width = 28.2; + plinth_height = 1.5; + plinth_diam = 22; + screw_spacing = 23.11; + screw_size = 2.6; + screw_depth = 3.0; + + difference() { + color([0.4, 0.4, 0.4]) { + translate([0, 0, -h/2]) { + rrect(size=[motor_width, motor_width, h], r=2, center=true); + } + } + grid_of( + xa = [-screw_spacing/2, screw_spacing/2], + ya = [-screw_spacing/2, screw_spacing/2], + za = [-screw_depth/2+0.05] + ) { + cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=8); + } + } + color("silver") { + translate([0, 0, plinth_height/2]) + cylinder(h=plinth_height, r=plinth_diam/2, center=true); + translate([0, 0, shaft_len/2]) + cylinder(h=shaft_len, r=shaft/2, center=true, $fn=12); + } +} +//!nema11_stepper(); + + + +module nema14_stepper(h=24, shaft=5, shaft_len=24) +{ + motor_width = 35.2; + plinth_height = 2; + plinth_diam = 22; + screw_spacing = 26; + screw_size = 3; + screw_depth = 4.5; + + difference() { + color([0.4, 0.4, 0.4]) { + translate([0, 0, -h/2]) { + rrect(size=[motor_width, motor_width, h], r=2, center=true); + } + } + grid_of( + xa = [-screw_spacing/2, screw_spacing/2], + ya = [-screw_spacing/2, screw_spacing/2], + za = [-screw_depth/2+0.05] + ) { + cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=8); + } + } + color("silver") { + translate([0, 0, plinth_height/2]) + cylinder(h=plinth_height, r=plinth_diam/2, center=true); + translate([0, 0, shaft_len/2]) + cylinder(h=shaft_len, r=shaft/2, center=true, $fn=12); + } +} +//!nema14_stepper(); + + + +module nema17_stepper(h=34, shaft=5, shaft_len=20) +{ + motor_width = 42.3; + plinth_height = 2; + plinth_diam = 22; + screw_spacing = 30.99; + screw_size = 3; + screw_depth = 4.5; + + difference() { + color([0.4, 0.4, 0.4]) { + translate([0, 0, -h/2]) { + rrect(size=[motor_width, motor_width, h], r=2, center=true); + } + } + grid_of( + xa = [-screw_spacing/2, screw_spacing/2], + ya = [-screw_spacing/2, screw_spacing/2], + za = [-screw_depth/2+0.05] + ) { + cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=8); + } + } + color("silver") { + translate([0, 0, plinth_height/2]) + cylinder(h=plinth_height, r=plinth_diam/2, center=true); + translate([0, 0, shaft_len/2]) + cylinder(h=shaft_len, r=shaft/2, center=true, $fn=12); + } +} +//!nema17_stepper(); + + + +module nema23_stepper(h=50, shaft=6.35, shaft_len=25) +{ + motor_width = 57.0; + plinth_height = 1.6; + plinth_diam = 38.1; + screw_spacing = 47.14; + screw_size = 5.1; + screw_depth = 4.8; + + screw_inset = motor_width - screw_spacing + 1; + difference() { + union() { + color([0.4, 0.4, 0.4]) { + translate([0, 0, -h/2]) { + rrect(size=[motor_width, motor_width, h], r=2, center=true); + } + } + color("silver") { + translate([0, 0, plinth_height/2]) + cylinder(h=plinth_height, r=plinth_diam/2, center=true, $fn=32); + translate([0, 0, shaft_len/2]) + cylinder(h=shaft_len, r=shaft/2, center=true, $fn=24); + } + } + grid_of( + xa = [-screw_spacing/2, screw_spacing/2], + ya = [-screw_spacing/2, screw_spacing/2] + ) { + translate([0, 0, -screw_depth/2+1]) + cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=12); + translate([0, 0, -screw_depth-h/2]) + cube(size=[screw_inset, screw_inset, h], center=true); + } + } +} +//!nema23_stepper(); + + + +module nema34_stepper(h=75, shaft=12.7, shaft_len=32) +{ + motor_width = 86; + plinth_height = 2.03; + plinth_diam = 73.0; + screw_spacing = 69.6; + screw_size = 5.5; + screw_depth = 9; + + screw_inset = motor_width - screw_spacing + 1; + difference() { + union() { + color([0.4, 0.4, 0.4]) { + translate([0, 0, -h/2]) { + rrect(size=[motor_width, motor_width, h], r=2, center=true); + } + } + color("silver") { + translate([0, 0, plinth_height/2]) + cylinder(h=plinth_height, r=plinth_diam/2, center=true, $fn=32); + translate([0, 0, shaft_len/2]) + cylinder(h=shaft_len, r=shaft/2, center=true, $fn=24); + } + } + grid_of( + xa = [-screw_spacing/2, screw_spacing/2], + ya = [-screw_spacing/2, screw_spacing/2] + ) { + translate([0, 0, -screw_depth/2+1]) + cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=12); + translate([0, 0, -screw_depth-h/2]) + cube(size=[screw_inset, screw_inset, h], center=true); + } + } +} +//!nema34_stepper(); + + + +module nema17_mount_holes(depth=5, len=5) +{ + plinth_diam = 22; + screw_spacing = 30.99; + screw_size = 3; + + union() { + grid_of( + xa=[-screw_spacing/2, screw_spacing/2], + ya=[-screw_spacing/2, screw_spacing/2] + ) { + hull() { + translate([0, -len/2, 0]) + cylinder(h=depth, r=screw_size/2, center=true, $fn=8); + translate([0, len/2, 0]) + cylinder(h=depth, r=screw_size/2, center=true, $fn=8); + } + } + } + hull() { + translate([0, -len/2, 0]) + cylinder(h=depth, r=plinth_diam/2, center=true); + translate([0, len/2, 0]) + cylinder(h=depth, r=plinth_diam/2, center=true); + } +} +!nema17_mount_holes(depth=5, len=5); + + + +// vim: tabstop=4 noexpandtab shiftwidth=4 softtabstop=4 nowrap + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..afd79ad --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +OPENSCAD=/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD + +# match files containing "// make me" +TARGETS=$(subst .scad,.stl,$(shell grep -l '// make me' *.scad | sort)) + +all: ${TARGETS} + +# auto-generated .scad files with .deps make make re-build always. keeping the +# scad files solves this problem. (explanations are welcome.) +.SECONDARY: $(shell echo "${TARGETS}" | sed 's/\.stl/.scad/g') + +# explicit wildcard expansion suppresses errors when no files are found +include $(wildcard *.deps) + + +%.stl: %.scad config.scad GDMUtils.scad joiners.scad publicDomainGearV1.1.scad + ${OPENSCAD} -m make -o $@ -d $@.deps $< + + +clean: + rm -f ${TARGETS} *.deps + diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..a57f6ae --- /dev/null +++ b/TODO.md @@ -0,0 +1,12 @@ +TODO: +----- +* Z-axis platform threaded screw nut holder. +* Z-axis motor mount in 90deg corner part. +* Y-axis cable chain. +* Z-axis cable chain. +* X-axis endstop. +* Y-axis endstop. +* Z-axis endstop. +* Electronics container. +* Extruder mount part. + diff --git a/cap_parts.scad b/cap_parts.scad new file mode 100644 index 0000000..a9defd8 --- /dev/null +++ b/cap_parts.scad @@ -0,0 +1,23 @@ +include +use +use + + +module cap_parts() { // make me + num_x = 4; + num_y = 5; + spacing = 25; + render(convexity=4) grid_of( + xa=[-spacing*(num_x-1)/2:spacing:spacing*(num_x-1)/2], + ya=[-spacing*(num_y-1)/2:spacing:spacing*(num_y-1)/2], + za=[2] + ) cap(r=roller_axle/2-3, h=10, lip=2, wall=3); +} + + +cap_parts(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/config.scad b/config.scad new file mode 100644 index 0000000..6d24b62 --- /dev/null +++ b/config.scad @@ -0,0 +1,32 @@ +platform_length = 100; +platform_width = 150; +platform_height = 40; +platform_thick = 7; +rack_tooth_size = 5; // mm per tooth. + +rail_length = 150; +rail_height = 50; +rail_thick = 5; + +motor_rail_length = 100; + +roller_thick = 12; +roller_diam = 30; +roller_axle = 15; +roller_angle = 30; +roller_base = 12; + +joiner_angle = 30; +joiner_width = 9; +joiner_slop = 0.5; + + + +rail_spacing = platform_width - joiner_width*4 - 10; +roller_spacing = rail_spacing-roller_diam+0.5; +rail_width = rail_spacing + joiner_width*2; + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/drive_gear_parts.scad b/drive_gear_parts.scad new file mode 100644 index 0000000..fb9064b --- /dev/null +++ b/drive_gear_parts.scad @@ -0,0 +1,62 @@ +include +use +use + + +module drive_gear() { + h = 10; + render(convexity=10) union() { + difference() { + mirror_copy([0, 0, 1]) { + translate([0, 0, h/4]) { + gear ( + mm_per_tooth = 5, + number_of_teeth = 9, + thickness = h/2, + hole_diameter = 5, + twist = 15, + teeth_to_hide = 0, + pressure_angle = 20 + ); + } + } + tube(h=6, r1=20, r2=7.5, wall=4); + } + difference() { + union() { + cylinder(h=h, r=5.5, center=true); + translate([0, 0, -(h+7)/2]) + cylinder(h=7, r=9, center=true); + } + cylinder(h=(h+5)*3, r=5.1/2, center=true, $fn=16); + translate([5/2+1, 0, -(h/2+12)/2]) { + yrot(90) { + scale([1.1, 1.1, 1.1]) hull() { + metric_nut(size=3, hole=false); + translate([5, 0, 0]) + metric_nut(size=3, hole=false); + } + translate([0, 0, 2]) + cylinder(r=3.2/2, h=9, center=true, $fn=8); + } + } + } + } +} +//!drive_gear(); + + +module drive_gear_parts() { // make me + translate([0, 0, 5+7]) + circle_of(r=15, n=2) + drive_gear(); +} + + + +drive_gear_parts(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/full_assembly.scad b/full_assembly.scad new file mode 100644 index 0000000..873fe88 --- /dev/null +++ b/full_assembly.scad @@ -0,0 +1,142 @@ +include +use + +use +use +use +use +use +use +use +use +use +use +use +use + +use + + +// Set default camera position. +$vpd = 650; +$vpt = [45, -165, 165]; +$vpr = [68, 0, 315]; + + +module full_assembly() +{ + joiner_length=10; + platform_vert_off = rail_height+roller_base+roller_thick/2+5; + + // Y-axis to Z-axis corner joiner. + rails_90deg_joint(); + + // Support legs. + translate([0, platform_length/2, 0]) { + zrot_copies([0,180]) { + translate([rail_spacing/2+joiner_width+7, 0, 0]) { + zrot(-90) support_leg(); + } + } + } + + translate([0, platform_length, 0]) { + // Y-axis rails. + translate([0, rail_length/2, 0]) { + rail_structure(); + translate([0, rail_length/2+motor_rail_length/2, 0]) { + rail_with_motor_mount(show_motor=true); + translate([0, motor_rail_length/2+rail_length/2, 0]) { + rail_structure(); + translate([0, rail_length/2, 0]) { + zrot(180) rails_end(); + } + } + } + } + + + translate([0, rail_length+motor_rail_length/2, 0]) { + // Y-axis slider platform. + translate([0, 0, platform_vert_off]) { + grid_of(ya=[-platform_length/2, platform_length/2]) { + yrot(180) slider_sled(show_rollers=true, with_rack=true); + } + } + + // X-axis to Y-axis vertical joiners. + translate([0, 0, platform_vert_off]) { + zrot_copies([0, 180]) { + translate([0, -platform_length, 0]) { + xy_joiner(); + } + } + } + + zrot(90) translate([0, 0, platform_vert_off]) { + // Horizontal X-axis rails. + grid_of(ya=[-(rail_length+motor_rail_length)/2, (rail_length+motor_rail_length)/2]) { + rail_structure(); + } + rail_with_motor_mount(show_motor=true); + zrot_copies([0, 180]) { + translate([0, rail_length+motor_rail_length/2, 0]) { + zrot(180) rails_end(); + } + } + + // X-axis slider platform. + translate([0, 0, platform_vert_off]) { + grid_of(ya=[-platform_length/2, platform_length/2]) { + yrot(180) slider_sled(show_rollers=true, with_rack=true); + } + zrot_copies([0, 180]) { + translate([0, -platform_length, 0]) { + yrot(180) sled_end(); + } + } + } + } + } + } + + translate([0, 0, platform_length]) { + // Vertical Z-axis slider rails. + for(i = [0:1]) { + translate([0, 0, (i+0.5)*rail_length]) + xrot(-90) rail_structure(); + } + translate([0, 0, 2*rail_length]) { + xrot(-90) rails_end(); + } + + translate([0, platform_vert_off, rail_length]) { + // Vertical Z-axis platform. + xrot(-90) yrot(180) slider_sled(show_rollers=true, with_rack=false); + + // Z-axis platform to extruder cantilever joint. + translate([0, 0, platform_length/2]) { + zrot(180) z_platform_joint(); + } + + translate([0, joiner_length, 0]) { + // Extruder cantilever. + for (i=[0:1]) { + translate([0, (i+0.5)*rail_length, 0]) { + rail_structure(); + } + } + } + } + } +} + + + +zrot(180) full_assembly(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + + diff --git a/joiners.scad b/joiners.scad new file mode 100644 index 0000000..7a33c16 --- /dev/null +++ b/joiners.scad @@ -0,0 +1,144 @@ +include +use + +module joiner(h=40, w=9, l=10, a=30, screwsize=undef, guides=true) +{ + dmnd_height = h/2; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + + render(convexity=4) union() { + difference() { + union() { + // Make base. + difference() { + union() { + translate([0,-l/2,0]) cube(size=[w, l, h], center=true); + translate([0,0,-h/4]) + scale([w, dmnd_width/2, dmnd_height/2]) + xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true); + } + translate([0,0,h/4]) + scale([w*1.1, dmnd_width/2, dmnd_height/2]) + xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true); + } + + // Make tab + translate([0,0,dmnd_height/2]) { + translate([0, -dmnd_width/4, 0]) + cube(size=[w/3, dmnd_width/2, dmnd_height], center=true); + scale([w/3, dmnd_width/2, dmnd_height/2]) xrot(45) + cube(size=[1,sqrt(2),sqrt(2)], center=true); + } + + // Guide ridges. + if (guides == true) { + translate([0,0,dmnd_height/2]) { + grid_of(xa=[-w/6,w/6]) { + scale([1,1,2]) yrot(45) + cube(size=[guide_size/sqrt(2), dmnd_width, guide_size/sqrt(2)], center=true); + } + } + } + } + + // Make slot + translate([0, 0, -dmnd_height/2]) { + translate([0, dmnd_width/4, 0]) + cube(size=[w/3+joiner_slop, dmnd_width/2, dmnd_height], center=true); + scale([w/3+joiner_slop, dmnd_width/2, dmnd_height/2]) xrot(45) + cube(size=[1,sqrt(2),sqrt(2)], center=true); + } + + // Blunt point of tab. + translate([0,(2+dmnd_width/2-guide_size*tan(a)),0]) + cube(size=[w*1.1,4,h], center=true); + + // Make screwholes, if needed. + if (screwsize != undef) { + xrot_copies([0, 180]) + translate([0, 0, dmnd_height/2]) + yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12); + } + + // Guide slots. + if (guides == true) { + translate([0,0,-dmnd_height/2]) { + grid_of(xa=[-(w/6+joiner_slop/2),(w/6+joiner_slop/2)]) { + scale([1,1,2]) yrot(45) + cube(size=[guide_size/sqrt(2), dmnd_width*1.1, guide_size/sqrt(2)], center=true); + } + } + } + } + + // Blunt point of slot. + translate([0,-(2+dmnd_width/2-guide_size*tan(a)),0]) + cube(size=[w,4,h], center=true); + } +} +//!joiner(screwsize=3); + + + +module lock_tab(h=30, wall=3, slop=0.0) +{ + s1 = 2*wall-slop/2; + s2 = wall-slop/2; + ang = atan(((s1-s2)/2)/(h-2)); + translate([0, -(1.5*wall), 0]) union () { + intersection() { + yrot( ang) translate([0,0,(h+5)/2]) cube(size=[s1*2, wall-slop, h+10], center=true); + yrot(-ang) translate([0,0,(h+5)/2]) cube(size=[s1*2, wall-slop, h+10], center=true); + translate([0,0,(h-wall-slop)/2]) cube(size=[s1*2, wall-slop, h-wall-slop+0.05], center=true); + } + } + translate([0, -(wall+slop/2)/2-0.05, (h-wall-slop)/2]) cube(size=[wall-slop, wall+slop/2+0.1, h-wall-slop+0.05], center=true); +} +//lock_tab(h=30, wall=2, slop=-0.9); + + + +module lock_slot(h=30, wall=3, slop=0.2) +{ + s1 = 2*wall+slop/2; + s2 = wall+slop/2; + w = 2*s1+2*wall; + d = wall*3+slop; + ang = atan(((s1-s2)/2)/(h-2)); + translate([0, d/2, 0]) difference() { + intersection() { + yrot( ang) translate([0, 0, (h+5)/2]) cube(size=[w, d, h+10], center=true); + yrot(-ang) translate([0, 0, (h+5)/2]) cube(size=[w, d, h+10], center=true); + translate([0, 0, h/2]) cube(size=[w, d, h], center=true); + } + translate([0, d/2+0.05, -0.05]) lock_tab(h=h, wall=wall, slop=-slop); + } +} +//lock_slot(h=30, wall=2, slop=0.1); + + + +module cap(r=roller_axle/2-3, h=10, wall=3, cap=2, lip=2) +{ + difference() { + union() { + translate([0,0,-cap/2]) + cylinder(r=r+lip+wall, h=cap, center=true, $fn=32); + translate([0,0,h*3/8]) + cylinder(r1=r-0.5, r2=r, h=h*3/4, center=true, $fn=32); + translate([0,0,h*7/8]) + cylinder(r1=r, r2=r-wall/2, h=h*1/4, center=true, $fn=32); + } + translate([0,0,h/2+1]) + cylinder(r=r-wall, h=h+1, center=true, $fn=12); + zrot_copies([0,90]) translate([0,0,h*5/8]) + cube(size=[1,r*2,h],center=true); + } +} +//cap(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/motor_mount_plate_parts.scad b/motor_mount_plate_parts.scad new file mode 100644 index 0000000..51e218f --- /dev/null +++ b/motor_mount_plate_parts.scad @@ -0,0 +1,42 @@ +include +use +use + + +module motor_mount_plate(thick=4, l=15) +{ + union() { + translate([0, 0, l-thick/2]) { + difference() { + cube(size=[43+joiner_width+10, rail_height, 4], center=true); + zrot(90) nema17_mount_holes(depth=thick+1, l=5); + } + } + + // Joiners + zrot_copies([0, 180]) { + translate([(43+joiner_width+10)/2, 0, 0]) { + xrot(-90) { + joiner(h=rail_height, w=joiner_width, l=l, a=joiner_angle); + } + } + } + } +} +//!motor_mount_plate(); + + + +module motor_mount_plate_parts() { // make me + spacing = 55; + grid_of(ya=[-spacing/2, spacing/2]) + yrot(180) motor_mount_plate(); +} + + + +motor_mount_plate_parts(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/publicDomainGearV1.1.scad b/publicDomainGearV1.1.scad new file mode 100644 index 0000000..f7040a7 --- /dev/null +++ b/publicDomainGearV1.1.scad @@ -0,0 +1,177 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// Public Domain Parametric Involute Spur Gear (and involute helical gear and involute rack) +// version 1.1 +// by Leemon Baird, 2011, Leemon@Leemon.com +//http://www.thingiverse.com/thing:5505 +// +// This file is public domain. Use it for any purpose, including commercial +// applications. Attribution would be nice, but is not required. There is +// no warranty of any kind, including its correctness, usefulness, or safety. +// +// This is parameterized involute spur (or helical) gear. It is much simpler and less powerful than +// others on Thingiverse. But it is public domain. I implemented it from scratch from the +// descriptions and equations on Wikipedia and the web, using Mathematica for calculations and testing, +// and I now release it into the public domain. +// +// http://en.wikipedia.org/wiki/Involute_gear +// http://en.wikipedia.org/wiki/Gear +// http://en.wikipedia.org/wiki/List_of_gear_nomenclature +// http://gtrebaol.free.fr/doc/catia/spur_gear.html +// http://www.cs.cmu.edu/~rapidproto/mechanisms/chpt7.html +// +// The module gear() gives an involute spur gear, with reasonable defaults for all the parameters. +// Normally, you should just choose the first 4 parameters, and let the rest be default values. +// The module gear() gives a gear in the XY plane, centered on the origin, with one tooth centered on +// the positive Y axis. The various functions below it take the same parameters, and return various +// measurements for the gear. The most important is pitch_radius, which tells how far apart to space +// gears that are meshing, and adendum_radius, which gives the size of the region filled by the gear. +// A gear has a "pitch circle", which is an invisible circle that cuts through the middle of each +// tooth (though not the exact center). In order for two gears to mesh, their pitch circles should +// just touch. So the distance between their centers should be pitch_radius() for one, plus pitch_radius() +// for the other, which gives the radii of their pitch circles. +// +// In order for two gears to mesh, they must have the same mm_per_tooth and pressure_angle parameters. +// mm_per_tooth gives the number of millimeters of arc around the pitch circle covered by one tooth and one +// space between teeth. The pitch angle controls how flat or bulged the sides of the teeth are. Common +// values include 14.5 degrees and 20 degrees, and occasionally 25. Though I've seen 28 recommended for +// plastic gears. Larger numbers bulge out more, giving stronger teeth, so 28 degrees is the default here. +// +// The ratio of number_of_teeth for two meshing gears gives how many times one will make a full +// revolution when the the other makes one full revolution. If the two numbers are coprime (i.e. +// are not both divisible by the same number greater than 1), then every tooth on one gear +// will meet every tooth on the other, for more even wear. So coprime numbers of teeth are good. +// +// The module rack() gives a rack, which is a bar with teeth. A rack can mesh with any +// gear that has the same mm_per_tooth and pressure_angle. +// +// Some terminology: +// The outline of a gear is a smooth circle (the "pitch circle") which has mountains and valleys +// added so it is toothed. So there is an inner circle (the "root circle") that touches the +// base of all the teeth, an outer circle that touches the tips of all the teeth, +// and the invisible pitch circle in between them. There is also a "base circle", which can be smaller than +// all three of the others, which controls the shape of the teeth. The side of each tooth lies on the path +// that the end of a string would follow if it were wrapped tightly around the base circle, then slowly unwound. +// That shape is an "involute", which gives this type of gear its name. +// +////////////////////////////////////////////////////////////////////////////////////////////// + +//An involute spur gear, with reasonable defaults for all the parameters. +//Normally, you should just choose the first 4 parameters, and let the rest be default values. +//Meshing gears must match in mm_per_tooth, pressure_angle, and twist, +//and be separated by the sum of their pitch radii, which can be found with pitch_radius(). +module gear ( + mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth + number_of_teeth = 11, //total number of teeth around the entire perimeter + thickness = 6, //thickness of gear in mm + hole_diameter = 3, //diameter of the hole in the center, in mm + twist = 0, //teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once + teeth_to_hide = 0, //number of teeth to delete to make this only a fraction of a circle + pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees. + clearance = 0.0, //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters) + backlash = 0.0 //gap between two meshing teeth, in the direction along the circumference of the pitch circle +) { + assign(pi = 3.1415926) + assign(p = mm_per_tooth * number_of_teeth / pi / 2) //radius of pitch circle + assign(c = p + mm_per_tooth / pi - clearance) //radius of outer circle + assign(b = p*cos(pressure_angle)) //radius of base circle + assign(r = p-(c-p)-clearance) //radius of root circle + assign(t = mm_per_tooth/2-backlash/2) //tooth thickness at pitch circle + assign(k = -iang(b, p) - t/2/p/pi*180) { //angle to where involute meets base circle on each side of tooth + difference() { + for (i = [0:number_of_teeth-teeth_to_hide-1] ) + rotate([0,0,i*360/number_of_teeth]) + linear_extrude(height = thickness, center = true, convexity = 10, twist = twist) + polygon( + points=[ + [0, -hole_diameter/10], + polar(r, -181/number_of_teeth), + polar(r, r +use +use +use +use + + +module rail_with_motor_mount(show_motor=false) +{ + joiner_length = 10; + + difference() { + union() { + difference() { + union() { + // Bottom. + translate([0,0,rail_thick/2]) yrot(90) + sparse_strut(h=rail_width, l=motor_rail_length, thick=rail_thick, maxang=45, strut=10, max_bridge=500); + + // Walls. + grid_of(xa=[-(rail_spacing/2+joiner_width/2), (rail_spacing/2+joiner_width/2)], za=[(rail_height+3)/2]) { + thinning_wall(h=rail_height+3, l=motor_rail_length-joiner_length, thick=joiner_width, strut=rail_thick); + } + } + + // Clear space out near clips. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + ya=[-motor_rail_length/2, motor_rail_length/2], + za=[(rail_height)/4, (rail_height)*3/4] + ) { + scale([1, tan(joiner_angle), 1]) xrot(45) + cube(size=rail_height/2/sqrt(2), center=true); + } + } + + // Rail backing. + grid_of([-(rail_spacing/2+joiner_width/2), (rail_spacing/2+joiner_width/2)]) + translate([0,0,rail_height+roller_thick/2]) + cube(size=[joiner_width, motor_rail_length, roller_thick], center=true); + + // Joiner clips. + translate([0,0,rail_height/2]) { + zrot_copies([0,180]) { + yrot_copies([0,180]) { + translate([rail_spacing/2+joiner_width/2, motor_rail_length/2, 0]) { + joiner(h=rail_height, w=joiner_width, l=13, a=joiner_angle); + } + } + } + } + + // Side mount slots. + grid_of(ya=[-(motor_rail_length/2-joiner_length-5), (motor_rail_length/2-joiner_length-5)]) { + zrot_copies([0,180]) { + translate([rail_width/2-2.5, 0, 0]) { + zrot(-90) lock_slot(h=25, wall=3); + } + } + } + + // Side supports. + zrot_copies([0, 180]) { + translate([0, motor_rail_length/2-8, rail_height/4]) + cube(size=[rail_width, 3, rail_height/2], center=true); + } + + // Motor clip mounts. + zrot_copies([0, 180]) { + translate([(43+joiner_width+10)/2, 0, 30]) { + xrot(90) { + joiner(h=rail_height, w=joiner_width, l=30, a=joiner_angle); + } + } + } + } + + // Rail grooves. + translate([0,0,rail_height+roller_thick/2]) { + grid_of([-(rail_spacing/2), (rail_spacing/2)]) { + scale([tan(roller_angle),1,1]) yrot(45) { + cube(size=[roller_thick*sqrt(2)/2,motor_rail_length+1,roller_thick*sqrt(2)/2], center=true); + } + } + } + } + if (show_motor == true) { + translate([0, 0, 30]) + motor_mount_plate(); + translate([0, 0, 35.9+rail_thick]) { + nema17_stepper(h=34, shaft_len=20.05); + translate([0, 0, 18]) + drive_gear(); + } + } +} +//!rail_with_motor_mount(show_motor=true); + + + +module rail_with_motor_mount_part() { // make me + rail_with_motor_mount(); +} + + +rail_with_motor_mount_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/rails_90deg_joint_part.scad b/rails_90deg_joint_part.scad new file mode 100644 index 0000000..0e241b8 --- /dev/null +++ b/rails_90deg_joint_part.scad @@ -0,0 +1,126 @@ +include +use +use + + +module rails_90deg_joint() +{ + joiner_length=10; + + difference() { + union() { + difference() { + union() { + // Bottom. + translate([0,platform_length/2,rail_thick/2]) yrot(90) + sparse_strut(h=rail_width, l=platform_length, thick=rail_thick, maxang=45, strut=10, max_bridge=500); + + // Back. + translate([0,rail_thick/2,platform_length/2]) zrot(90) { + thinning_wall(h=platform_length, l=rail_width, thick=rail_thick, strut=5); + } + + // Side Walls + grid_of(xa=[-(rail_spacing/2+joiner_width/2), (rail_spacing/2+joiner_width/2)]) { + // Upper Walls. + grid_of( + ya=[rail_height/2], + za=[(platform_length-rail_height-joiner_length)/2+rail_height] + ) { + thinning_wall(h=platform_length-joiner_length-rail_height+2*rail_thick, l=rail_height, thick=joiner_width, strut=rail_thick); + } + + // Lower Walls. + grid_of( + ya=[(platform_length-rail_height-joiner_length)/2+rail_height], + za=[rail_height/2] + ) { + thinning_wall(l=platform_length-joiner_length-rail_height+2*rail_thick, h=rail_height, thick=joiner_width, strut=rail_thick); + } + + // Corner Walls. + grid_of( + ya=[rail_height/2], + za=[rail_height/2] + ) { + thinning_wall(l=rail_height, h=rail_height, thick=joiner_width, strut=rail_thick); + } + + // Rail tops. + translate([0, rail_height, rail_height]) { + translate([0, (platform_length-rail_height)/2, roller_thick/2]) + cube(size=[joiner_width, platform_length-rail_height, roller_thick], center=true); + translate([0, roller_thick/2, (platform_length-rail_height)/2]) + cube(size=[joiner_width, roller_thick, platform_length-rail_height], center=true); + } + } + } + + // Clear space out near front clips. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + ya=[platform_length], + za=[(rail_height)/4, (rail_height)*3/4] + ) { + scale([1, tan(joiner_angle), 1]) xrot(45) + cube(size=rail_height/2/sqrt(2), center=true); + } + + // Clear space out near top clips. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + ya=[rail_height/4, rail_height*3/4], + za=[platform_length] + ) { + scale([1, 1, tan(joiner_angle)]) xrot(45) + cube(size=rail_height/2/sqrt(2), center=true); + } + } + + // Front Joiner clips. + translate([0, 0, rail_height/2]) { + yrot_copies([0, 180]) { + translate([rail_spacing/2+joiner_width/2, platform_length, 0]) { + joiner(h=rail_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + + // Top Joiner clips. + translate([0, (rail_height)/2, platform_length]) { + zrot_copies([0,180]) { + translate([rail_spacing/2+joiner_width/2, 0, 0]) { + xrot(90) joiner(h=rail_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + + // Side mount slots. + translate([0, platform_width/3, 0]) { + grid_of(ya=[-platform_width/3/2, platform_width/3/2]) { + zrot_copies([0,180]) { + translate([rail_width/2-2.5, 0, 0]) { + zrot(-90) lock_slot(h=25, wall=3); + } + } + } + } + } + } +} +//!rails_90deg_joint(); + + + +module rails_90deg_joint_point() { // make me + rails_90deg_joint(); +} + + + +rails_90deg_joint_point(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/rails_end_part.scad b/rails_end_part.scad new file mode 100644 index 0000000..f380180 --- /dev/null +++ b/rails_end_part.scad @@ -0,0 +1,67 @@ +include +use +use + + +module rails_end() +{ + joiner_length=10; + base_height = rail_height+roller_thick; + difference() { + union() { + difference() { + union() { + // Bottom. + translate([0, -(joiner_length+rail_thick)/2, rail_thick/2]) + cube(size=[rail_width, joiner_length+rail_thick, rail_thick], center=true); + + // Back. + translate([0, -joiner_length-rail_thick/2+0.05, base_height/2]) zrot(90) + thinning_wall(h=base_height, l=rail_width, thick=rail_thick, strut=5); + } + + // Clear space out near front clips. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + za=[(base_height)/4, (base_height)*3/4] + ) { + scale([1, tan(joiner_angle), 1]) xrot(45) + cube(size=base_height/2/sqrt(2), center=true); + } + } + + // Corner pieces. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + za=[base_height] + ) { + translate([0, -(base_height-rail_height)/2-0.05, -(base_height-rail_height)/2-0.05]) + cube(size=[joiner_width, (base_height-rail_height-0.05), (base_height-rail_height+0.05)], center=true); + } + + // Joiner clips. + translate([0, 0, base_height/2-(base_height-rail_height)/2]) { + yrot_copies([0,180]) { + translate([rail_spacing/2+joiner_width/2, 0, 0]) { + joiner(h=rail_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + } + } +} +//!rails_end(); + + + +module rails_end_part() { // make me + zrot(90) rails_end(); +} + + +rails_end_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/rails_part.scad b/rails_part.scad new file mode 100644 index 0000000..31368a7 --- /dev/null +++ b/rails_part.scad @@ -0,0 +1,81 @@ +include +use +use + + +module rail_structure() +{ + difference() { + union() { + difference() { + union() { + // Bottom. + translate([0,0,rail_thick/2]) yrot(90) + sparse_strut(h=rail_width, l=rail_length, thick=rail_thick, maxang=45, strut=10, max_bridge=500); + + // Walls. + grid_of(xa=[-(rail_spacing/2+joiner_width/2), (rail_spacing/2+joiner_width/2)], za=[(rail_height+3)/2]) { + thinning_wall(h=rail_height+3, l=rail_length-10*2, thick=joiner_width, strut=rail_thick); + } + } + + // Clear space out near clips. + grid_of( + xa=[-(rail_spacing+joiner_width)/2, (rail_spacing+joiner_width)/2], + ya=[-rail_length/2, rail_length/2], + za=[(rail_height)/4, (rail_height)*3/4] + ) { + scale([1, tan(joiner_angle), 1]) xrot(45) + cube(size=rail_height/2/sqrt(2), center=true); + } + } + + // Rail backing. + grid_of([-(rail_spacing/2+joiner_width/2), (rail_spacing/2+joiner_width/2)]) + translate([0,0,rail_height+roller_thick/2]) + cube(size=[joiner_width, rail_length, roller_thick], center=true); + + // Joiner clips. + translate([0,0,rail_height/2]) { + zrot_copies([0,180]) { + yrot_copies([0,180]) { + translate([rail_spacing/2+joiner_width/2, rail_length/2, 0]) { + joiner(h=rail_height, w=joiner_width, l=13, a=joiner_angle); + } + } + } + } + + // Side supports. + zrot_copies([0, 180]) { + translate([0, rail_length/2-8, rail_height/4]) + cube(size=[rail_width, 3, rail_height/2], center=true); + } + } + + // Rail grooves. + translate([0,0,rail_height+roller_thick/2]) { + grid_of([-(rail_spacing/2), (rail_spacing/2)]) { + scale([tan(roller_angle),1,1]) yrot(45) { + cube(size=[roller_thick*sqrt(2)/2,rail_length+1,roller_thick*sqrt(2)/2], center=true); + } + } + } + } +} +//!rail_structure(); + + + +module rails_part() { // make me + rail_structure(); +} + + + +rails_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/roller_parts.scad b/roller_parts.scad new file mode 100644 index 0000000..c7f1e26 --- /dev/null +++ b/roller_parts.scad @@ -0,0 +1,40 @@ +include +use +use + + +module roller() +{ + render(convexity=2) difference() { + union() { + translate([0,0,-roller_thick/4]) + cylinder(h=roller_thick/2, r1=roller_diam/2, r2=roller_diam/2+(roller_thick/2)*tan(roller_angle), center=true, $fn=48); + cylinder(h=0.05, r=roller_diam/2+(roller_thick/2)*tan(roller_angle), center=true, $fn=48); + translate([0,0,roller_thick/4]) + cylinder(h=roller_thick/2, r2=roller_diam/2, r1=roller_diam/2+(roller_thick/2)*tan(roller_angle), center=true, $fn=48); + } + cylinder(h=roller_thick+2.1, r=roller_axle/2, center=true, $fn=32); + } +} +//!roller(); + + + +module roller_parts() { // make me + num_x = 3; + num_y = 4; + spacing = 40; + grid_of( + xa=[-spacing*(num_x-1)/2:spacing:spacing*(num_x-1)/2], + ya=[-spacing*(num_y-1)/2:spacing:spacing*(num_y-1)/2], + za=[roller_thick/2] + ) roller(); +} + + + +roller_parts(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/sled_end_parts.scad b/sled_end_parts.scad new file mode 100644 index 0000000..1909d16 --- /dev/null +++ b/sled_end_parts.scad @@ -0,0 +1,63 @@ +include +use +use + + +module sled_end() +{ + snap_width = 15; + union() { + difference() { + union() { + // Base. + translate([0,-(snap_width/2),platform_thick/2]) + cube(size=[platform_width, snap_width, platform_thick], center=true); + + // Back wall. + translate([0,-(snap_width-platform_thick/2),platform_height/2]) + cube(size=[platform_width, platform_thick, platform_height], center=true); + } + + // Remove bits of back wall that would hit rails. + translate([0, -snap_width/2, platform_height/2+roller_base-2]) { + cube(size=[rail_spacing+joiner_width*2+5, snap_width+platform_thick+10, platform_height], center=true); + } + + // Remove bits from platform so snap tabs have freedom. + grid_of( + xa=[-(platform_width-joiner_width/2-5)/2, (platform_width-joiner_width/2-5)/2] + ) { + xrot(joiner_angle) translate([-(joiner_width+10)/2,0,-1]) + cube(size=[joiner_width+10,platform_thick,platform_thick*1.5], center=false); + } + } + + // Joiner tabs. + translate([0,0,platform_height/2]) { + yrot_copies([0,180]) { + translate([platform_width/2-joiner_width/2, 0, 0]) { + joiner(h=platform_height, w=joiner_width, l=10, a=joiner_angle); + } + } + } + + // Rack endstop block. + translate([0, -snap_width/2, (platform_thick+3+10)/2]) + cube(size=[15,snap_width,(platform_thick+3+10)], center=true); + } +} +//!sled_end(); + + + +module sled_end_part() { // make me + zrot_copies([90,270]) translate([0,20,0]) sled_end(); +} + + +sled_end_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/slider_sled.scad b/slider_sled.scad new file mode 100644 index 0000000..bc46fee --- /dev/null +++ b/slider_sled.scad @@ -0,0 +1,121 @@ +include +use +use +use +use +use + + +module herringbone_rack(l=100, h=10, w=10, tooth_size=5, CA=30) +{ + render(convexity=10) translate([-(rack_tooth_size/2), 0, 0]) { + mirror_copy([0,0,1]) { + skew_along_z(xang=CA) { + intersection() { + translate([-(l/2-rack_tooth_size/2), 0, h/4]) { + rack( + mm_per_tooth=rack_tooth_size, + number_of_teeth=floor(l/rack_tooth_size), + thickness=h/2, + height=w, + pressure_angle=20, + backlash=0 + ); + } + cube(size=[l, h*3, h*3], center=true); + } + } + } + } +} +//!herringbone_rack(l=100, h=10, tooth_size=5, CA=30); + + + +module slider_sled(show_rollers=false, with_rack=false) +{ + platform_length=with_rack? ceil(platform_length/rack_tooth_size)*rack_tooth_size : platform_length; // quantize to rack tooth size, if needed. + axle_rad = (roller_axle/2) - 0.5; + axle_len = roller_thick; + + union() { + difference() { + // Bottom strut. + translate([0,0,platform_thick/2]) + yrot(90) sparse_strut(h=platform_width, l=platform_length, thick=platform_thick, maxang=45, strut=12, max_bridge=999); + + // Remove bits from platform so snap tabs have freedom. + zrot_copies([0,180]) { + grid_of( + xa=[-(platform_width-joiner_width/2-5)/2, (platform_width-joiner_width/2-5)/2], + ya=[platform_length/2] + ) { + xrot(joiner_angle) translate([-(joiner_width+10)/2,0,-1]) + cube(size=[joiner_width+10,platform_thick,platform_thick*3], center=false); + } + } + } + + translate([0,0,platform_height/2]) { + // Snap-tab joiners. + zrot_copies([0,180]) { + yrot_copies([0,180]) { + translate([platform_width/2-joiner_width/2, platform_length/2, 0]) { + joiner(h=platform_height, w=joiner_width, l=10, a=joiner_angle); + } + } + } + + // Solid walls. + grid_of(xa=[-(platform_width-joiner_width)/2, (platform_width-joiner_width)/2]) { + thinning_wall(h=platform_height, l=platform_length-18, thick=joiner_width, strut=platform_thick, wall=3); + } + } + + grid_of(xa=[-roller_spacing/2,roller_spacing/2]) { + grid_of(ya=[-(platform_length/2)/2, (platform_length/2)/2]) { + // Roller pedestals + translate([0,0,roller_base/2]) { + cylinder(h=roller_base, r=axle_rad+2, center=true, $fn=32); + } + + // Roller axles + translate([0,0,axle_len/2+roller_base]) { + tube(h=axle_len+0.05, r=axle_rad, wall=2.5, center=true, $fn=32); + if (show_rollers) { + roller(); + translate([0,0,axle_len/2]) xrot(180) + cap(r=roller_axle/2-3, h=10, lip=2, wall=3); + } + } + } + } + + translate([0,0,platform_thick/2]) { + // Length-wise bracing. + if (with_rack == true) { + translate([-10, 0, 1]) + cube(size=[14,platform_length,platform_thick+2], center=true); + } else { + translate([ 0, 0, 1]) + cube(size=[14,platform_length,platform_thick], center=true); + } + } + + // Drive rack + if (with_rack == true) { + translate([-8, 0, platform_thick+2+5]) { + zrot(-90) herringbone_rack(l=platform_length, h=10, tooth_size=rack_tooth_size, CA=30); + } + } + } +} + + + +slider_sled(show_rollers=true); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/support_leg_part.scad b/support_leg_part.scad new file mode 100644 index 0000000..07911db --- /dev/null +++ b/support_leg_part.scad @@ -0,0 +1,40 @@ +include +use +use + + +module support_leg(h=30, l=100, wall=3) +{ + ang = atan((h-10)/l); + union() { + translate([0, 5/2, h/2]) + cube(size=[platform_length/2+6*wall, 5, h], center=true); + grid_of(xa=[-platform_length/4, platform_length/4]) { + lock_tab(h=h, wall=wall); + translate([0, 0, h]) { + difference() { + translate([-wall, 0, -h]) + cube(size=[2*wall, l, h], center=false); + xrot(-ang) translate([-wall*1.5, 0, 0]) + cube(size=[3*wall, l*sqrt(2), h], center=false); + } + } + } + } +} +//!support_leg(); + + + +module support_leg_part() { // make me + support_leg(); +} + + + +support_leg_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/xy_joiner_parts.scad b/xy_joiner_parts.scad new file mode 100644 index 0000000..e67d17f --- /dev/null +++ b/xy_joiner_parts.scad @@ -0,0 +1,77 @@ +include +use +use + + +module xy_joiner() +{ + joiner_length=10; + hoff = (platform_length*2-rail_width)/2-3*3+1; + union() { + // Joiners + translate([0, 0, -platform_height/2]) { + yrot_copies([0, 180]) { + translate([(platform_width-joiner_width)/2, 0, 0]) { + joiner(h=platform_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + + //Vertical brace bars. + grid_of( + xa=[-(platform_width-joiner_width)/2, (platform_width-joiner_width)/2], + ya=[-(joiner_length*1.5-0.05)], + za=[-platform_height/2+22/2] + ) { + cube(size=[joiner_width, joiner_length+0.05, platform_height+22], center=true); + } + + translate([0, hoff, 0]) { + // tabs connector. + translate([0, -platform_thick/2, 22/2]) { + cube(size=[(platform_width-joiner_width), platform_thick, 22], center=true); + } + + // Lock tabs + grid_of( + xa=[-(motor_rail_length/2-joiner_length-5), (motor_rail_length/2-joiner_length-5)] + ) { + zrot(180) lock_tab(h=25, wall=3); + } + } + + // Bottom + translate([0, hoff/2-joiner_length/2-5, 22-5/2]) { + xrot(90) zrot(90) sparse_strut(l=platform_width, h=hoff+joiner_length+10, thick=5, maxang=45, strut=platform_thick, max_bridge=999); + } + + // Side walls + grid_of(xa=[-(platform_width-joiner_width)/2, (platform_width-joiner_width)/2]) { + translate([0, hoff/2-joiner_length/2-5, 22/2]) { + cube(size=[joiner_width, hoff+joiner_length+10, 22], center=true); + } + } + // Back Wall + translate([0, -joiner_length*2+platform_thick/2, 7]) { + zrot(90) thinning_wall(l=platform_width-joiner_width, h=30, thick=platform_thick, maxang=45, strut=5, max_bridge=999); + } + } +} +//!xy_joiner(); + + + +module xy_joiner_parts() { // make me + translate([0, 0, 22]) { + zrot(90) xrot(180) xy_joiner(); + } +} + + + +xy_joiner_parts(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/xy_slider_part.scad b/xy_slider_part.scad new file mode 100644 index 0000000..d0bdb1d --- /dev/null +++ b/xy_slider_part.scad @@ -0,0 +1,19 @@ +include +use +use +use +use +use +use + + +module xy_sled_part() { // make me + zrot(90) slider_sled(with_rack=true); +} + + +xy_sled_part(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/z_platform_joint_part.scad b/z_platform_joint_part.scad new file mode 100644 index 0000000..fa72f1d --- /dev/null +++ b/z_platform_joint_part.scad @@ -0,0 +1,48 @@ +include +use +use + + +module z_platform_joint() +{ + joiner_length=10; + xrot(-90) union() { + translate([0, 0, platform_height/2]) { + yrot_copies([0, 180]) { + translate([-(platform_width-joiner_width)/2, 0, 0]) { + yrot(180) joiner(h=platform_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + translate([0, -joiner_length/2, -joiner_length/2]) + cube(size=[platform_width, joiner_length, joiner_length], center=true); + translate([0, rail_height/2-0.05, -joiner_length/2]) xrot(90) zrot(90) { + thinning_wall(h=rail_height+0.05, l=rail_width-joiner_width*2+0.05, thick=joiner_length, strut=4); + } + translate([0, rail_height/2, -joiner_length]) { + zrot_copies([0, 180]) { + translate([-(rail_width-joiner_width)/2, 0, 0]) { + xrot(-90) yrot(180) joiner(h=rail_height, w=joiner_width, l=joiner_length, a=joiner_angle); + } + } + } + } +} +//!z_platform_joint(); + + + +module z_platform_joint_part() { // make me + translate([0, 0, 10]) zrot(90) xrot(180) { + z_platform_joint(); + } +} + + + +z_platform_joint_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + diff --git a/z_sled_part.scad b/z_sled_part.scad new file mode 100644 index 0000000..191ed0e --- /dev/null +++ b/z_sled_part.scad @@ -0,0 +1,18 @@ +include +use +use + + + +module z_sled_part() { // make me + zrot(90) slider_sled(with_rack=false); +} + + + +z_sled_part(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +