The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Machine Shop

Mechanical widgetry

  • Drying Rack Re-Repair

    Despite having sworn a mighty oath to the contrary, I found myself doing this again:

    Clothes Rack - end clamp
    Clothes Rack – end clamp

    A strut on the other end of the dowel split across its face:

    Clothes Rack - split clamp
    Clothes Rack – split clamp

    The white stuff is wood-filled epoxy, normally used to repair rotted wood, left over from another project. I’ll claim this tests its mechanical strength against peeling forces.

    Easily determined by inspection: a sensible person would toss the rack, but …

  • HP 7475A Plotter: Pen Carousel Geneva Drive

    A note arrived from someone who obviously couldn’t pass up an orphaned HP 7475A plotter:

    The plotter I received works beautifully, except that the carousel doesn’t rotate. I found a YouTube video showing a 7475a running with the cover off, and there’s a little plastic piece – it looks like a teardrop – that advances the carousel, and is apparently part of the carousel motor assembly. Mine is missing that piece …

    The keyword is Geneva drive, a wonderfully simple technique to convert one rotation of the stepper motor into 1/6 turn of the pen carousel, with no need for fancy sensors.

    The (unofficial) HP Computer Museum has All The HP 7475A Documents and the Plotter Service Manual shows All The Parts. And, of course, I’ve written a bit about my adventures with an old 7475A.

    Back in the day, you could get the entire Pen Carousel Housing Assembly w/ Motor (PN 07475-60175) as a unit and the Carousel Motor Only (PN 3140-0687) as a separate thing, but not the Geneva drive wheel:

    HP7475A Carousel - Geneva drive cam
    HP7475A Carousel – Geneva drive cam

    The cam’s drive wheel end (in inches, because early 1980s):

    • 0.25 thick overall
    • 0.10 thick plate under pin end
    • 1.09 OD – rounded end

    The pin sticking up from the cam:

    • 0.154 OD (or fit to slot?)
    • 0.16 tall (above base plate)

    I have no good (i.e., easy + accurate) way to measure the distance from the motor shaft to the pin, but I doubt it’s critical. As long as the pin doesn’t quite whack the hub end of the slot, it’s all good:

    HP7475A Carousel - cam driving
    HP7475A Carousel – cam driving

    The 0.10 plate + 0.16 pin height don’t quite add up to the 0.25 overall measurement, but that’s certainly measurement error. I’d round the pin length downward and carve the drive from a 1/4 inch sheet.

    A 3D printed part would probably work, apart from the accuracy required to fit the D-shaped motor shaft. Perhaps a round hole, reamed to fit the shaft, carefully aligned / positioned, with epoxy filling the D-shaped void, would suffice. A dent in the round hole would give the epoxy something to grab.

    I’d be sorely tempted to use an actual metal / plastic rod for the pin, rather than depend on a stack of semi-fused plastic disks. The pin must withstand hitting the end of the “missing” slot during the power-on indexing rotation, because turning the carousel isn’t quite a non-contact sport. Normally, though, it enters the end of the slot without much fuss:

    HP7475A Carousel - cam engaging
    HP7475A Carousel – cam engaging

    The blocked slot sits at the bottom of that picture, with a small locating pin sticking upward just above the circular feature at the end of the arm: we’re seeing the negative of a plug inserted into the original injection mold.

    With a bit of luck, another HP 7475A plotter will fascinate everybody within hearing distance!

    [Update: It lives! ]

  • Mailing Tube End Caps: Screw-in Version

    The mailing tube arrived with contents intact, although the USPS inlet scanning didn’t work and the tube pretty much teleported across several states without leaving any tracking data behind. The recipient suggested several modifications to the caps:

    Review of user experience of tube end:
    The ribs on the endcap are very good at holding the cap on, so much so that I had to use a prying implement to remove it, which cracked the flange.
    Would consider less depth on the cap, and possibly another layer on the flange.

    Some continuous process improvement (a.k.a OpenSCAD hackage) produced a swoopy threaded cap with thumb-and-finger grips:

    Mailing Tube Screw Cap - top - Slic3r
    Mailing Tube Screw Cap – top – Slic3r

    The finger grips are what’s left after stepping a sphere out of the cap while rotating it around the middle:

    Mailing Tube Cap - finger grip construction
    Mailing Tube Cap – finger grip construction

    That worked out surprisingly well, with the deep end providing enough of a vertical-ish surface to push against.

    The two hex holes fit a pin wrench, because the grips twist only one way: outward. The wrench eliminates the need for a flange, as you can now adjust the cap insertion before slathering packing tape over the ends. Man, I loves me some good late binding action!

    A three-start thread seemed like overkill, but was quick & easy. The “thread form” consists of square rods sunk into the cap perimeter, with one edge sticking out:

    Mailing Tube Cap - thread detail
    Mailing Tube Cap – thread detail

    They’re 1.05 times longer than the cap perimeter facets to make their ends overlap, although they’re not tapered like the ones in the broom handle dingus, because it didn’t (seem to) make any difference to the model’s manifoldhood.

    Not needing any endcaps right now, I built one for show-n-tell:

    Threaded mailing tube end cap - installed
    Threaded mailing tube end cap – installed

    The OpenSCAD source code as a GitHub Gist:

    // Mailing tube end cap
    // Ed Nisley KE4ZNU – June 2017
    Layout = "Build";
    Model = "Screw";
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    inch = 25.4;
    TubeID = 2 * inch;
    TubeWall = 0.1 * inch;
    CapInsert = 15.0;
    CapRim = 6*ThreadThick;
    CapWall = 3*ThreadWidth;
    NumFlanges = 3;
    FlangeHeight = 3*ThreadThick;
    FlangeWidth = ThreadWidth/2;
    FlangeSpace = CapInsert / (NumFlanges + 1);
    ThumbHoleOD = 20.0;
    ThumbHoleAngle = 100;
    ThumbHoleSteps = 10;
    SpannerPinOD = 5.0;
    HelixOD = 4*ThreadThick;
    HelixHeight = 0.75*CapInsert;
    HelixAngle = atan(HelixHeight/(PI*TubeID));
    HelixStarts = 3;
    OAHeight = CapInsert + CapRim;
    NumRibs = 3*4;
    NumSides = 3*NumRibs;
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    module ScrewCap() {
    union() {
    difference() {
    cylinder(d=TubeID,h=OAHeight,$fn=NumSides);
    for (a=[0,180])
    for (i=[0:ThumbHoleSteps-1])
    rotate(a + i*ThumbHoleAngle/ThumbHoleSteps)
    translate([TubeID/4,0,-i*ThumbHoleOD/(2*ThumbHoleSteps)])
    sphere(d=ThumbHoleOD);
    for (a=[0,180])
    rotate(a – 60)
    translate([0.75*TubeID/2,0,-Protrusion])
    rotate(0*180/6)
    PolyCyl(SpannerPinOD,0.75*CapInsert,6);
    }
    for (s=[0:HelixStarts-1])
    for (i=[0:NumSides-1])
    rotate(i*360/NumSides + 180/NumSides + s*360/HelixStarts)
    translate([TubeID/2 – 0.25*HelixOD,0,i*HelixHeight/NumSides + HelixOD])
    rotate([90 + HelixAngle,0,0])
    cylinder(d=HelixOD,h=1.05*PI*TubeID/NumSides,center=true,$fn=4);
    }
    }
    module PushCap() {
    difference() {
    cylinder(d=TubeID,h=OAHeight,$fn=NumSides);
    translate([0,0,CapWall])
    cylinder(d=TubeID – 2*CapWall,h=OAHeight,$fn=NumSides);
    }
    for (i=[1:NumFlanges])
    translate([0,0,i*FlangeSpace])
    difference() {
    cylinder(d=TubeID + 2*FlangeWidth,h=FlangeHeight,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 2*CapWall,h=FlangeHeight + 2*Protrusion,$fn=NumSides);
    }
    for (i=[0:NumRibs-1])
    rotate(i*360/NumRibs)
    translate([0,-ThreadWidth,CapWall + ThreadThick])
    cube([TubeID/2 – CapWall/2,2*ThreadWidth,CapInsert + CapRim – CapWall – ThreadThick],center=false);
    translate([0,0,CapInsert]) {
    difference() {
    cylinder(d=TubeID + 2*TubeWall,h=CapRim,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 3*2*CapWall,h=2*CapRim,$fn=NumSides);
    }
    }
    }
    //- Build things
    if (Model == "Push")
    if (Layout == "Show")
    PushCap();
    else if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    PushCap();
    if (Model == "Screw")
    if (Layout == "Show")
    ScrewCap();
    else if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    ScrewCap();
  • Handbag Strap Rivet Repair

    One of the leather strap anchors on Mary’s giant haul-everything-to-a-concert(*) handbag pulled its rivet through the canvas fabric:

    Handbag - pulled-through rivet
    Handbag – pulled-through rivet

    We knotted the strap around the zippered opening and completed the mission.

    Of course, it wouldn’t have pulled through if they’d splurged on washers, but noooo too expensive:

    Handbag - intact rivet - inside
    Handbag – intact rivet – inside

    Some rummaging produced a pan-head M3 screw of suitable length:

    Handbag - repaired - outside
    Handbag – repaired – outside

    A slightly battered acorn nut was a special treat for the inside, with another washer to keep me happy:

    Handbag - repaired - inside
    Handbag – repaired – inside

    That was easy!

    (*) At Tanglewood, where they don’t strip-search you on the way in, tow-behind coolers seemed de rigueur, and a good time was had by all.

  • Mailing Tube End Caps

    Faced with a need to send documents rolled up in a tube, rather than folded flat, I sawed off a suitable length of cardboard tube from the heap, then discovered a distinct lack of end caps.

    Well, once again, it’s 3D printing to the rescue:

    Mailing Tube Cap - top - Slic3r
    Mailing Tube Cap – top – Slic3r

    The small ribs probably don’t actually do anything, but seemed like a nice touch.

    They’re somewhat less boring from the bottom:

    Mailing Tube Cap - bottom - Slic3r
    Mailing Tube Cap – bottom – Slic3r

    The fancy spider supports that big flat top and provides some crush resistance. The flat flange should collect the edge of the packing tape wrapped around the ends.

    A firm shove installs them, so the size worked out perfectly:

    Mailing tube end cap - installed
    Mailing tube end cap – installed

    Add a wrap of tape to each end, affix the USPS label, and they went out with the next day’s mail, PETG hair and all.

    The OpenSCAD source code as a GitHub Gist:

    // Mailing tube end cap
    // Ed Nisley KE4ZNU – June 2017
    Layout = "Build";
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    inch = 25.4;
    TubeID = 2 * inch;
    TubeWall = 0.1 * inch;
    CapInsert = 15.0;
    CapRim = 1.0;
    CapWall = 3*ThreadWidth;
    NumFlanges = 3;
    FlangeHeight = 3*ThreadThick;
    FlangeWidth = ThreadWidth/2;
    FlangeSpace = CapInsert / (NumFlanges + 1);
    OAHeight = CapInsert + CapRim;
    NumRibs = 3*4;
    NumSides = 3*NumRibs;
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    module TubeCap() {
    difference() {
    cylinder(d=TubeID,h=OAHeight,$fn=NumSides);
    translate([0,0,CapWall])
    cylinder(d=TubeID – 2*CapWall,h=OAHeight,$fn=NumSides);
    }
    for (i=[1:NumFlanges])
    translate([0,0,i*FlangeSpace])
    difference() {
    cylinder(d=TubeID + 2*FlangeWidth,h=FlangeHeight,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 2*CapWall,h=FlangeHeight + 2*Protrusion,$fn=NumSides);
    }
    for (i=[0:NumRibs-1])
    rotate(i*360/NumRibs)
    translate([0,-ThreadWidth,CapWall + ThreadThick])
    cube([TubeID/2 – CapWall/2,2*ThreadWidth,CapInsert + CapRim – CapWall – ThreadThick],center=false);
    translate([0,0,CapInsert]) {
    difference() {
    cylinder(d=TubeID + 2*TubeWall,h=CapRim,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 3*2*CapWall,h=2*CapRim,$fn=NumSides);
    }
    }
    }
    //- Build things
    if (Layout == "Show")
    TubeCap();
    if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    TubeCap();
  • Proto Board Holder: Revised Screw Mounts

    Improving the crystal tester’s (nonexistent) grounding requires a band of copper tape around the inside of the proto board holder. Rather than cut the tape lengthwise to fit the holder, a new one will be just tall enough:

    Proto Board - 80x120 - revised inserts - Slic3r
    Proto Board – 80×120 – revised inserts – Slic3r

    While I was at it, I deleted the washer recesses, because those didn’t work out well, and fiddled the screw holes to put the inserts in from the bottom:

    Proto Board - 80x120 - revised inserts - detail - Slic3r
    Proto Board – 80×120 – revised inserts – detail – Slic3r

    Although the overhang inside the holes will be ugly, I’ll epoxy the inserts flush with the bottom and nobody will ever know.

    The copper tape now makes a tidy ground strap:

    Crystal Tester - ground strap - rear
    Crystal Tester – ground strap – rear

    With a gap in the front to eliminate the obvious loop:

    Crystal Tester - ground strap - front gap
    Crystal Tester – ground strap – front gap

    The OpenSCAD source code as a GitHub Gist:

    // Test support frame for proto boards
    // Ed Nisley KE4ZNU – Jan 2017
    // June 2017 – Add side-mount bracket, inserts into bottom
    Layout = "Frame";
    ClampFlange = true;
    Channel = false;
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    inch = 25.4;
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [3.9,4.6,5.8];
    //- PCB sizes
    PCBSize = [80.0,120.0,1.6];
    PCBShelf = 1.0; // support rim under PCB
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    WallThick = 4.0;
    FrameHeight = IntegerMultiple(3/8 * inch,1.0);
    echo(str("Inner height: ",FrameHeight));
    ScrewOffset = 0.0 + Clear4_40/2;
    ScrewSites = [[-1,1],[-1,1]]; // -1/0/+1 = left/mid/right and bottom/mid/top
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2];
    echo(str("OAH: ",OAHeight));
    FlangeExtension = 3.0;
    FlangeThick = IntegerMultiple(2.0,ThreadThick);
    Flange = PCBSize
    + 2*[ScrewOffset,ScrewOffset,0]
    + 2*[Washer4_40OD,Washer4_40OD,0]
    + [2*FlangeExtension,2*FlangeExtension,(FlangeThick – PCBSize[2])]
    ;
    echo(str("Flange: ",Flange));
    NumSides = 4*5;
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]];
    WireChannelOffset = [Flange[0]/2,25.0,(FrameHeight + PCBSize[2] – WireChannel[2]/2)];
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Build things
    if (Layout == "Frame")
    difference() {
    union() { // body block
    translate([0,0,OAHeight/2])
    cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    for (x=[-1,1], y=[-1,1]) { // screw bosses
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    }
    if (ClampFlange) // flange for work holder
    linear_extrude(height=Flange[2])
    hull()
    for (i=[-1,1], j=[-1,1]) {
    translate([i*(Flange[0]/2 – Washer4_40OD/2),j*(Flange[1]/2 – Washer4_40OD/2)])
    circle(d=Washer4_40OD,$fn=NumSides);
    }
    }
    for (x=[-1,1], y=[-1,1]) { // screw position indexes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6); // screw clearance holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Insert[OD],OAHeight – PCBSize[2] – 3*ThreadThick + Protrusion,6); // inserts
    if (false)
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // washer recess
    }
    translate([0,0,OAHeight/2]) // through hole below PCB
    cube(PCBSize – 2*[PCBShelf,PCBShelf,0] + [0,0,2*OAHeight],center=true);
    translate([0,0,(OAHeight – (PCBSize[2] + Clearance[2])/2 + Protrusion/2)]) // PCB pocket on top
    cube(PCBSize + Clearance + [0,0,Protrusion],center=true);
    if (Channel)
    translate(WireChannelOffset) // opening for wires from bottom side
    cube(WireChannel + [0,0,Protrusion],center=true);
    }
    // Add-on bracket to hold smaller PCB upright at edge
    PCB2Insert = [3.0,4.9,4.1];
    PCB2OC = 45.0;
    if (Layout == "Bracket")
    difference() {
    hull() // frame body block
    for (x=[-1,1]) // bosses around screws
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    for (x=[-1,1]) // frame screw holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,-Protrusion])
    rotate(x*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6);
    for (x=[-1,1]) // PCB insert holes
    translate([x*PCB2OC/2,(Washer4_40OD + Protrusion),OAHeight/2])
    rotate([90,0,0])
    cylinder(d=PCB2Insert[OD],h=2*(Washer4_40OD + Protrusion),$fn=6);
    }

  • Under-cabinet LED Strip IR Sensor: Re-aimed

    The under-cabinet LED strips work wonderfully well, except that the IR sensor seemed rather hypersensitive, so I added a small reflector made of shiny steel:

    Under-cabinet light - IR sensor mirror
    Under-cabinet light – IR sensor mirror

    Even though I rounded those corners and deburred the edges, it does look a bit threatening, doesn’t it?

    It moves the sensor’s hotspot back about half a foot, which seems Good Enough to eliminate false triggering from normal activity over the cutting board.