Gas station without pumps

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]);
    }
}

5 Comments »

  1. Thanks for sharing this! Doing this kind of ‘special’ things is excactly where 3D printers are so helpful. I have used OpenSCAD only a bit and not to a more expert level (I think usuability of the UI could be better, and a I had issues with the rendering). But I love the OpenSCAD principle, but in many cases I ended up doing things in a ‘quick-and-dirty’ way with a simple CAD program instead. Your post encourages me to get used to OpenSCAD more :-)

    Comment by Erich Styger — 2018 August 3 @ 23:07 | Reply

    • I don’t find OpenSCAD to be a great tool—the error messages are terrible (assertion failures when a polygon is rotated to form something that isn’t a proper 2-manifold). The visualization is crude, there are no measuring tools, and tools for making curves are practically non-existent (it would be nice if in addition to polygon 2D tools, it had something like SVG’s path. The rendering of even simple shapes in CGAL takes forever, but needs to be done to generate the STL output format.

      But I’m a programmer, not a graphic artist, and I found the SolidWorks drawing tool very frustrating—writing code is more comfortable. I like that I can make reusable, parameterizable modules in OpenSCAD, which is very difficult in more sophisticated CAD tools.

      Along the same lines, when making templates for laser cutting, I sometimes find it easier to write SVG manually rather than try to use inkscape or SolidWorks.

      Comment by gasstationwithoutpumps — 2018 August 4 @ 09:06 | Reply

  2. Sorry my advice was correct. ☺ If you have kapton tape sitting around from electronics work, you might try shimming your clips with one or more layers of it and see if it helps. But hot glue makes sense.

    I made a specialized rake head out of PETG that I clamped to a garden rake head. It had bent tines to get under acorns and pull them up out of the grass. I printed it on end, which, although it required printing supports, also made the tines strong enough to use for a season of acorn removal—about 6 large wheelbarrow loads, so about 36 cubic feet of acorns. If your attachments break, consider reprinting them in PETG. You need to print hotter, but it’s quite tough as my experience showed. It can be a little “stringy” compared to PLA but the material characteristics can make up for that.

    Comment by Michael Johnson — 2018 August 4 @ 17:18 | Reply

    • I don’t have any kapton tape—I’ve never heard of hobbyist uses for it other than 3D printing (I realize that is is used as a high-temperature insulation, but I’ve never needed that). The low-temperature hot-melt glue takes only a few seconds to apply (after the glue gun heats up), so I’ll stick with that for a while. I have some higher-temperature glue sticks also (and a beefier glue gun), if I need it.

      Printing with PETG is something I’ve thought of, but from what I’ve seen on the web, the Monoprice Delta Mini can’t get bed temperatures high enough (75–75°C with extruder at 220–245°C). The problem is that they sell it with a wimpy power supply, then wrote the firmware so that the extruder heater and bed heater aren’t on at the same time. I’d need to replace the power supply (easy) and replace the firmware (somewhat risky).

      The Monoprice Delta Mini printer is a toy, and I bought it as such, so I’m not upset by its limitations—it is still much better than printers from 5 years ago that cost 5–10 times as much.

      Comment by gasstationwithoutpumps — 2018 August 4 @ 17:35 | Reply

      • Ah, I used kapton first for masking off around small soldering rework, before I had a 3d printer.

        Comment by Michael Johnson — 2018 August 4 @ 17:46 | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: