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.

Month: March 2017

  • 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();
    }
  • Streaming Radio Advertisements: Carpet Bombing

    After a protracted silence in a Radionomy stream, the Raspberry Pi player offered this log:

    2017-03-05 11:17:07,890 INFO: Starting mplayer on Plenitude -> /home/pi/Playlists/Radio-PLENITUDE.m3u
    2017-03-05 11:17:13,651 INFO: Track name: []
    2017-03-05 11:44:02,296 INFO: Track name: [David Wahler - Whispers from Eternity]
    2017-03-05 11:46:36,995 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 11:47:07,117 INFO: Track name: []
    2017-03-05 11:49:07,080 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 11:49:10,079 INFO: Track name: [Jef Mounet & Danièle Mounet - L'ancre musicale Natures d'Eau]
    2017-03-05 12:02:02,271 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:02:32,424 INFO: Track name: []
    2017-03-05 12:04:32,243 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:05:01,925 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:07:02,276 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:07:31,968 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:09:32,262 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:10:02,192 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:12:02,311 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:12:32,184 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:14:32,085 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:15:02,217 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:17:02,057 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:17:32,445 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:19:32,083 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:19:35,171 INFO: Track name: [Jean-Marc Staehle - Bercé par tant de beauté]
    2017-03-05 12:23:42,410 INFO: Track name: [Francesco - Sur le chemin]
    2017-03-05 12:29:50,265 INFO: Track name: [Michel Pépé - Pacifica]
    2017-03-05 12:35:07,493 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:35:37,377 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:37:37,478 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:37:41,476 INFO: Track name: [Music And Wellness (Musique Et Bien Etre) - Absolute Winner]
    2017-03-05 12:46:36,742 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 12:47:06,668 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 12:49:06,538 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 12:49:10,270 INFO: Track name: [Patrick Vuillaume &Nicole Bally - Pearls of Light (Instrumental by Nicole Bally)]
    2017-03-05 12:53:45,357 INFO: Track name: [Trine Opsahl - Sister moon]
    2017-03-05 12:54:58,596 INFO: Track name: [Peter Kater - Rebirth]
    2017-03-05 13:04:52,726 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:05:22,665 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 13:07:21,561 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:07:25,808 INFO: Track name: [Deuter - Flowing]
    2017-03-05 13:12:55,970 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:13:25,859 INFO: Track name: []
    2017-03-05 13:15:26,449 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:15:33,022 INFO: Track name: [Radio PLENITUDE - Jingle Intro Publicité]
    2017-03-05 13:15:59,437 INFO: Track name: [Targetspot - TargetSpot]
    2017-03-05 13:17:59,559 INFO: Track name: [Radio PLENITUDE - Jingle Extro Publicité]
    2017-03-05 13:18:06,133 INFO: Track name: [O - Part I]
    

    The Jingle lines introduce a short interlude of chimes separating music from advertisements. The Intro chimes play for 30 seconds and the Extro chimes play for three to five seconds. Some stations have similar interludes, others do not; apparently the station gets to choose the format.

    The [Targetspot - TargetSpot] lines mark two minutes of TargetSpot insertion: either advertisements (if you’re in their target market) or generic musical interludes similar to the station’s genre (if you’re out-of-market). The ads and music often lack volume-matching with the streaming music, rarely have lower volume, and the ads are incomprehensible to my ears. The musical interludes seem to be randomly chosen from a small set of candidate tracks that, along with the chimes, become annoyingly familiar in short order.

    The [] lines (yes, an empty string) mark two minutes of Public Service Announcements, advertisements, or generic musical interludes. I’m uncertain how they differ from the [Targetspot - TargetSpot] insertions.

    At a minimum, Radionomy inserts two minutes of TargetSpot / PSAs after every 12 to 15 minutes of music. Adding in the Jingle markers, ads occupy just under 20% of the total “airtime” for this station.

    However, bizarre events like the 17 nonstop minutes of jingles and ads inserted just after noon occur with inexplicable frequency. I’ve noticed half an hour of similar back-to-back-to-back ads on other stations, so it’s not a rare event.

    To quote the TargetSpot website:

    TargetSpot serves ads in real time to each listener’s personalized stream, creating a one-to-one relationship between the advertiser and the listener. The result is a dramatic increase in message relevancy and campaign effectiveness

    Those keyword markers turn out to be incredibly convenient. Just sayin’…

  • SMA Attenuators vs. Broadcast FM vs. NooElec SDR

    Four SMA attenuators arrived from halfway around the planet:

    SMA Attenuators
    SMA Attenuators

    The top line has ATTENUATOR wrapped around the body. They’re rated for 2 W = +33 dBm, suitable for antennas and SDR and suchlike, not real radios or even HTs.

    That assortment provides 39 dB of attenuation in 3 dB steps:

    • 3 6 9
    • 10 13 16 19
    • 20 23 26 29
    • 30 33 36 39

    Sweeping them on the spectrum analyzer shows they’re doing what they claim, to within the resolution of the analyzer, and remain flat through 1.5 GHz, where my cheap N-to-SMA adapter cables roll off by 3 dB. Stacking them produces 38 dB of attenuation, which is certainly the small difference of large values and fine for my simple needs.

    Conversely, a quick test with a NooElec SDR shows plenty of hocus-pocus betwixt antenna and display: the RF doesn’t attenuate nearly the way you’d (well, I’d) expect.

    Direct from the antenna, with AGC off and 50 dB of RF gain:

    WPDH Spectrum - 0 dB atten
    WPDH Spectrum – 0 dB atten

    3 dB attenuator:

    WPDH Spectrum - 3 dB atten
    WPDH Spectrum – 3 dB atten

    6 dB attenuator:

    WPDH Spectrum - 6 dB atten
    WPDH Spectrum – 6 dB atten

    10 dB attenuator:

    WPDH Spectrum - 10 dB atten
    WPDH Spectrum – 10 dB atten

    20 dB attenuator:

    WPDH Spectrum - 20 dB atten
    WPDH Spectrum – 20 dB atten

    Ain’t nothin’ simple…

  • 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
  • Cheap WS2812 LEDs: Test Fixture Failure 1

    Well, that didn’t take long:

    WS2812 array - failure 1
    WS2812 array – failure 1

    The red spot in the next-to-bottom row of the test fixture (*) marks a failed WS2812 LED. All of the LEDs above it, plus the LED just to its left, are in pinball panic mode: random colors flicker across the panel as the LED’s controller transmits garbled data and the downstream LEDs pass it on.

    This failure provides several bits of information:

    • The LED sees the same power supply as all the rest, so it’s not a power thing
    • The LED gets data from the adjacent WS2812, so it’s not an Arduino output thing
    • It failed after about four days = 100 hours of continuous operation

    I connected the previous LED’s output (#6) to the next one’s input (#8), so the failed LED (#7, now with output disconnected) continues to flicker, but doesn’t influence any of the downstream LEDs.

    (*) The LEDs are daisy-chained from lower right to upper left, row by row, so that’s LED #7 of 28.

  • 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!

  • Squirrel vs. Bird Feeder

    After months of attempts and (occasionally) spectacular failures, one of the backyard squirrels managed to climb aboard the bird feeder:

    Squirrel on bird feeder
    Squirrel on bird feeder

    The shutter closes when more than two cardinals and a titmouse perch on the wood bar, so the squirrel didn’t get anything. However, back in 2008, one of that critter’s ancestors mastered the trick:

    Not a Squirrel-Proof Feeder
    Not a Squirrel-Proof Feeder

    Since then, I’ve raised the feeder about five feet and inverted a big pot over two feet of loose PVC pipe around the pole.

    Given the number of squirrel-training videos on Youtube, however, it’s only a matter of time until the critters put all the tricks together!