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

  • ShopVac Hose Barb Adapter

    A small ShopVac arrived with a ribbed hose carrying an absurdly long wand, so I conjured a barbed adapter with a much shorter tapered snout for the machine tools:

    Vacuum hose fittings - hose barb to nozzle
    Vacuum hose fittings – hose barb to nozzle

    Trimming the hose end at one of the ribs makes a tidy fit:

    Vacuum hose fittings - ribbed hose barb
    Vacuum hose fittings – ribbed hose barb

    Now I need not trip over the vacuum hose between the bandsaw bench and the sander bench…

    The OpenSCAD code as a GitHub Gist:

    // Vacuum Hose Fittings
    // Ed Nisley KE4ZNU July 2016
    // March 2017
    Layout = "HoseBarb"; // PVCtoHose ExpandRing PipeToPort FVacPipe FVacFitting HoseBarb
    //- Extrusion parameters must match reality!
    // Print with 2 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    Protrusion = 0.1; // make holes end cleanly
    //———————-
    // Dimensions
    ID = 0;
    OD = 1;
    LENGTH = 2;
    VacNozzle = [30.1,31.8,30.0]; // nozzle on vacuum hose (taper ID to OD over length)
    MINOR = 0;
    MAJOR = 1;
    PITCH = 2;
    FORM_OD = 3;
    HoseThread = [32.0,(37.0 + HoleWindage),4.25,(1.8 + 0.20)]; // vacuum hose thread
    NumSegments = 64; // .. number of cylinder approximations per turn
    $fn = NumSegments;
    ThreadLength = 4 * HoseThread[PITCH];
    ScrewOAL = ThreadLength + HoseThread[PITCH];
    WallThick = 2.5;
    echo(str("Pitch dia: ",HoseThread[MAJOR]));
    echo(str("Root dia: ",HoseThread[MAJOR] – HoseThread[FORM_OD]));
    echo(str("Crest dia: ",HoseThread[MAJOR] + HoseThread[FORM_OD]));
    //———————-
    // Wrap cylindrical thread segments around larger plug cylinder
    module CylinderThread(Pitch,Length,PitchDia,ThreadOD,PerTurn,Chirality = "Left") {
    CylFudge = 1.02; // force overlap
    ThreadSides = 6;
    RotIncr = 1/PerTurn;
    PitchRad = PitchDia/2;
    Turns = Length/Pitch;
    NumCyls = Turns*PerTurn;
    ZStep = Pitch / PerTurn;
    HelixAngle = ((Chirality == "Left") ? -1 : 1) * atan(Pitch/(PI*PitchDia));
    CylLength = CylFudge * (PI*(PitchDia + ThreadOD) / PerTurn) / cos(HelixAngle);
    for (i = [0:NumCyls-1]) {
    Angle = ((Chirality == "Left") ? -1 : 1) * 360*i/PerTurn;
    translate([PitchRad*cos(Angle),PitchRad*sin(Angle),i*ZStep])
    rotate([90+HelixAngle,0,Angle]) rotate(180/ThreadSides)
    cylinder(r1=ThreadOD/2,
    r2=ThreadOD/(2*CylFudge),
    h=CylLength,
    center=true,$fn=ThreadSides);
    }
    }
    //– PVC fitting to vacuum hose
    module PVCtoHose() {
    Fitting = [34.0,41.0,16.0]; // 1 inch PVC elbow
    Adapter = [HoseThread[MAJOR],(Fitting[OD] + 2*WallThick + HoleWindage),(ScrewOAL + Fitting[LENGTH])]; // dimensions for entire fitting
    union() {
    difference() {
    cylinder(d=Adapter[OD],h=Adapter[LENGTH]); // overall fitting
    translate([0,0,-Protrusion]) // remove thread pitch dia
    cylinder(d=HoseThread[MAJOR],h=(ScrewOAL + 2*Protrusion));
    translate([0,0,(ScrewOAL – Protrusion)]) // remove PVC fitting dia
    cylinder(d=(Fitting[OD] + HoleWindage),h=(Fitting[LENGTH] + 2*Protrusion));
    }
    translate([0,0,HoseThread[PITCH]/2]) // add the thread form
    CylinderThread(HoseThread[PITCH],ThreadLength,HoseThread[MAJOR],HoseThread[FORM_OD],NumSegments,"Left");
    }
    }
    //– Expander ring from small OD to large ID PVC fittings
    // So a small elbow on the bandsaw fits into the hose adapter, which may not be long-term useful
    module ExpandRing() {
    Fitting_L = [34.0,41.0,16.0]; // 1 inch PVC pipe elbow
    Fitting_S = [26.8,32.8,17]; // 3/4 inch PVC elbow
    difference() {
    cylinder(d1=Fitting_L[OD],d2=(Fitting_L[OD] – HoleWindage),h=Fitting_L[LENGTH]); // overall fitting
    translate([0,0,-Protrusion])
    cylinder(d=(Fitting_S[OD] + HoleWindage),h=(Fitting_L[LENGTH] + 2*Protrusion));
    }
    }
    //– 1 inch PVC pipe into vacuum port
    // Stick this in the port, then plug a fitting onto the pipe section
    module PipeToPort() {
    Pipe = [26.5,33.5,20.0]; // 1 inch Schedule 40 PVC pipe
    difference() {
    union() {
    cylinder(d=Pipe[OD],h=(Pipe[LENGTH] + Protrusion));
    translate([0,0,(Pipe[LENGTH] – Protrusion)])
    cylinder(d1=VacNozzle[OD],d2=VacNozzle[ID],h=VacNozzle[LENGTH]);
    }
    translate([0,0,-Protrusion])
    cylinder(d=Pipe[ID],h=(Pipe[LENGTH] + VacNozzle[LENGTH] + 2*Protrusion));
    }
    }
    //– Female Vac outlet inside PVC pipe
    // Plug this into PVC fitting, then plug hose + nozzle into outlet
    module FVacPipe() {
    VacPort = [30.0,31.3,25]; // vacuum port on belt sander (taper ID to OD over length)
    Pipe = [26.5,33.5,20.0]; // 1 inch Schedule 40 PVC pipe
    difference() {
    cylinder(d=Pipe[OD],h=VacPort[LENGTH]);
    translate([0,0,-Protrusion])
    cylinder(d1=VacPort[ID],d2=VacPort[OD],h=(VacPort[LENGTH] + 2*Protrusion));
    }
    }
    //– Female Vac outlet on 3/4 inch fitting OD
    // Jam this onto OD of fitting, plug hose + nozzle into outlet
    module FVacFitting() {
    Adapter = [26.5,(33.5 + 2*WallThick),17.0]; // overall adapter
    //VacPort = [30.0,31.3,25]; // vacuum port on belt sander (taper ID to OD over length)
    VacPort = [30.1,31.8,30.0]; // vacuum port for bandsaw = inverse of hose nozzle
    Fitting = [26.8,32.8,17]; // 3/4 inch PVC elbow
    TaperLength = 5.0; // inner taper to avoid overhang
    difference() {
    cylinder(d=Adapter[OD],h=Adapter[LENGTH]); // overall fitting
    translate([0,0,-Protrusion])
    cylinder(d=(Fitting[OD] + HoleWindage),h=(Adapter[LENGTH] + 2*Protrusion));
    }
    translate([0,0,Adapter[LENGTH]])
    difference() {
    cylinder(d=Adapter[OD],h=TaperLength);
    translate([0,0,-Protrusion])
    cylinder(d1=(Fitting[OD] + HoleWindage),d2=VacPort[ID],h=(TaperLength + 2*Protrusion));
    }
    translate([0,0,(TaperLength + Adapter[LENGTH])]) // vac fitting
    difference() {
    cylinder(d=Adapter[OD],h=VacPort[LENGTH]);
    translate([0,0,-Protrusion])
    cylinder(d1=VacPort[ID],d2=VacPort[OD],h=(VacPort[LENGTH] + 2*Protrusion));
    }
    }
    //– Hose barb to male vacuum taper
    module HoseBarb() {
    HoseFitting = [29.0,32.2,38.5];
    Barb = [HoseFitting[OD],35.5,4.0];
    BarbOffset = 17.0;
    Seat = [HoseFitting[OD],36.0,5.0];
    SeatSupport = [HoseFitting[OD],Seat[OD],(Seat[OD] – HoseFitting[OD])/2];
    OAL = HoseFitting[LENGTH] + SeatSupport[LENGTH] + Seat[LENGTH] + VacNozzle[LENGTH];
    NumSides = 4*8;
    difference() {
    union() {
    cylinder(d=HoseFitting[OD],h=HoseFitting[LENGTH],$fn=NumSides);
    translate([0,0,BarbOffset])
    cylinder(d1=Barb[ID],d2=Barb[OD],h=Barb[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH]])
    cylinder(d1=SeatSupport[ID],d2=SeatSupport[OD],h=SeatSupport[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH] + SeatSupport[LENGTH]])
    cylinder(d=Seat[OD],h=Seat[LENGTH],$fn=NumSides);
    translate([0,0,HoseFitting[LENGTH] + SeatSupport[LENGTH] + Seat[LENGTH]])
    cylinder(d1=VacNozzle[OD],d2=VacNozzle[ID],h=VacNozzle[LENGTH],$fn=NumSides);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=HoseFitting[ID],d2=(VacNozzle[ID] – 10*ThreadWidth),h=OAL + 2*Protrusion,$fn=NumSides);
    }
    }
    //———-
    // Build things
    if (Layout == "PVCtoHose")
    PVCtoHose();
    if (Layout == "ExpandRing") {
    ExpandRing();
    }
    if (Layout == "PipeToPort") {
    PipeToPort();
    }
    if (Layout == "FVacPipe") {
    FVacPipe();
    }
    if (Layout == "FVacFitting") {
    FVacFitting();
    }
    if (Layout == "HoseBarb") {
    HoseBarb();
    }
  • Tour Easy Rear Fender Clip

    One of the clips holding the rear fender on my Tour Easy broke:

    Rear fender clip - broken
    Rear fender clip – broken

    Well, if the truth be told, the fender jammed against the tire when I jackknifed the trailer while backing into a parking spot, dragged counterclockwise with the tire, and wiped that little tab right off the block. After 16 years of service, it doesn’t owe me a thing.

    Although the clip around the fender sits a bit lower than it used to (actually, the entire fender sits a bit lower than it should be), you can see the tab had a distinct bend at the edge of the aluminum block supporting the underseat bag frame: the block isn’t perpendicular to the tire / fender at that point.

    After devoting far too long to thinking about how to angle the tab relative to the clip, I realized that I live in the future and can just angle the clip relative to the tab. Soooo, the solid model has a rakish tilt:

    Fender Clip - Slic3r preview
    Fender Clip – Slic3r preview

    The original design had a pair of strain relief struts where the tab meets the clip, but I figured I’ll add those after the PETG fractures.

    I mooched the small bumpouts along the arc from the original design; they provide a bit of stretch & bend so to ease the hooks around the fender.

    The hooks meet the clip with very slight discontinuities that, I think, come from slight differences between the 2D offset() operation and the circle() diameter; the usual 1/cos(180/numsides) trick was unavailing, so I tinkered until the answer came out right.

    Despite those stretchy bumps, it took three iterations, varying the chord height by about 1.5 mm, to securely snap those hooks onto the fender:

    Rear fender clip - 3D printed improvement
    Rear fender clip – 3D printed improvement

    Yeah, sorry ’bout the fuzzy focus on the screw head.

    It’s impossible to measure the chord height accurately enough in that position and I was not going to dismount the rear tire just to get a better measurement.

    You can see how the clip’s rakish tilt matches the fender’s slope, so the tab isn’t bent at all. It’ll probably break at the block the next time I jackknife the trailer, of course.

    I heroically resisted the urge to run off a lower fender mount.

    The OpenSCAD source code as a GitHub Gist:

    // Tour Easy rear fender clip
    // Ed Nisley KE4ZNU February 2017
    Layout = "Build"; // Build Profile Tab Clip
    //- Extrusion parameters must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    //———————-
    // Dimensions
    // special case: fender is exactly half a circle!
    FenderC = 47.0; // fender outside width = chord
    FenderM = 18.5; // height of chord
    FenderR = (pow(FenderM,2) + pow(FenderC,2)/4) / (2 * FenderM); // radius
    echo(str("Fender radius: ", FenderR));
    FenderD = 2*FenderR;
    FenderA = 2 * asin(FenderC / (2*FenderR));
    echo(str(" … arc: ",FenderA," deg"));
    FenderThick = 2.5; // fender thickness, assume dia of edge
    ClipHeight = 18.0; // top to bottom, ignoring rakish tilt
    ClipThick = 3.0; // thickness of clip around fender
    ClipD = FenderD; // ID of clip against
    ClipSides = 4 * 8; // polygon sides around clip circle
    BendReliefD = 2.5; // bend arch diameter
    BendReliefA = 2/3 * FenderA/2; // … angle from dead ahead
    BendReliefCut = 1.0; // factor to thin outside of bend
    TabAngle = -20; // angle from perpendicular to fender
    TabThick = 2.0;
    TabWidth = 15.0;
    ScrewOffset = 15.0; // screw center to fender along perpendicular
    ScrewD = 5.0;
    ScrewSlotLength = 2*ScrewD;
    //———————-
    // 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);
    }
    //———————-
    // Clip profile around fender
    // Centered on fender arc
    module Profile(HeightScale = 1) {
    linear_extrude(height=HeightScale*ClipHeight,convexity=5) {
    difference() {
    offset(r=ClipThick) // outside of clip
    union() {
    circle(d=ClipD,$fn=ClipSides);
    for (i=[-1,1])
    rotate(i*BendReliefA) {
    translate([ClipD/2 + BendReliefD/2,0,0])
    circle(d=BendReliefD,$fn=6);
    }
    }
    union() { // inside of clip
    circle(d=ClipD,$fn=ClipSides);
    for (i=[-1,1])
    rotate(i*BendReliefA) {
    translate([ClipD/2 + BendReliefCut*BendReliefD/2,0,0])
    circle(d=BendReliefD/cos(180/6),$fn=6);
    translate([ClipD/2,0,0])
    square([BendReliefCut*BendReliefD,BendReliefD],center=true);
    }
    }
    translate([(FenderR – FenderM – FenderD/2),0]) // trim ends
    square([FenderD,2*FenderD],center=true);
    }
    for (a=[-1,1]) // hooks around fender
    rotate(a*(FenderA/2))
    translate([FenderR – FenderThick/2,0]) {
    difference() {
    rotate(1*180/12)
    circle(d=FenderThick + 2*ClipThick,$fn=12);
    rotate(1*180/8)
    circle(d=FenderThick,$fn=8);
    rotate(a * -90)
    translate([0,-2*FenderThick,0])
    square(4*FenderThick,center=false);
    }
    }
    }
    }
    //———————-
    // Mounting tab
    module Tab() {
    linear_extrude(height=TabThick,convexity=3)
    difference() {
    hull() {
    circle(d=TabWidth,$fn=ClipSides);
    translate([(ScrewSlotLength – ScrewD)/2 + (FenderR + ScrewOffset),0,0])
    circle(d=TabWidth,$fn=ClipSides);
    }
    circle(d=ClipD,$fn=ClipSides); // remove fender arc
    hull() // screw slot
    for (i=[-1,1])
    translate([i*(ScrewSlotLength – ScrewD)/2 + (FenderR + ScrewOffset),0,0])
    rotate(180/8)
    circle(d=ScrewD/cos(180/8),$fn=8);
    }
    }
    //———————-
    // Combine at mounting angle
    module Clip() {
    difference() {
    union() {
    translate([-FenderR,0,0])
    Tab();
    rotate([0,TabAngle,0])
    translate([-FenderR,0,0])
    Profile(2); // scale upward for trimming
    }
    translate([0,0,-ClipHeight]) // trim bottom
    cube(2*[FenderD,FenderD,ClipHeight],center=true);
    translate([0,0,ClipHeight*cos(TabAngle)+ClipHeight]) // trim top
    cube(2*[FenderD,FenderD,ClipHeight],center=true);
    }
    }
    //———————-
    // Build it
    if (Layout == "Profile") {
    Profile();
    }
    if (Layout == "Tab") {
    Tab();
    }
    if (Layout == "Clip") {
    Clip();
    }
    if (Layout == "Build") {
    Clip();
    }

    The original doodle, with some measurements unable to withstand the test of time:

    Rear Fender Clip - measurement doodles
    Rear Fender Clip – measurement doodles
  • Epoxy Mixing Pads

    Quilters hold fabric in place with freezer paper while piecing their blocks; it’s basically plastic-coated paper that gets tacky at ordinary clothes iron temperatures.

    It’s useful in the shop, too. Cut a length of freezer paper into small pages, pad them plastic-side-up atop a sheet of cardboard, and you get a great place to mix small amounts of epoxy:

    Epoxy mixing pad
    Epoxy mixing pad

    Let the pad stay next to whatever you’re epoxying (like, say, the lathe tailstock ways), then test the leftover epoxy for hardness… rather than messing up the joint you so laboriously created by moving the parts an hour too soon.

    Works for me, anyhow. Highly recommended!

  • Mini-Lathe Carriage Stop: Spring Counterbore

    While pondering the tailstock ways, I realized the spring on the LMS Adjustable Carriage Stop just needed a counterbore to make it work right:

    LMS Carriage Stop - spring counterbore
    LMS Carriage Stop – spring counterbore

    The OEM spring now sits slightly compressed with the screw tip flush at the far end of the block:

    LMS Carriage Stop - reassembled
    LMS Carriage Stop – reassembled

    That OEM screw head knurling leaves a bit to be desired, doesn’t it?

    Actually boring the hole would be a remarkably tedious process for little gain. Instead, I lined up the block in the drill press using a ¼ inch drill (the OEM hole isn’t hard metric!) in the unthreaded section, enlarged it with progressively larger drills up to an O (0.316 inch = 8 mm), then finished with a P (0.323 in = 8.2 mm).

    As it turned out, my guesstimated relaxed spring length was a bit off, so I turned a brass bushing to shorten the hole by 2 mm:

    LMS Carriage Stop - screw bushing
    LMS Carriage Stop – screw bushing

    If I don’t mention it, nobody will ever know!

    The original doodle, with close-enough sizes:

    LMS Carriage Stop - spring counterbore doodle
    LMS Carriage Stop – spring counterbore doodle
  • Mini-Lathe Tailstock Way Repair

    After the faceplant caused by the crappy compound way finishing, I decided to try repairing the tailstock ways as a means of gaining experience before tackling the real problem. The general idea is to see whether filling the gouges with epoxy will suffice.

    I’m using good ol’ JB Weld steel-filled epoxy, rather than graphite / molybdenum disulfide loaded epoxy, mostly because:

    • I have it on the shelf
    • This is a non-sliding joint
    • My technique needs polishing, too

    The key point: the tailstock is (astonishingly) well aligned and, if I can manage to not change how it sits on the lathe bed, this should be a zero-impact operation. Scraping / filing / fiddling with the high spots will change the alignment; I expect I must eventually do such things; this represents a first pass at the problem.

    Applying a fat blue Sharpie to the tailstock ways:

    Tailstock way repair - blue Sharpie
    Tailstock way repair – blue Sharpie

    After sliding the tailstock back and forth a few times, the remaining blue shows where the ways did not make contact. Those shiny and silvery spots rubbed against the lathe bed ways.

    The flat way looked like this:

    Tailstock way repair - flat contacts
    Tailstock way repair – flat contacts

    The patch along the upper-left edge and the small dot near the upper-right corner are the only contact points across the entire flat.

    The outside of the V groove:

    Tailstock way repair - outer V contacts
    Tailstock way repair – outer V contacts

    As nearly as I can tell, that’s actually a reasonably flat and well-aligned surface, with small contact points scattered all over. Granted, there’s a larger contact patch to the left and less to the right.

    The inside of the V groove:

    Tailstock way repair - inner V contacts
    Tailstock way repair – inner V contacts

    There’s a single point near the top left, another over on the right, and that’s about it.

    I cleaned the tailstock ways with acetone to get rid of the Sharpie / grease / oil / whatever. Under normal circumstances you’d roughen the surface to give the epoxy something to grip, which definitely seemed akin to perfuming a lily.

    To prevent permanently affixing the tailstock to the lathe, some folks put a generous layer of oil / graphite / soot / release agent on the lathe bed ways. I used some 3 mil = 0.08 mm Kapton tape, figuring an impervious layer would pretty much guarantee I could get the tailstock off again, no matter what.

    So, we begin.

    Butter up the tailstock ways with epoxy and smoosh into place atop the Kapton:

    Tailstock way repair - V groove on tape
    Tailstock way repair – V groove on tape

    Make sure the tailstock remains well-seated where it should be:

    Tailstock way repair - weights
    Tailstock way repair – weights

    Do other things for 24 hours while the epoxy cures, pry the tailstock loose by hammering The Giant Prying Screwdriver between the lathe bed and the underside of the tailstock (just right of the V-groove, where nothing slides on the bed, but I did use a bit of plastic as a shield), chip off excess epoxy, clean things up, etc, etc.

    This time, I applied Sharpie to the lathe bed, then slid the tailstock back & forth a few times. As a result, the blue areas now show the contact patches and the gray areas just slid by without touching.

    The flat way looks pretty good:

    Tailstock way repair - flat epoxy blued
    Tailstock way repair – flat epoxy blued

    That round dot over on the right seems to be a steel protrusion; I think it’s part of the same lump appearing in the “before” picture above. That rather sharp point seems to have indented the tape and produced a low area in the epoxy around it, which may not matter much: it was the only contact point before I did this.

    The V groove isn’t anywhere near perfect:

    Tailstock way repair - V groove epoxy blued
    Tailstock way repair – V groove epoxy blued

    On the upside, the ways have much, much larger contact patches spread across nearly their entire lengths, which isn’t to be sniffed at.

    While reassembling the tailstock, I added a pair of M6 washers above the clamp plate so it cleared the bed with the screw tightened into the cam-lock post:

    Tailstock clamp plate - washers
    Tailstock clamp plate – washers

    Which definitely calls for a small bushing, of course. If you put a lockwasher under the screw head, it won’t clear the end of the bed casting. So it goes.

    Another washer under the ram lock screw changed the phase enough to keep the knob out of the way in both the fully locked and unlocked positions:

    Tailstock ram lock - added washer
    Tailstock ram lock – added washer

    I slobbered some Mobil Vactra #2 Sticky Way Oil (thanks, Eks!) on the bed ways, snuggled the tailstock in place, and wow does that thing move! Verily, it slides smoothly and clamps solidly in place: a tremendous improvement over the status quo ante.

    Some observations…

    • The tape (perhaps the adhesive layer) produces a slightly textured epoxy surface
    • The tailstock way’s small contact points indented the tape, even though it’s only 3 mil thick
    • Filling the low areas in the way works well
    • The high areas may not have enough epoxy for good durability
    • I expect the epoxy will wear faster than steel, so contact should improve with time
    • This is not a permanent fix

    What I’ll do differently next time…

    • Apply more epoxy to avoid those small gaps along the edges
    • Use a real release agent: smoothed in place, it might provide a better finish. Might not matter
    • Verify a good prying spot before epoxying, say, the compound

    All in all, though, this worked much better than I expected!

  • Raspberry Pi 3: Disabling the Build-In WiFi

    streaming media player in the Basement Laboratory Warehouse Wing has a concrete block wall between it and the WiFi router, so that even high(er)-gain USB antennas can’t grab enough signal for reliable streaming. After some fiddling, I snaked a cable from a hub, along the floor joints, to the Pi and declared victory. It turned out the Pi, an old Pi 1 Model B, had some trouble keeping up with the times, and I eventually swapped it for a Pi 3.

    Forcing a static address for the wired port followed the now-standard recipe, with eth0 instead of wlan0 in /etc/dhcpcd.conf.

    However, plugging a network cable into the Pi 3 then produces two network connections: the wired one I wanted and the aforementioned unreliable WiFi link through the built-in hardware. The only reliable way to turn off the WiFi connection seems to require applying The BFH through a line in /etc/rc.local:

    sudo ifconfig wlan0 down
    

    Removing my WiFi credentials from /etc/wpa_supplicant/wpa_supplicant.conf prevents the hardware from connecting before the hammer comes down.

    And then it streams perfectly…

  • Cheap WS2812 LEDs: Test Fixture Mount

    Mounting the ungainly WS2812 LED test fixture seemed like a Good Idea to keep the electricity out of the usual conductive litter:

    WS2812 array test fixture - rear
    WS2812 array test fixture – rear

    The solid model shows more details:

    LED Test Fixture - solid model
    LED Test Fixture – solid model

    The power wires along the array edges slide into the rear (thinner) slot, with enough friction from a few gentle bends to hold the whole mess in place.

    The knockoff Arduino Nano rests on the recessed ledge in the pit, with M2 screws and washers at the corners holding it down (the PCB’s built-in holes might work with 1 mm or 0-90 screws, but that’s just crazy talk). I soldered the power wires directly to the coaxial jack pins under the PCB; they snake out to the LEDs through the little trench. There should be another cutout around the USB connector for in-situ programming, although the existing code works fine.

    The front (wider) slot holds a piece of translucent white acrylic to diffuse the light:

    WS2812 array test fixture - front flash
    WS2812 array test fixture – front flash

    It’s painfully bright: a few layers of neutral density filter would be appropriate for a desk toy.

    The array runs hot enough at MaxPWM = 255 to produce a gentle upward breeze.

    It looks even better without the flash:

    WS2812 array test fixture - front dark
    WS2812 array test fixture – front dark

    You’ll find many easier ways to get RGB LED panels, but that’s not the point here; I’m waiting for these things to die an unnatural death.

    The OpenSCAD source code as a GitHub Gist:

    // LED Test Fixture
    // Ed Nisley KE4ZNU – February 2017
    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
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [2.8,3.5,4.0]; // M2 threaded insert
    ScrewOD = 2.0;
    WasherOD = 5.0;
    //- Component sizes
    PCBSize = [18.0,43.5,1.6]; // microcontroller PCB
    PCBClear = 2*[ThreadWidth,ThreadWidth,0]; // clearance around board
    PCBShelf = [ThreadWidth,ThreadWidth,0]; // shelf under perimeter
    PCBCavity = PCBSize – PCBShelf + [0,0,2.5]; // support shelf around bottom parts
    LEDPanel = [70,40,4.0]; // lying flat, LEDs upward
    LEDWire = [LEDPanel[0],LEDPanel[1] + 2*5.0,2.0]; // power wires along sides
    Diffuser = [LEDPanel[0],LEDPanel[1] + 2*4.0,3.5];
    echo(str("Diffuser panel: ",Diffuser));
    WallThick = 8.0;
    BaseThick = 3*ThreadThick + Insert[LENGTH] + PCBCavity[2];
    Block = [3*WallThick + PCBSize[0] + LEDPanel[2] + Diffuser[2],
    2*WallThick + IntegerMultiple(max(PCBSize[1],LEDWire[1]),5),
    BaseThick + LEDPanel[0]];
    echo(str("Block: ",Block));
    CornerRadius = 5.0;
    NumSides = 4*5;
    //- 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 it
    difference() {
    hull() // main block with rounded corners
    for (i=[-1,1], j=[-1,1])
    translate([i*(Block[0]/2 – CornerRadius),j*(Block[1]/2 – CornerRadius),,0])
    cylinder(r=CornerRadius,h=Block[2],$fn=NumSides);
    translate([2*WallThick + PCBSize[0] – Block[0],
    0,
    (Block[2]/2 + BaseThick)])
    cube(Block + [0,2*Protrusion,0],center=true); // cut out over PCB
    translate([WallThick + (PCBSize + PCBClear)[0]/2 – Block[0]/2,
    0,
    0]) {
    translate([0,0,(BaseThick + (Protrusion – PCBSize[2])/2)])
    cube(PCBSize + PCBClear + [0,0,Protrusion],center=true); // PCB recess
    translate([0,0,(BaseThick + (Protrusion – PCBCavity[2])/2)])
    cube(PCBCavity + [0,0,Protrusion],center=true); // cavity under PCB
    translate([PCBSize[0]/2 + WallThick/2 – Protrusion/2,PCBSize[1]/2 – 15/2,BaseThick – PCBCavity[2]/2 + Protrusion/2])
    cube([WallThick + PCBShelf[0] + Protrusion,
    15,PCBCavity[2] + Protrusion],center=true); // wiring cutout
    for (i=[-1,1], j=[-1,1]) // screw inserts
    translate([i*(PCBSize[0] + ScrewOD)/2,j*(PCBSize[1] + ScrewOD)/2,-Protrusion])
    rotate(180/(2*6))
    PolyCyl(Insert[OD],BaseThick + 2*Protrusion,6);
    }
    resize([2*Block[0],0,LEDPanel[0] + Protrusion]) // LED panel outline
    translate([0,0,BaseThick])
    rotate([0,-90,0])
    translate([(LEDPanel[0] + Protrusion)/2,0,0])
    cube(LEDPanel + [Protrusion,0,0],center=true);
    translate([-Block[0]/2 + 2*WallThick + PCBSize[0] + LEDWire[2]/2 + 5*ThreadWidth,
    0,BaseThick]) // LED wiring recess
    rotate([0,-90,0])
    translate([(LEDWire[0] + Protrusion)/2,0,0])
    cube(LEDWire + [Protrusion,0,0],center=true);
    translate([Block[0]/2 – Diffuser[2]/2 – 5*ThreadWidth,0,BaseThick]) // diffuser
    rotate([0,-90,0])
    translate([(Diffuser[0] + Protrusion)/2,0,0])
    cube(Diffuser + [Protrusion,0,0],center=true);
    }