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: Home Ec

Things around the home & hearth

  • Monthly Science: Inchworms

    Monthly Science: Inchworms

    A Rudbeckia Black-eyed-susan coneflower from the garden carried a passenger to our patio table:

    Inchworm - linear
    Inchworm – linear

    Even linearized, the inchworm was barely 20 mm long; it’s the thought that counts.

    The stamens mature in concentric rings, each stamen topped by a pollen grain. Apparently, those grains are just about the most wonderful food ever, as the inchworm made its way around the ring eating each grain in succession:

    Inchworm - feeding
    Inchworm – feeding

    Of course, what goes in must come out:

    Inchworm - excreting
    Inchworm – excreting

    I had to brush off the table before washing it; the pellets are dry, but smear when you get them wet.

    Another flower in the vase held a 10 mm inchworm with plenty of upside potential:

    Inchworm - junior edition
    Inchworm – junior edition

    After nearly a week, the flowers were done and the inchworms had moved on. We wish them well, although we likely won’t recognize them in the future.

  • Outdoor Junction Box: Stretch to Fit

    Outdoor Junction Box: Stretch to Fit

    For whatever reason, a two-outlet junction box stands outside the Credit Union:

    Outdoor Junction Box - angled conduit
    Outdoor Junction Box – angled conduit

    The slanted conduit certainly looks in need of an elbow to line it up, doesn’t it?

    It seems whoever installed it, many years ago, simply forced the conduit to line up, no matter the consequences:

    Outdoor Junction Box - open wiring
    Outdoor Junction Box – open wiring

    The threaded entries on the die-cast outlet box were never intended to cope with that much misalignment; half the bottom has vanished. I think the round box on the top originally held a floodlight to wash the (uninspired) building facade at night, but those days are long gone.

    If the conduit has horizontal underground runs, both are certainly full of water by now. The white(-ish) “Romex” cable insulation looks like ordinary indoor wiring, not the grayish direct-burial sheath, but it may be sun-bleached after years of exposure.

    Surely there’s a tripped GFI on that circuit …

  • Power Outage

    Power Outage

    Just before Tropical Storm Isaias rolled through, my hygrometer reached a new high:

    Pre-Isaias humidity
    Pre-Isaias humidity

    The National Weather Service reported 99% at the airport a few miles away, so the meter’s calibration seems about right.

    Shortly thereafter, the humidity dropped to the mid-70s as the wind picked up and, over the next few hours, falling branches took out vast swaths of Central Hudson’s electrical infrastructure. My little generator saved our refrigerator & freezer during 15 hours of outage; three days later, thousands of folks around us still have no power.

    A confluence of other events, none nearly so dramatic, will throttle my posting over the next two weeks.

    We’re OK and hope you’re OK, too …

  • EonSmoke Vape Debris

    EonSmoke Vape Debris

    Being the type of guy who uses metal bits & pieces, I thought this might be a useful aluminum rod:

    EonSmoke vape stick
    EonSmoke vape stick

    It turns out to be an aluminum tube holding a lithium cell and a reservoir of oily brown juice:

    EonSmoke - peeled open
    EonSmoke – peeled open

    The black plastic cap read “EonSmoke”, which led to a defunct website at the obvious URL. Apparently, EonSmoke went toes-up earlier this year after ten years of poisoning their customers, most likely due to “competitor litigation”.

    The black cap held what looks like a pressure switch:

    EonSmoke - switch
    EonSmoke – switch

    Suck on the icky end of the tube to activate the switch, pull air past the battery (?), pick up some toxic vapor around the heater, and carry it into your lungs:

    EonSmoke - reservoir heater
    EonSmoke – reservoir heater

    Maybe there’s a missing mouthpiece letting you suck on the icky end, activate the switch, pull vapor through the heater, and plate your lungs with toxic compounds. I admit certain aspects of my education have been sadly neglected.

    The lithium cell was down to 1.0 V, with no overdischarge protection and no provision for charging, so it’s a single-use item. I’m sure the instructions tell you to recycle the lithium cell according to local and state regulations, not toss it out the window of your car.

    I had to wash my hands so hard

  • Shuttles Game: Tapered Pegs

    Shuttles Game: Tapered Pegs

    As is all too common with 3D printed replacement parts done remotely, the first Shuttles game pegs didn’t quite fit into the game board’s holes. Fortunately, living in the future means rapid prototyping and quick turnaround:

    Shuttles Game pegs - tapered - solid model
    Shuttles Game pegs – tapered – solid model

    They’re slightly smaller, tapered toward the bottom, and take slightly less time to print.

    The OpenSCAD code in the GitHub Gist now has has the tweaks.

  • Seedling Shelter Frame

    Seedling Shelter Frame

    Plant seedlings started in pots require some hardening off time outdoors before being transplanted. Veggie seedlings also require protection from critters regarding them as a buffet, so Mary covers them with a sheet of floating row cover, which must be both suspended over the plants to give them growing room and tucked under the tray to keep the bugs out. She asked for a frame to simplify the process:

    Mesh Shelter Frame - assembled
    Mesh Shelter Frame – assembled

    The solid model shows the structure with no regard for proportion:

    Mesh Shelter Frame - show view
    Mesh Shelter Frame – show view

    The 5 mm fiberglass rods come from our decommissioned six-passenger umbrella, cut to length in the Tiny Lathe™ by applying a Swiss Pattern knife file around the perimeter, over the ShopVac’s snout to catch the glass dust. I started with a pull saw (also over the vacuum) during the weekly Squidwrench v-meeting, whereupon Amber recommended either a Dremel slitting wheel or a file, so I mashed everything together and it worked wonderfully well, without producing any errant glass-fiber shards to impale my fingers.

    The corners consist of three tubes stuck together at the origin:

    Mesh Shelter Frame - non-hulled corner model
    Mesh Shelter Frame – non-hulled corner model

    Shrink-wrapping them with a hull() adds plenty of strength where it’s needed:

    Mesh Shelter Frame - hulled corner model
    Mesh Shelter Frame – hulled corner model

    I decided putting the belly side (facing you in the picture) downward on the platform and the peak upward would distribute the distortion equally among the tubes and produce a nicely rounded outer surface for the mesh fabric:

    Mesh Shelter Frame - build layout
    Mesh Shelter Frame – build layout

    Which led to some Wikipedia trawling to disturb the silt over my long-buried analytic geometry, plus some calculator work to help recall the process; back in the day I would have used a slipstick, but I was unwilling to go there. Although I could special-case this particular layout, the general method uses Euler’s Rotation Theorem, simplified because I need only one rotation.

    Should you need concatenated rotations, you probably need quaternions, but, at this point, I don’t even remember forgetting quaternions.

    Anyhow, the Euler rotation axis is the cross product of the [1,1,1] vector aimed through the middle of the corner’s belly with the [0,0,-1] target vector pointing downward toward the platform. The rotation amount is the acos() of the dot product of those two vectors divided by the product of their norms. With vector and angle in hand, dropping them into OpenSCAD’s rotate() transformation does exactly what’s needed:

    rotate(acos((BaseVector*Nadir)/(norm(BaseVector)*norm(Nadir))),
           v=cross(BaseVector,Nadir))   // aim belly side downward
      Corner();

    Dang, I was so happy when that worked!

    Because the corner model rotates around the origin where all three tube centerlines meet, the result puts the belly below the platform, pointed downward. The next step applies a translation to haul the belly upward:

    translate([ArmOAL,0,    // raise base to just below platform level
               ArmOC/sqrt(3) + (ArmRadius/cos(180/SocketSides))*cos(atan(sqrt(3)/2)) + Finagle])

    This happens in a loop positioning the four corners for printing, so the first ArmOAL as the X axis parameter translates the shape far enough to let four of them coexist around the origin, as shown above.

    The mess in the Z axis parameter has three terms:

    • Raise the centerline of the ends of the tubes to Z=0
    • Raise the rim of the tube to Z=0
    • Add a wee bit to make the answer come out right

    The 0.18 mm Finagle constant fixes things having to do with the hull() applied to miscellaneous leftover angled-circles-as-polygons approximations and leaves just a skin below the platform to be sheared off by a huge cube below Z=0, matching the corner bellies with the bottoms of the feet.

    Because the corners have awful overhangs, the results look a bit raggedy:

    Mesh Shelter Frame - corner underside
    Mesh Shelter Frame – corner underside

    That’s after knocking off the high spots with a grubby sanding sponge and making a trial fit. They look somewhat less grotendous in person.

    If we need another iteration, I’ll think hard about eliminating the overhangs by splitting the corner parallel to the belly, flipping the belly upward, and joining the pieces with a screw. What we have seems serviceable, though.

    The OpenSCAD source code as a GitHub Gist:

    // Mesh Shelter Frame for outdoor sprouts
    // Ed Nisley KE4ZNU – July 2020
    /* [Layout Options] */
    Layout = "Show"; // [Build, Show, Corner, CornerSet, Base, BaseSet]
    //——-
    //- Extrusion parameters must match reality!
    // Print with 2 shells
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleFinagle = 0.2;
    HoleFudge = 1.00;
    function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle;
    Protrusion = 0.1; // make holes end cleanly
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    inch = 25.4;
    //——-
    // Dimensions
    RodOD = 5.0;
    SocketDepth = 3*RodOD;
    WallThick = 3.0;
    ArmOD = RodOD + 2*WallThick;
    ArmRadius = ArmOD / 2;
    SocketSides = 3*4;
    ArmOC = SocketDepth + ArmOD; // rod entry to corner centerline
    ArmOAL = ArmOC + ArmRadius; // total arm length to outer edge
    echo(str("ArmOC: ",ArmOC));
    echo(str("ArmOAL: ",ArmOAL));
    Nadir = [0,0,-1]; // vector toward print platform
    RodLength = 100; // just for show
    //——-
    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=HoleAdjust(FixDia)/2,h=Height,$fn=Sides);
    }
    //——-
    BaseVector = [1,1,1]; // vector through middle of base surface
    module Corner() {
    difference() {
    hull() {
    scale([1/cos(180/SocketSides),1/cos(180/SocketSides),1])
    rotate(180/SocketSides)
    sphere(d=ArmOD,$fn=SocketSides);
    rotate(180/SocketSides)
    cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
    rotate([-90,0,0]) rotate(180/SocketSides)
    cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
    rotate([0,90,0]) rotate(180/SocketSides)
    cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
    }
    rotate(180/SocketSides)
    translate([0,0,ArmOD])
    PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
    rotate([-90,0,0]) rotate(180/SocketSides)
    translate([0,0,ArmOD])
    PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
    rotate([0,90,0]) rotate(180/SocketSides)
    translate([0,0,ArmOD])
    PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
    }
    }
    module CornerSet(s=RodLength) {
    translate([-s/2,-s/2,s])
    mirror([0,0,1])
    Corner();
    translate([s/2,-s/2,s])
    rotate([0,0,90]) mirror([0,0,1])
    Corner();
    translate([s/2,s/2,s])
    rotate([0,0,180]) mirror([0,0,1])
    Corner();
    translate([-s/2,s/2,s])
    rotate([0,0,-90]) mirror([0,0,1])
    Corner();
    }
    module Base() {
    difference() {
    union() {
    cylinder(d=ArmOD,h=ArmOAL/2,$fn=SocketSides);
    resize([0,0,ArmOC/2])
    sphere(d=ArmOC,$fn=2*SocketSides);
    }
    translate([0,0,3*ThreadThick])
    PolyCyl(RodOD,ArmOAL,SocketSides);
    translate([0,0,-SocketDepth]) // cut sphere below platform
    cube(2*SocketDepth,center=true);
    }
    }
    module BaseSet(s=RodLength) {
    for (i=[-1,1], j=[-1,1])
    translate([i*s/2,j*s/2,0])
    Base();
    }
    //——-
    // Build it!
    if (Layout == "Corner")
    Corner();
    if (Layout == "CornerSet")
    CornerSet();
    if (Layout == "Base")
    Base();
    if (Layout == "BaseSet")
    BaseSet();
    if (Layout == "Show") {
    CornerSet();
    for (i=[-1,1])
    translate([i*RodLength/2,RodLength/2,RodLength])
    rotate([90,0,0])
    color("Green",0.5)
    cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
    for (j=[-1,1])
    translate([RodLength/2,j*RodLength/2,RodLength])
    rotate([0,-90,0])
    color("Green",0.5)
    cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
    BaseSet();
    for (i=[-1,1], j=[-1,1])
    translate([i*RodLength/2,j*RodLength/2,0])
    color("Green",0.5)
    cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
    }
    if (Layout == "Build") {
    Finagle = 0.18; // hack for hull's angled round-to-polygon approximations, I think
    difference() { // slice sliver from base to sit flat on platform
    union()
    for (a=[45:90:360])
    rotate(a) // distribute around origin
    translate([ArmOAL,0, // raise base to just below platform level
    ArmOC/sqrt(3) + (ArmRadius/cos(180/SocketSides))*cos(atan(sqrt(3)/2)) + Finagle])
    rotate(17) // arbitrary rotation for tidy arrangement
    rotate(acos((BaseVector*Nadir)/(norm(BaseVector)*norm(Nadir))),
    v=cross(BaseVector,Nadir)) // aim belly side downward
    Corner();
    translate([0,0,-ArmOD/2]) // slicing block below platform
    cube([6*ArmOAL,6*ArmOAL,ArmOD],center=true);
    }
    rotate(45)
    for (i=[-1,1], j=[-1,1])
    translate([i*1.5*ArmOC,j*1.5*ArmOC,0])
    Base();
    }

  • Reinforced QD Propane Adapter Tool

    Reinforced QD Propane Adapter Tool

    Having just emptied a propane tank while making bacon, I couldn’t find any of the wrench adapters I made to remove the QD adapter from the tank’s POL fitting. With memory of the broken garden valve wrench still fresh, I tweaked the solid model to include a trio of 1 mm music wire reinforcements:

    Propane QD Adapter Tool - reinforced - Slic3r
    Propane QD Adapter Tool – reinforced – Slic3r

    Holes that small require clearing with a 1 mm drill, after which ramming the wires in place poses no problem:

    Reinforced QD Adapter Tool - inserting wire
    Reinforced QD Adapter Tool – inserting wire

    Except for the one that got away:

    Reinforced QD Adapter Tool - errant wire
    Reinforced QD Adapter Tool – errant wire

    The music wire came from a coil and each snippet required gentle straightening; perhaps that one wasn’t sufficiently bar-straight.

    Anyhow, I printed two tools for that very reason:

    Reinforced QD Adapter Tool - side view
    Reinforced QD Adapter Tool – side view

    They’re now where I can’t miss ’em the next time I need them, although that’s not where the previous ones reside.

    The OpenSCAD source code as a GitHub Gist:

    // Propane tank QD connector adapter tool
    // Ed Nisley KE4ZNU November 2012
    // 2018-04-08 toss MCAD includes overboard
    // 2020-07-27 add reinforcing rods
    //- Extrusion parameters must match reality!
    // Print with about half a dozen perimeter threads and 50% infill
    ThreadThick = 0.25;
    ThreadWidth = 2.0 * ThreadThick;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    //———————-
    // Dimensions
    WrenchSize = (5/8) * inch; // across the flats
    WrenchThick = 10;
    NoseDia = 8.6;
    NoseLength = 9.0;
    LockDia = 12.5;
    LockRingLength = 1.0;
    LockTaperLength = 1.5;
    TriDia = 15.1;
    TriWide = 12.2; // from OD across center to triangle side
    TriOffset = TriWide – TriDia/2; // from center to triangle side
    TriLength = 9.8;
    NeckDia = TriDia;
    NeckLength = 4.0;
    RebarOD = 1.0; // music wire pin 1 mm = 39 mil
    RebarLength = WrenchThick + NeckLength + TriLength;
    //———————-
    // Useful routines
    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 it…
    $fn = 4*6;
    union() {
    translate([0,0,(WrenchThick + NeckLength + TriLength – LockTaperLength – LockRingLength + Protrusion)])
    cylinder(r1=NoseDia/2,r2=LockDia/2,h=LockTaperLength);
    translate([0,0,(WrenchThick + NeckLength + TriLength – LockRingLength)])
    cylinder(r=LockDia/2,h=LockRingLength);
    difference() {
    union() {
    translate([0,0,WrenchThick/2])
    cube([WrenchSize,WrenchSize,WrenchThick],center=true);
    cylinder(r=TriDia/2,h=(WrenchThick + NeckLength +TriLength));
    cylinder(r=NoseDia/2,h=(WrenchThick + NeckLength + TriLength + NoseLength));
    }
    for (a=[-1:1]) {
    rotate(a*120)
    translate([(TriOffset + WrenchSize/2),0,(WrenchThick + NeckLength + TriLength/2 + Protrusion/2)])
    cube([WrenchSize,WrenchSize,(TriLength + Protrusion)],center=true);
    }
    for (a=[-1:1]) {
    rotate(a*120 + 60)
    translate([NoseDia/2,0,-Protrusion])
    PolyCyl(RebarOD,RebarLength,6);
    }
    }
    }