Gas station without pumps

2018 August 29

3D printed names

Filed under: Uncategorized — gasstationwithoutpumps @ 11:57
Tags: , ,

To explore OpenSCAD and 3D printing further, I have made two more objects: a key holder for my home and office keys and a nametag.  I’m not planning to put these items on Thingiverse, since they have my name on them and would need to be customized.

The key holder was printed as two plates with holes 65mm apart. One hole had a round surround to accept a socket-head M3 screw, while the other hole had a hexagonal surround to accept an M3 nut.

The basic idea of the key holder is simple: two identical plates held together with M3 stainless-steel screws, with the keys capable of rotating on the screws. The hexagonal socket for the nut was just a little tight, so I had to hammer the nuts into the plate, which turned out to be a good thing, as the nuts are then held firmly by the plate.

I printed a textured surface by designing in shallow V-shaped grooves, and removing my name somewhat deeper. My first attempt printed the lettering and grooves on the top of the print, which resulted in a rather rough surface, due to all the retractions made between printing the separate islands. My second attempt printed face down, to get a smooth surface as seen here.

The keys fold out like a utility knife and provide a more convenient handle for turning the key than the usual small tab. I’ve deliberately erased the information-bearing part in the picture of the key.

Because the name did not stand out well, I tried painting the recessed areas with acrylic paint (no primer). The paint does not stick all that well to the bare PLA, and I got some paint stuck in the little crevices on the surface outside the letters. It looks ok from a distance, but closer up it is rather messy, as can be seen in the photo above.

The key holder holds 6 keys—3 at each end. The keys were not of equal thickness, so I added some 3D-printed washers with the thinner stack of keys. I printed several washers in different thicknesses (1mm to 3mm in 0.5mm steps), and tried different combinations until I got a pair that provided the right spacing. Putting the spacers outside the keys seems to work better than putting them between the keys.

I’m still not sure about the wisdom of having my name on my key holder—it makes it easy for someone to return my keys if I accidentally leave them somewhere, but it also makes it easier for a thief who finds the keys to figure out where they are keys for! I haven’t lost my keys or had them stolen in my 32 years of living in Santa Cruz, so I’m not too concerned about either outcome.

One problem I have noticed with the key holder is that the stainless-steel screws can work loose, particularly from repeated use of the bike key, which has high friction to the screw passing through it. I may want to get a little threadlock to reduce the chance of the key holder coming apart accidentally.

The nametag is thinner than the plates of the key holder (2mm instead of 4mm), and printed with finer layers, but its diagonal size (103.8mm) is almost the largest that can be printed on the Monoprice Delta Mini (which has a 110mm limit).  It took about 2 hours to print.

I printed this only 2mm thick with the lettering recessed 1mm. Once again it was printed face down and I tried painting the recessed areas with acrylic paint.  I had a little less paint in the crevices around each letter, but still enough to look a bit messy.

The nametag was slightly too large for the holder I had designed it for (probably due to a combination of spreading of the first layer and my not allowing for the 2mm thickness), but it works ok in this somewhat oversized holder.

The nametag “worked” but does not look very good—I think that a maker would be better off with a PC board or laser-etched nametag, either of which could provide much better quality.

Advertisements

2018 August 14

3D-printed small snifter

Filed under: Uncategorized — gasstationwithoutpumps @ 11:51
Tags: , , , , , ,

To practice using a different feature of OpenSCAD, I tried making a wine glass by rotating a 2D outline around the Z-axis.  OpenSCAD has much more limited 2D capabilities than SVG, allowing only polygons, not smooth curves, so it took me a while to come up with a way to get a decent shape with just a few vertices.

The tiny wine glass looks ok, but the single-layer walls of the bowl leak, and the stem is too springy.

My first design was based on a tall, thin wine glass, but the glass was too tall for the print volume of the Monoprice Delta Mini printer, so I tried printing it scaled down by a factor of 2. The printer had a lot of trouble with this tiny design. Perhaps the biggest problem is that the thin stem is too flexible, so the printer head dragged the bowl around as it was printing. The stick-slip action resulted in a few small holes in the single-layer bowl.

I then tried adjusting the design so that the default settings would still produce the wine glass, but different settings would match a mini-snifter that does fit in the print volume. This mini-snifter has a bowl volume of about 135ml (4.5oz), and the snifter weighs 33g. I printed it at 0.15 mm/layer, with a wall count of 5 in Cura, 6 top and bottom layers, and 10% infill.

The roughness of the surface is probably due to inconsistent feeding of filament, except where the bowl flares out widely. There the filament is drooping a bit due to insufficient support.

The mini-snifter holds water and is a reasonably close approximation to the glass that I was copying.

Side view of the snifter, showing the “seam” that results from Cura starting each layer by moving to the same starting position on the circle.

Bottom view of the snifter, showing the flat surface created by the glass bed added to my printer.

Inside view of the bowl of the snifter.

Snifter on its side, to show the roughness of the printing where the bowl widens unsupported. The vertices of the polygon that is rotated are also clearly visible here as three lines around the bowl.

I’ve included the OpenSCAD source code below, but it can be more easily downloaded from
https://www.thingiverse.com/thing:3049512

// OpenSCAD module for making goblets
// Kevin Karplus
// 2018 Aug 14
// Creative Commons - Attribution - Non-Commercial - Share Alike license.</pre>

function interp(x, y0, y1) = (1-x)*y0 + x*y1;

// Goblet using rotation about Z-axis
// Defaults are for a tall, narrow wine glass: too tall for the
// Monoprice Delta Mini 3D printer
// h is height
// foot_r is radius of foot
// foot_h is height of foot where it joins stem
// stem_r is radius of stem
// stem_h is height of stem where it joins bowl
// bowl_bottom_r is radius of bowl at stem_h
// bowl_mid_r is radius of bowl at h-bowl_mid_depth
// bowl_top_r is radius of bowl at top
// thickness is thickness of sides of bowl.
module goblet(h= 214, foot_r=36, foot_h=12, stem_r=4, stem_h=110,
bowl_bottom_r=12, bowl_mid_r=28, bowl_mid_depth=42,
bowl_top_r=25, thickness=1.5)
{
bowl_depth=h-stem_h;
sphere_width = interp(0.75, bowl_mid_r,bowl_bottom_r) -thickness;
echo( "sphere_width=", sphere_width);

delta_r =interp(0.5,bowl_mid_r,bowl_bottom_r)-bowl_bottom_r;
delta_d =bowl_depth-interp(0.7,bowl_mid_depth,bowl_depth);
echo("delta_d=", delta_d, "delta_r=",delta_r);
alpha = atan( delta_d/delta_r);
echo ("alpha=", alpha);
sphere_radius= sphere_width/sin(alpha);
sphere_height = stem_h+sphere_radius;
echo( "sphere_radius=", sphere_radius);

difference()
{
rotate_extrude($fa=2)
{ polygon(
points=[
// foot
[0,0], [foot_r,0], [foot_r,thickness],
[foot_r-4, thickness],
[stem_r*1.5, foot_h- 0.5*stem_r],

// stem
[stem_r,foot_h],
[stem_r, stem_h*0.75],
[interp(0.5,stem_r,bowl_bottom_r), 0.9*stem_h],

//bowl
[bowl_bottom_r,stem_h],
[interp(0.5,bowl_mid_r,bowl_bottom_r),
h-interp(0.7,bowl_mid_depth,bowl_depth)],
[interp(0.2,bowl_mid_r,bowl_bottom_r),
h-interp(0.5,bowl_mid_depth,bowl_depth)],
[bowl_mid_r,
h-interp(0.2,bowl_mid_depth,bowl_depth)],
[bowl_mid_r,
h-interp(-0.07,bowl_mid_depth,bowl_depth)],
[bowl_top_r, h],
[bowl_top_r-thickness, h],
[bowl_mid_r-thickness,
h-interp(-0.07,bowl_mid_depth,bowl_depth)],
[bowl_mid_r-thickness,
h-interp(0.2,bowl_mid_depth,bowl_depth)],
[interp(0.2,bowl_mid_r,bowl_bottom_r) -thickness,
h-interp(0.5,bowl_mid_depth,bowl_depth)],
[interp(0.5,bowl_mid_r,bowl_bottom_r)-thickness,
h-interp(0.7,bowl_mid_depth,bowl_depth)],
[interp(0.75,bowl_mid_r,bowl_bottom_r)-thickness,
h- interp(0.85,bowl_mid_depth,bowl_depth)],
[0,stem_h]
]);
};
translate([0,0,sphere_height])
color("red")
intersection()
{ sphere(r=sphere_radius, $fs=0.1);
translate([0,0,-2*sphere_radius+delta_d])
cube([10*sphere_radius, 10*sphere_radius, 2*sphere_radius],
center=true);
}
}

}

// The default example is for a small snifter, with an interior volume of about 135ml.
// Printed in PLA with 0.15mm layers, wall line count 5, top and bottom layer count 6, and 10% infill, it weighs 33g.
goblet(h=95, foot_r=29, foot_h=10, stem_r=4.5, stem_h=30,
bowl_bottom_r=18, bowl_mid_r=33, bowl_mid_depth=40,
bowl_top_r=23, thickness=1.8);
<pre>

2018 August 3

Tool for sweeping roof valleys

Filed under: Uncategorized — gasstationwithoutpumps @ 21:42
Tags: , , , , ,

As I mentioned in Fixing Monoprice Delta Mini end stops,

I’ve also been working on creating broomstick threading on the printer, both to test the ability of the printer to do screw threads and to make a socket and bracket for attaching a broom head to a telescoping pole for cleaning the valleys of my tile roof.  I’ll post on that in a separate post, with pictures, once I get the whole thing working.

I designed the threads using OpenSCAD, which has no built-in support for creating helical structures like threaded rods and sockets. I found an open-source thread module on the web, but I found its parameterization awkward, so I ended up writing my own.

Here are my practice pieces for designing threaded rods and sockets with OpenSCAD, and printing them on the Monoprice Delta Mini.

All the test pieces were printed with the threads spiraling around the vertical Z-axis, to minimize the amount of unsupported filament. The undersides of the threads are a bit rough, but the roughness can be minimized by making the threads have a nearly triangular profile, so that only a little bit of filament is unsupported in each layer.

The first threaded rod, at the top left, screws easily into a broom head, and has fairly smooth surfaces, but was a bit too loose (diameters a little too small) and only a rather thin part of the thread was taking most of the contact force. The second one was sturdier, but still too loose.  The third one was too fat and would not screw into a broom head, but the fourth one was a good fit, screwing in easily, but not being wobbly.

The first two sockets worked with the first two printed screws, but not with the steel threads on my broom sticks—there was not enough clearance.  The third one fit fairly well and had smooth threads, but the threads were rather thin, and I felt they would not be sturdy enough. The two sockets with plates were beefier, and were experiments in seeing whether I could print horizontal screw holes.  I printed holes for #8 and #9 flat-head screws (countersunk), and the bridging did not cause problems with loose filaments.

I did have a little problem with the flat plates not printing properly, which I tracked down to the glass plate on the bed rotating a little in the clips (pretty much as Michael Johnson had warned me).  I fixed the problem by adding some dabs of hot-melt glue to hold the clips to the plate.  I have renew the hot-melt glue every few prints, since it does not bond the glass and the PLA permanently, but I find that an acceptable tradeoff for the ease of removing prints from glass rather than the bed that the printer comes with.

Here is the final socket attached to the broom head with 1″ #8 wood screws.

Of course, one socket at the end of the broom head would not be very secure—the length of the broom head makes a pretty big lever arm for twisting, and PLA is not that strong a plastic. So I designed a support for the broomstick at the other end of the broom head.  I could, perhaps, have used a commercial conduit strap, but the screw holes in them are a bit far apart for screwing to the broom head, so I made one that fits the broom handle I’m using precisely.

Here are the practice pieces I made, to make sure that the broom handle would fit and to check that I could make long screw holes. Note the notch on the right-hand example.

The first test piece held the broomstick well, but did not allow the broomstick to be inserted and withdrawn easily—there are a pair of screws on the brookstick that hold on the threaded end, and the screwheads stick out a little. The notch in the second test piece was carefully designed to just allow the screwheads through, but I added 1mm more clearance for the final piece.  The test pieces were printed with thin shells and 10% or 20% infill, and so were very light, but I printed the final pieces with thick shells and high infill (50%?), so that they ended up fairly solid.

The screw head on one side fits through the notch, and on the other side is in the diagonal space left by extending the circular hole to form a U. The block is held in place with 2″ #9 deck screws.

Although the deck screws were supposed to drill their own holes in the wood, I found that I had to drill pilot holes for them—the first one I inserted split the wooden broom head, which I had to glue back together. I also made a mistake in printing the U-shaped piece with the screw holes horizontal, as torquing down the last screw started to split the plastic along the laminations. If I were to reprint it, I would print it “legs-up” so that the deck screws compress the laminations together.

Here is the finished roof-valley sweeper, mounted on the telescoping pole.

The broom head is still usable as an ordinary broom, as the extra hardware does not interfere with inserting a broomstick in the normal way.

I used the roof-valley sweeper today to clean one of the roof valleys on my tile roof (the one reachable from the porch roof). It worked well to remove a year’s accumulation of leaves and twigs, though I’ll probably have to use it again this fall, after the trees have shed their leaves. I still have the other roof valleys to clean, which needs to be done with the telescoping pole from a ladder.

I don’t expect the PLA pieces to last forever (this is not UV-resistant material), but I store the broom head in a dark garage, so I expect to get a few year’s use out of them.

I’ve included the OpenSCAD source code below, but it can be more easily downloaded from
https://www.thingiverse.com/thing:3033594

The threaded_rod module:

// All dimensions in mm

// build a polygon for the cross-section of the thread 
// perpendicular to the thread.  
// The outer edge of the thread is on x=0 and all coordinate <=0
module thread_cross_section(height, pitch, base_fraction, top_fraction, pitch_angle)
{
    scale = cos(pitch_angle);
    scaled_top  = top_fraction*pitch*scale;
    scaled_base = base_fraction*pitch*scale;
    polygon(points= [ [-height,0], 
        [0, -(scaled_base-scaled_top)/2],
        [0, -(scaled_base+scaled_top)/2],
        [-height,-scaled_base]]);    
}

// one segment of the thread for the threaded rod, but oriented along the z-axis
module thread_segment(height, pitch, base_fraction, top_fraction, pitch_angle, segments_per_turn)
{
    length = pitch/(sin(pitch_angle) * segments_per_turn);
    // echo("pitch=",pitch, "pitch_angle=",pitch_angle, segments_per_turn,"segments, length=",length);
    linear_extrude(height=length, slices=1)
    {   thread_cross_section(height=height, pitch=pitch,
            top_fraction=top_fraction,
            base_fraction=base_fraction,
            pitch_angle=pitch_angle);
    }
}


// make a long cone for intersecting with screw to trim z=0 end
module chamfer_cone(inner_diam, outer_diam, chamfer_length, total_length)
{
    cylinder(d1=inner_diam, 
             d2=inner_diam+ (outer_diam-inner_diam)*total_length/chamfer_length,
            h=total_length, $fn=60);
}

// Build a threaded rod with trapezoidal threading
//       ____________                  _____________
//  ____/            \_______________/
// Threads start at z=0 on the x axis.
module threads(
        //Screw thread dimensions:
        //Length of Screw
        screw_length = 25.0,

        // Diameter at base of threads
        inner_diam = 13.5,
        // diameter at top of threads
        outer_diam = 18,

        //Thread Pitch
        pitch = 5.0,
        // Thread fraction (thickness of thread at base/pitch)
        base_fraction=1,
        // Thread fraction (thickness of thread at top/pitch)
        top_fraction=0,

        // how much of turn is made per segment of result
        degrees_per_segment=6,

        // chamfer at start (Z=0)  (defaults to pitch/2, use 0 to turn off)
        chamfer_start=-1,
        // chamfer at end (Z=screw_length)
        chamfer_end=0
        )
{
    pi= 3.1415926535897932346;
    pitch_angle = atan(pitch/ (pi *outer_diam));
    
    augmented_length = screw_length+pitch;
    turns = augmented_length/pitch;
    segments_per_turn = 360/ degrees_per_segment;
    segments = ceil(turns*segments_per_turn);
    
    // set the lengths for the chamfers if defaults needed
    local_chamfer_start = chamfer_start<0? pitch/2: chamfer_start;
    local_chamfer_end = chamfer_end<0? 0: chamfer_end; intersection() { if (local_chamfer_start>0)  
        {   chamfer_cone(inner_diam=inner_diam, 
                outer_diam=outer_diam, 
                chamfer_length=local_chamfer_start,
                total_length=augmented_length);
        }
        else
        {    cylinder(d=2*outer_diam, h=augmented_length);
        }
        translate([0,0,screw_length])   
            rotate([180,0,0]) 
                if (local_chamfer_end>0) 
                {     chamfer_cone(inner_diam=inner_diam, 
                    outer_diam=outer_diam, 
                    chamfer_length=local_chamfer_end,
                    total_length=augmented_length);
                }
                else
                {   cylinder(d=2*outer_diam, h=augmented_length); 
                }
        union()
        {
            color("red") cylinder(d=inner_diam+0.001, h=screw_length, $fn=segments_per_turn);
            for (i = [0:segments])
            {   angle = i*degrees_per_segment;
                translate([outer_diam*cos(angle)/2,
                          outer_diam*sin(angle)/2,
                          i*pitch*degrees_per_segment/360])
                    rotate([0,0,angle])
                        rotate([90-pitch_angle,0,0])
                thread_segment(height=(outer_diam-inner_diam)/2, 
                    pitch=pitch, 
                    base_fraction=base_fraction,
                    top_fraction=top_fraction, 
                    pitch_angle=pitch_angle, 
                    segments_per_turn=segments_per_turn);
            } 
        }
    }
}

// example
// rotate([-180,0,0]) union()
// {
//    color("green") translate([0,0,25-0.001])
//            cylinder(d=23, h=5,$fn=60);
//    threads(top_fraction=0.3, base_fraction=0.6, 
//        screw_length=25,
//        inner_diam=15, outer_diam=18);
// }

The broomstick socket:

use <threaded_rod.scad>

// create a screwhole for metric screw of given length,
// with head extending in -z direction and screw in +z direction
// Loose=0.10 for loose fit, 0.05 for tight fit, about -0.15 for threaded hole
module screw_hole(metric_size=3, length=10, loose=0.10)
{   union()
    {   cylinder(d=metric_size*(1+loose), h=length*1.1, $fs=0.3);  // body of screw
    translate([0,0,-metric_size]) cylinder(d=metric_size*2.2, h=metric_size*1.1, $fs=0.3);  // counterbore for head of screw
    }
}

// make countersunk hole for flathead screw
// surface on xy plane at (0,0), screw extends in +z direction.
// length is the length of the hole for the threads (past the base of the head)
// diam is the diameter of the hole for the screw threads
// depth is the depth of the countersink
// top_diam is the diameter at the surface (the xy plane)
module countersunk(length=35, diam=5, depth=3, top_diam=11)
{
    union()
    {    cylinder (d=diam, h=length+depth, $fs=0.3);   // hole for screw
        // countersink (adding 0.001 overshoot to avoid coincident faces)
        translate([0,0,-0.001]) cylinder (d1=top_diam+0.2, d2=diam, h=depth+0.1, $fs=0.3); 
    }
}

// make countersunk hole for 10-24 flathead machine screw
module countersunk_10_24(length=35)
{    countersunk(length=length, diam=5, depth=3, top_diam=11);

} 

// make countersunk hole for 8-32 flathead wood screw 
module countersunk_8_32(length=22, diam=4.3, top_diam=7.6, depth=3.2)
{
    countersunk(length=length, diam=3.2, top_diam=7.6, depth=3.2);
}


// example using broom handle thread
module broom_handle()
 {
    rotate([-180,0,0]) union()
    {
        color("green") translate([0,0,15-0.001]) cylinder(d=23, h=5,$fn=60);
        threads(top_fraction=0.35, base_fraction=0.7, 
            screw_length=15,
            inner_diam=15.5, outer_diam=18.5);
    }
}
// makes a threaded rod that matches the one in broom_handle,
// but bloats diameters and threads.
// By subtracting this from an object, we should be able to
// create an acceptable socket for the broom handle.
module bloated_broom_thread(bloat=0.5, depth=20,
	inner_diam=15.5,
	outer_diam=18.5,
	pitch=5,
	top_fraction=0.35,
	base_fraction=0.7)
{
    threads(top_fraction=min(0.95,top_fraction+0.5*bloat/pitch), 
        base_fraction=min(1,base_fraction+0.5*bloat/pitch), 
        screw_length=depth,
        inner_diam=inner_diam+bloat, outer_diam=outer_diam+bloat,
        pitch=pitch, chamfer_start=0);
}

// Make a cylinder with a plate whose outer edge is tangential to the cylinder
// cylinder has axis along z-axis.
// Plate is parallel to xz-plane on positive y side.
// The cylindrical boss has a fillet that goes from the diameter
//   an extra "fillet" mm wider on each side where it touches the plate.
module plate_with_boss(width=55, length=55, boss_diam=30, thickness=5, fillet=2.5)
{
   boss_radius = boss_diam/2;
   fillet_height=boss_radius-thickness;
   union()
   {  
      color("green")cylinder(d=boss_diam, h=length, $fn=90);
      color("red") translate([-width/2,boss_radius-thickness+0.001,0.001]) cube([width,thickness,length]);
      color("blue") translate([-boss_radius-0.001,0,-0.001]) cube([boss_diam+0.002, boss_radius-0.001, length]);
       linear_extrude(height=length)
       {    polygon(points=[
                [boss_radius-0.1, fillet_height+0.1],
                [boss_radius, fillet_height-fillet], 
                [boss_radius+0.25*fillet, fillet_height-0.5*fillet],
                [boss_radius+0.5*fillet, fillet_height-0.25*fillet],
                [boss_radius+fillet, fillet_height]
           ]);
       }
       linear_extrude(height=length)
       {    polygon(points=[
                [-boss_radius+0.1, fillet_height+0.1],
                [-boss_radius, fillet_height-fillet], 
                [-boss_radius-0.25*fillet, fillet_height-0.5*fillet],
                [-boss_radius-0.5*fillet, fillet_height-0.25*fillet],
                [-boss_radius-fillet, fillet_height]
           ]);
       }
   }  
}

boss_diam=28;
depth=30;   // length of threads in socket
width = 60; // width of plate
length = depth+4; // length of plate
thickness = 7;

screw_depth=max(0,thickness-3.5);  // leave thin plate closing hole

screw_x = boss_diam/2+7;
screw_y = boss_diam/2-thickness-0.001;
screw_z = 8;
screw_holes=true;
rotate([180,0,0])
    difference()
    {   plate_with_boss(boss_diam=boss_diam, length=length, width=width, thickness=thickness);
        translate([0,0,-0.01]) bloated_broom_thread(depth=depth);
        if (screw_holes)
        {    
            translate([screw_x, screw_y, screw_z])
                rotate([-90,0,0]) 
                    countersunk_8_32(length=screw_depth);
            translate([-screw_x, screw_y, screw_z])
                rotate([-90,0,0]) 
                    countersunk_8_32(length=screw_depth);
            translate([screw_x, screw_y, length-screw_z])
                rotate([-90,0,0]) 
                    countersunk_8_32(length=screw_depth);
            translate([-screw_x, screw_y, length-screw_z])
                rotate([-90,0,0]) 
                    countersunk_8_32(length=screw_depth);
        }
    }

The guide for the broomstick:

// create a screwhole for metric screw of given length,
// with head extending in -z direction and screw in +z direction
// Loose=0.10 for loose fit, 0.05 for tight fit, about -0.15 for threaded hole
module screw_hole(metric_size=3, length=10, loose=0.10)
{   union()
    {   cylinder(d=metric_size*(1+loose), h=length*1.1, $fs=0.3);  // body of screw
    translate([0,0,-metric_size]) cylinder(d=metric_size*2.2, h=metric_size*1.1, $fs=0.3);  // counterbore for head of screw
    }
}

// make countersunk hole for flathead screw
// surface on xy plane at (0,0), screw extends in +z direction.
// length is the length of the hole for the threads (past the base of the head)
// diam is the diameter of the hole for the screw threads
// depth is the depth of the countersink
// top_diam is the diameter at the surface (the xy plane)
module countersunk(length=35, diam=5, depth=3, top_diam=11)
{
    union()
    {    cylinder (d=diam, h=length+depth, $fs=0.3);   // hole for screw
        // countersink (adding 0.001 overshoot to avoid coincident faces)
        translate([0,0,-0.001]) cylinder (d1=top_diam+0.2, d2=diam, h=depth+0.1, $fs=0.3); 
    }
}

// make countersunk hole for 10-24 flathead machine screw
module countersunk_10_24(length, diam=5, depth=3, top_diam=11)
{     countersunk(length=length, diam=diam, top_diam=top_diam, depth=depth);
} 

// make countersunk hole for #9 flathead wood screw
module countersunk_9(length, diam=4.4, top_diam=8.3, depth=4)
{     countersunk(length=length, diam=diam, top_diam=top_diam, depth=depth);
} 

// make countersunk hole for 8-32 flathead wood screw 
module countersunk_8_32(length=22, diam=4.3, top_diam=7.6, depth=3.2)
{
    countersunk(length=length, diam=diam, top_diam=top_diam, depth=depth);
}

module rounded_posy_cube(x,y,z,radius=1)
{
    difference()
    {    cube([x,y,z]);
         translate([0,y,0]) 
            cube([2*radius,2*radius, 2*z+1], center=true);
         translate([x,y,0]) 
            cube([2*radius,2*radius, 2*z+1], center=true);
    }
    translate([radius, y-radius, 0]) cylinder(r=radius, h=z, $fs=0.1);
    translate([x-radius, y-radius, 0]) cylinder(r=radius, h=z, $fs=0.1);
}

U_diam=26;
inner_depth=28;
thickness=6;

U_radius = U_diam/2;
center_y = inner_depth-U_radius;
outer_height = inner_depth+thickness;

width = 60;
length = 34; // length of plate
thickness = 6;

screw_length=outer_height+2; 

// screw_x = max(U_radius+thickness+2.2, width/2-thickness-2.2);
screw_x=21;   // (moved in slightly to engage wood better)
screw_y = outer_height+0.001;
screw_z = 8;
screw_holes=true;

notch_angle = atan(center_y/U_radius);
notch_depth = 2;
notch_radius= 5;

notch_offset = U_radius+notch_depth-notch_radius;
notch_x = cos(notch_angle)*notch_offset;
notch_y = center_y + sin(notch_angle)*notch_offset;

test=false;   // set to trim the model in Z for fast printing of
    // a size test

rotate([-90,0,0])   // to make this "legs up" when printing
difference()
{   translate([-width/2,0,0]) 
        rounded_posy_cube(width,outer_height, length, radius=3);
    
    // hole for handle
    translate([0,center_y,-0.01])
        cylinder(d=U_diam, h=length+0.02, $fn=100);
    
    //extend hole to base 
    translate([-U_radius,-0.01,-0.02])
        cube([U_diam,center_y+0.02, length+0.04]);
  
    // cut notch to allow screw head through
    translate([notch_x, notch_y, -1])
        cylinder(r=notch_radius, h=length+2, $fs=0.1);
    // cut notch to allow screw head through
    translate([-notch_x, 2*center_y-notch_y, -1])
        cylinder(r=notch_radius, h=length+2, $fs=0.1);

    if (screw_holes)
    {    
        translate([screw_x, screw_y, screw_z])
            rotate([90,0,0]) 
                countersunk_9(length=screw_length);
        translate([-screw_x, screw_y, screw_z])
            rotate([90,0,0]) 
                countersunk_9(length=screw_length);
        translate([screw_x, screw_y, length-screw_z])
            rotate([90,0,0]) 
                countersunk_9(length=screw_length);
        translate([-screw_x, screw_y, length-screw_z])
            rotate([90,0,0]) 
                countersunk_9(length=screw_length);
    }
    
    if (test)
    {   // just include up through the lower screw hole
        translate([-width,-1, screw_z+6])
            cube([2*width, 2*outer_height, length]);
    }
}

2018 July 9

Monoprice delta 3d printer glass clips

Filed under: Uncategorized — gasstationwithoutpumps @ 14:50
Tags: , ,

In addition to the legs I added to my new Monoprice Delta Mini 3d printer, I’ve also added a 120mm borosilicate glass plate to the heated bed.  The glass plate needs to be immobilized, so I designed some clips loosely based on designs I saw on Thingiverse.

This is version 7 of the clip. The clip here is shown upside-down, which is how it is built on the printer. The rim holds the plate down, and the two cantilevered sections fit under the aluminum baseplate.  The image is exported directly from OpenSCAD.

Here is another view, with the clip the right way up, viewed from the pin side, rather than the glass side. The existing pin for holding down the aluminum plate rotates into place in the semicircular hole left for it.

Several of the dimensions of the clip are critical, and getting them even half a millimeter off made the clip non-functional. Because I’m not very good at measuring (nor very observant about all the dimensions I needed to measure), it took me seven tries to get a design that would work.

To make the design easier, I recalibrated my printer based on the Make magazine dimensional accuracy test object. The test object was printing a little smaller than it should, so I scaled the steps/mm calibration from the 113 steps/mm to 116 steps/mm. To make the change, I gave the “m503;” command to get the current settings, then “m92 x116 y116 z116;” to rescale the steps, and finally “m500;” to save the settings in non-volatile storage. I power-cycled the 3d printer and confirmed that the settings had been saved with “m503;”.

I designed the clips with OpenSCAD and sliced them with Cura, using 0.1mm/layer, 3 layers for the walls, and 4 layers top and bottom (I think—Cura does not save any of this metadata in the gcode file except the layer height).  The V7 clip supposedly uses 70cm of filament, which at 1.24 g/cm3 or 3g/m for 1.75mm diameter PLA is about 2.1g of PLA per clip. At 2¢/g, the clips cost under 20¢ for materials, even with the 6 failed designs, but I spent a lot of time on the design and a fair amount on the printing (each print run takes about 15–20 minutes, so there were about 3–4 hours of printing involved).

Some parts of the design were simple, like getting the 120mm diameter circular arc and the 2mm wide lip to hold down the glass plate. The harder parts were getting the pin diameter and distance from the glass plate right, leaving room on the bottom for the alignment pin, and making the clip under the plate thin enough not to interfere with the button movement for the automatic bed leveling.

This is the top view of all 7 clip designs showing the evolution from a pair of circular arcs to a more solid object.

The bottom views are more informative. V1 did not extend below the aluminum plate at all, and had too small a radius for the pin. V2 extended below the plate, but did not wrap around it and the cutout for the plate did not extend back far enough. V3 extended the cutout for late back far enough, but got the angle wrong. V4 tried wrapping around the edge of the plate, but got the depth of the slot wrong—I also had to cut some of the print with diagonal cutters to make room for the alignment pin. V5 fixed the angle of the slot and left room for the pin, but did not correct for the change in angle by increasing the slot depth. V6 got the slot depth and other XY dimensions right and reinforced the side of the slot by adding triangular panels to the clip, but was too thick below the plate, so that the buttons wouldn’t press. V7 extended the plates to reinforce the slot for the whole length of the slot and had only 1mm below the plate.

Here are the clips in place, holding down the glass plate after reprinting Make‘s dimensional test.

The prints pop off the glass plate very easily—perhaps too easily.  I used the last of our spray fixative on the plate before printing, as an attempt with nothing on the plate ended up with no adhesion—I’ll probably go buy some hair spray to use as fixative for future prints.

The glass plate does result in a shiny bottom layer for the prints—a much smoother bottom layer than the slightly textured surface of the provided bed.

Here are the dimensions of the dimensional test printed on the glass bed:

nominal 25mm 20mm 15mm 10mm
center X 24.95mm 19.95mm 15mm 10mm
center Y 24.85mm 19.9mm 15mm 10mm
outer X 25.05mm 19.95mm 14.9mm 10mm
outer Y 25.15mm 20mm 15.25mm 10mm

Center Z height: 20.4mm  Outer Z height: 20.5mm

The X measurements are using the edges with a notch, and the Y measurements are from the perpendicular pair of edges. The center measurements are from the copy closer to the center of the bed. All XY dimensional inaccuracy is less than 1%, so the step size is adjusted about as good as I can make it (maybe I could change the X to 116.1 steps/mm and Y to 115.9 steps/mm) , but the Z dimension is 2–2.5% too large, so I should set the Z steps/mm to 113.4.

Here is the OpenSCAD code for the clip. It is not the most elegant way to describe the clip (because of the evolution of the code over the 7 versions), but it works. (Update 2018 July 15: I’ve made the clips available on Thingiverse: https://www.thingiverse.com/thing:3001881)

// Clip for holding down 120mm diam glass plate
// on Monoprice Delta printer
// Kevin Karplus
// 2018 July 6 

// xy plane is top of the clip.

thickness= 3;   // thickness of glass in mm
radius = 60;   // radius of glass in mm

overhang_thickness = 2;   // thickness of overhanging lip that holds glass
overhang_width = 2;     // how fare lip extends over the glass.
glass_angle = 42;   // arc in degrees of lip for glass

above_plate= thickness+overhang_thickness;

plate = 2;      // Existing plate is 2mm thick
below_plate=1;  // Max thickness below plate
                // (2mm space, but need 1mm travel for bed leveling)

height = above_plate + plate + below_plate;

pin_radius = 5.5;   // radius of circle cut away for pin
big_pin_radius = pin_radius+4; // size of ring around pin

pin_center = radius + 11;

plate_angle = 100;   // angle of plate edges in degrees.

intersection()
{
    difference()
    {
        union()
        {

            // Part that holds down glass
            intersection()
            {
                difference()
                {   // outer-edge
                    cylinder(r=radius+4, h=height, $fn=180);  // outer edge
                    translate([0,0,overhang_thickness]) cylinder(r=radius, h=height, $fn=180); //glass edge
                    cylinder(r=radius-2, h=3*height, center=true, $fn=180);// inner edge
                } // end difference

                // wedge to reduce arg
                linear_extrude(height=3*height, center=true)
                {   polygon(points=[ [0,0],
                        [100,100*tan(glass_angle/2)],
                        [100,-100*tan(glass_angle/2)]]);
                }

            } // end intersection

            // part that connects with existing clip pin
            translate([pin_center,0,0])
                difference()
                {   cylinder(r=big_pin_radius, h=height, $fs=0.2);  //outer edge
                    cylinder(r=pin_radius, h=3*height, center=true, $fs=0.2); // edge contacting pin

                    // cut to half circle
                    translate([3*pin_radius,0,0])
                        cube([6*pin_radius,6*pin_radius,3*height], center=true); 

                } // end difference

            // triangular fill to reinforce join between pin and glass
            translate([pin_center,0,0])
            {   outside_x= pin_center - cos(glass_angle/2)*(radius+4);
                linear_extrude(height=height)
                {    polygon(points=[ [-big_pin_radius, 0], [0, big_pin_radius],
                        [-outside_x, big_pin_radius+outside_x*tan(plate_angle/2)]]);
                };
                linear_extrude(height=height)
                {    polygon(points=[ [-big_pin_radius, 0], [0, -big_pin_radius],
                        [-outside_x, -big_pin_radius-outside_x*tan(plate_angle/2)]]);
                };
            } // end translate
        } // end union

        // cut away space for baseplate
        triangle_x = 40;  // large enough to cut everything
        triangle_y = triangle_x * tan(plate_angle/2);

        translate([pin_center+3,0,above_plate])
            {    linear_extrude(height=plate)
                    polygon(points=[[0,0],
                                [-triangle_x,-triangle_y],
                                [-triangle_x,triangle_y]]);
            }
    }

    // keep only those parts that don't interfere with the alignment pin
    // trimming the cantilevered below-plate part to a narrow rim
    union()
    {
        // everything above the plate is fine
        translate([0,0,above_plate/2]) cube([500,500,above_plate+0.001], center=true);
        // below the plate, make circular keep-regions
        keep_radius=22;
        translate([pin_center+8, keep_radius, above_plate])
            cylinder(r=keep_radius, h=height, $fn=180);
        translate([pin_center+8, -keep_radius, above_plate])
            cylinder(r=keep_radius, h=height, $fn=180);
    }
}

2018 July 1

Adding feet to the Monoprice Delta Mini 3D printer

Filed under: Uncategorized — gasstationwithoutpumps @ 09:22
Tags: , , ,

The first functional prints I’ve created from the new Monoprice Delta Mini 3D printer that I bought earlier this week (see Three boxes this morning!) are small plates to attach rubber feet to the bottom of the printer, to raise it off the table and improve airflow.

I looked at what Thingiverse had for accessories for the printer, and several people had designs for feet, but I didn’t really like any of the designs, so I decided to design my own.

Because 3D printing is so slow, I decided not to try to print tall feet, but to use some rubber feet that I bought back in 2012 from Parts Express. (If you follow that link, you’d find that Parts Express no longer has these feet, but has slightly smaller black rubber feet for about 66¢ each.)  This meant that I only had to design and print adapter plates that could be screwed on over the existing  feet, with a central hole for attaching the rubber foot.

The plate attaches with M3 screws. The design calls for replacing the existing M3×8 Phillips head screws with M3×12 socket-head screws, and I decided to recess the sockets into the plate, both for looks and to see whether the printer could bridge over the recess.  (If the printer had not been able to bridge, then I would have made that surface of the adapter plate be flat, and used M3×16 screws instead.)

I spent a fair amount of time measuring the irregular hexagonal end of the uprights of the printer, so that I could match it and the get the screw holes in the right places.  The measurements were not perfectly consistent, so I had to decide which measurements to take as “correct” and which to compute based on the chosen parameters.  I decided that all the angles should be treated as canonical (multiples of 30°), since that seemed like a likely choice for the designers of the printers.  I decided that the two parallel edges and the distance between them would be my other defining parameters, since that allowed easy definition in Cartesian coordinates, which also seemed like a likely choice for the original designers.

I did the design in OpenSCAD, which does not have anywhere near the feature richness of a professional tool like SolidWorks, but which is (for a programmer) much easier to learn to use, and easier to get precise results with.

Unfortunately, OpenSCAD does not produce the pretty renderings that SolidWorks does, so I can’t show you pretty design pictures. I can, however, share the source code for the design, which you can modify to produce different designs, or just compile and print. The code is at the end of this post.

View of the adapter plate from the outside, rendered by Finder’s “Quick Look” on a Mac.

View of the adapter plate from the printer side, showing the countersunk hole in the center, rendered by Finder’s “Quick Look” on the Mac.

My first print was to test whether I had the holes in the right places, and whether the printer was printing things at the specified size.  (I was pretty sure it was not, as I had printed the Make magazine test piece for dimensional accuracy, and had seen that the printer was printing about 3% small.)  The test print was just a 3mm slab, printed with 10% infill and 0.2mm layers for speed.  OpenSCAD made it easy to create this slab, by intersecting the design with a rectangular prism of the appropriate thickness and location.

The two test pieces I printed. Test piece 1 is a little small and has a few holes misplaced.  On test piece 2, you can see a little “stringing” where the unsupported bridging filaments drooped, but the overall integrity of the bridge seemed adequate.

The slab showed me that the printer was indeed printing a little small, and that I had misplaced the hole for the existing printer foot by 1mm and the screw holes further from the outside edge by about 0.5mm.  I moved the holes, figured out how to do scaling in Cura to scale the part by 102.9% when slicing, and did another test print—a 4mm slab that included the end of the recesses for the socket-head screws, so that I could test the overhang capability.  I printed this one with 20% infill.

The final design is a slab 7mm thick, which 5 screw holes: 4 for the M3 socket-head screws, and one for a 10-24 flat-head screw for attaching the rubber foot. The 10-24 hole is countersunk and is at the base of a cylindrical recess deep enough that the head of the screw has clearance from the foot that is already on the base of the printer. Because the existing feet are just stuck on with double-stick tape, it would probably have been easier to remove them rather than make clearance for them.

Outside view of a disassembled leg.

Printer-side view of a disassembled leg.

Because the 10-24 screw will be hard to retighten once the leg is on the printer, I squirted a little low-temperature hot-melt glue onto the nut after tightening it, so that it would not work loose from vibration.

The three printed legs, with the rubber feet attached. If you look closely, you can see a little of the clear hot-melt glue in the right-hand foot, to keep the nut from loosening.

Bottom view of the printer with the legs attached.

Side view of the printer with the legs attached.

Closeup of the printer with the legs attached, showing the greater space now available for airflow.

I printed the legs one at a time, so that in the event of printer failure, I would only have to redo one leg, rather than all three.

To print the legs, you need to open the .scad file with OpenSCAD, render it, and output a .stl file. Then use Cura to slide the model. I chose a layer height of 0.1mm, a wall count of 4 layers (for strength and stiffness), 4 top layers and 3 bottom layers, a concentric top and bottom pattern, 20% infill (a compromise between strength and speed of printing), and no special build-plate adhesion. The top layers are excessive, as that face is buried against the bottom of the printer and does not need to be pretty. The first leg I printed had some sort of printer/communication failure and froze after only one top layer had printed, and it is still a perfectly usable leg.

(Update 2018 July 15: I’ve made the clips available on Thingiverse: https://www.thingiverse.com/thing:3001872)

Here is the code for the adapter plate for extending the legs of the printer:

// Kevin Karplus
// 2018 June 30
// Leg for extending height of Monoprice Delta Printer, to improve airflow under printer.


// create a screwhole for metric screw of given length,
// with head extending in -z direction and screw in +z direction
// Loose=0.10 for loose fit, 0.05 for tight fit, about -0.15 for threaded hole
module screw_hole(metric_size=3, length=10, loose=0.10)
{   union()
    {   cylinder(d=metric_size*(1+loose), h=length*1.1, $fs=0.3);  // body of screw
    translate([0,0,-metric_size]) 
        cylinder(d=metric_size*2.2, h=metric_size*1.1, $fs=0.3);  // counterbore for head of screw
    }
}

// make countersunk hole for 10-24 flathead screw
// surface on xy plane at (0,0), screw extends in +z direction.
// length is the length of the hole for the threads (past the base of the head)
// diam is the diameter of the hole for the screw threads
// depth is the depth of the countersink
module countersunk_10_24(length=35, diam=5, depth=3)
{
    // top_diam is the diameter at the surface (the xy plane)
    // assumes that cone has an apeture of 90°
    top_diam = diam + 2*depth;
    union()
    {    cylinder (d=diam, h=length+depth, $fs=0.3);   // hole for screw
        // countersink (adding 0.1 overshoot to avoid coincident faces)
        translate([0,0,-0.1]) 
cylinder (d1=top_diam+0.2, d2=diam, h=depth+0.1, $fs=0.3); 
    }
}


// polygon that matches the foot of the Monoprice Delta printer,
// with outside edge on the x-axis, with center of edge at origin.
// "base" is the length of the outside edge in mm.
// Long edge of plate parallel to x-axis at y=height, of length long.
//
// The default parameter values were measured from the printer.  
// The angles are taken as canonical, and the parameters were chosen as
// those that could be easily defined on a Catersian coordinate system,
// with the other side lengths calculated from the geometry.
module foot_poly(base=50,long=65, height=32)
{   
    // how wide trapezoid would be without trimmed-off corner
    extra_long= base+ 2* height/tan(60);
    echo("extra_long=",extra_long);
    
    short = (extra_long-long)/2 * cos(30);  // shortest edge
    
    // Width at widest point (right-angle vertex)
    width =long + 2*short*cos(30);
    
    // remaining edge
    side = (width-base)/(2*cos(60));
    
    // How high up is the widest point
    height_at_width= side*sin(60);
    
    echo("edges=", base, side,short, long);
    echo("width=",width, "height=", height);
    echo("height_at_width=",height_at_width);
    polygon(points=[ [base/2,0], 
            [width/2, height_at_width],
            [long/2, height],
            [-long/2, height],
            [-width/2, height_at_width],
            [-base/2,0]]);
}



module foot(screw_length=12)
{
    // For the 4 M3 screws that hold foot plate to delta printer
    hole_length=screw_length-8;  // how much of screw thread is left?
    counterbore= 3;  // depth of counterbore
    thickness = hole_length+counterbore;
    
    old_foot = 3.7;  // height of bore to avoid old foot
    
    difference()
    {
        color ("red")  linear_extrude(height=thickness) foot_poly();

        color("green") translate([22.5,5,counterbore])  
            screw_hole(metric_size=3,length=hole_length);
        color("green") translate([-22.5,5,counterbore])
            screw_hole(metric_size=3,length=hole_length);
        
        color("blue") translate([32.5,25,counterbore])
            screw_hole(metric_size=3,length=hole_length);
        color("blue") translate([-32.5,25,counterbore]) 
            screw_hole(metric_size=3,length=hole_length);
 
        // make hole for old foot
        center=18;
        color("brown") translate([0,center,thickness-old_foot]) 
            cylinder(d=14,h=old_foot +0.1, $fs=0.6);
        
        // make countersunk hole for 10-24 screw for new foot
        color("magenta") translate([0,center,thickness-old_foot])
            rotate([180,0,0]) countersunk_10_24();
        
    }
}


{  foot();  
}

Blog at WordPress.com.

%d bloggers like this: