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: Recumbent Bicycling

Cruisin’ the streets

  • Garage Door Openers: Pity the Color Blind

    The small garage door opener I tote around in the Tour Easy’s underseat bag failed after many years of exposure to the elements, so I paid a few bucks more for a cheap replacement in order to get fast delivery from a (US!) eBay supplier:

    Garage door opener remote controls
    Garage door opener remote controls

    For whatever it’s worth, before buying the replacement I tried:

    • Cleaning the battery contacts
    • Installing a new CR2032 battery
    • Programming the hitherto-unused buttons to open the door

    The remote control would occasionally work, but none of the “repairs” made much difference; I suspect corrosion hidden under the components or cracked solder joints.

    The eBay item description clearly, if inarticulately, specifies the compatibility requirement:

    key chain remote control
    compatible for purple learn button

    So I trotted out to the garage and inspected the button:

    Sears Garage Door Opener - purple button
    Sears Garage Door Opener – purple button

    Looks purple to me, but, being that type of guy, I also read the adjacent instruction sticker:

    Sears Garage Door Opener - instructions
    Sears Garage Door Opener – instructions

    Nobody, nobody, maintains the documentation. [sigh]

    I figured if they went to all the trouble of ordering a bazillion switches with purple caps, then the PCB surely holds the corresponding RF filters & firmware & whatever else that button signifies.

    Seeing as how we have exactly one garage door opener and no lights or other doodads, I told the opener to obey both the 1 and 2 buttons, thereby dramatically reducing the dexterity required to open the door while pedaling up the driveway. The opener can remember an unspecified number of transmitters, so I didn’t go for all four buttons.

  • Eneloop AAA Cells: First Charge

    With an AAA-to-AA adapter in hand, the Eneloop AAA cells looked like this:

    Eneloop AAA - as received - Ah scale - 2017-04-20
    Eneloop AAA – as received – Ah scale – 2017-04-20

    The glitch comes from a not-quite-seated cell, showing that a poor connection matters.

    The package touts “up to 800 mA·h, 750 mA·h min”, with asterisks and superscripts leading to “Based on IEC 61951-2(7.3.2)“, access to which requires coughing up 281 bucks. So it goes.

    A full charge made them happier:

    Eneloop AAA - first charge - Ah scale - 2017-04-22
    Eneloop AAA – first charge – Ah scale – 2017-04-22

    The as-delivered 530 mA·h capacity represents 73% of the 725 mA·h after the first charge, so I suppose they’re more-or-less within the “Maintains up to 70% charge after 10 years of storage” claim. The 16-10 date code suggests they’re hot off the factory charger, so they must ship with somewhat less than a full charge.

    Comparing the capacity in W·h makes more sense, because most devices (other than the Planet Bike blinky light these will go into, of course) use a boost converter to get a fixed voltage from the declining terminal voltage.

    They arrived bearing just over 600 mW·h:

    Eneloop AAA - as received - Wh scale - 2017-04-20
    Eneloop AAA – as received – Wh scale – 2017-04-20

    After charging, that went a bit over 850 mW·h :

    Eneloop AAA - first charge - Wh scale - 2017-04-22
    Eneloop AAA – first charge – Wh scale – 2017-04-22

    Call it 71% of full capacity on arrival. Close enough.

    The Planet Bike blinky will be somewhat dimmer with two NiMH cells delivering 2.3-ish V, compared with the initial 3-ish V from a pair of alkaline cells. I generally burn the alkalines down to 1.1 V apiece, so perhaps they’ll be Good Enough.

    Now, if I were gutsy, I’d install a rechargeable lithium AAA cell, with a dummy pass-through adapter in the other cell socket, and run the blinky at 3.7 V. At least for a few moments, anyhow …

  • Tour Easy Front Fender Clip

    We rode the Feeder Canal trail during a recent bike vacation in exotic Glens Falls NY:

    Feeder Canal Park Trail - Branches
    Feeder Canal Park Trail – Branches

    The numerous downed branches along the trail and countless twigs on the trail came from a brush-clearing operation:

    Feeder Canal Park Trail - Brush Clearing
    Feeder Canal Park Trail – Brush Clearing

    As luck would have it, a twig snagged between my front tire and fender, snapping the clips holding the fender in place:

     

    Tour Easy front fender mount breakage
    Tour Easy front fender mount breakage

    Should it not be obvious, each ferrule formerly had two parallel jaws (on the left) gripping the fender, with the tiny screw digging into the fender. I affixed the fender to the broken clips with copious amounts of duct tape and we continued the mission.

    It should be obvious why those ferrules are not suitable for 3D printing.

    However, with the recent rear fender clip serving as inspiration, this didn’t take long:

    Tour Easy - Front Fender Clip - Slic3r
    Tour Easy – Front Fender Clip – Slic3r

    The front fender fits a 20 inch wheel and is somewhat wider and flatter than the rear fender (I think they bent the same plastic strip around a smaller mandrel), so I did a quick copy-and-paste hack job on the OpenSCAD source code, rather than trying to parameterize the daylights out of the previous model.

    The posts around the wire stays are 6 diameters deep and reamed to fit; the stays won’t be flopping around even without fiddly mechanical hardware retaining them. The holes extend about halfway into those posts to mimic the dimensions of the original ferrules.

    All of us can predict where the next break will occur, right? That’s OK: I want this to break, instead of wrecking the fender, so the only question is how much abuse those simple joints can withstand. The printing orientation wraps the perimeter threads from the posts around the clip, making it about a strong as it can be.

    The ferrules should splay outward by a few degrees to match the angle from the fender to the fork eyelets, but that’s in the nature of fine tuning.

    The arch accommodates a strip of double-sided foam tape holding the clip in place along the fender curve, with those cute little hooks capturing the fender to keep the tape in compression:

    Tour Easy Front Fender Clip - installed
    Tour Easy Front Fender Clip – installed

    I really must get some black foam tape …

    The picture shows the fender sitting well away from the tire, due to the upper fender mount bending in response to the splash flap snagging on curbs and random debris; the wire stays didn’t seat completely into the posts.

    The extender I made during the cracked fork episode remained perfectly straight, though:

    Tour Easy - new fork - fender extender
    Tour Easy – new fork – fender extender

    So I re-bent the upper fender mount (not the extender!) to its original angle, thereby moving the bottom of the fender much closer to the tire. Now the stays seat fully, the clip holds the fender firmly in place with no rattles, and it’s all good.

    The OpenSCAD source code as a GitHub Gist:

    // Tour Easy front fender clip
    // Ed Nisley KE4ZNU April 2017
    Layout = "Clip"; // Build Profile Ferrule 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 = 51.0; // fender outside width = chord
    FenderM = 21.0; // 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 = 15.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
    ID = 0;
    OD = 1;
    LENGTH = 2;
    StayDia = 3.3; // fender stay rod diameter
    StayOffset = 23.0; // stay-to-fender distance
    StayAngle = -5; // angle from stay to fender
    FerruleSides = 2*4;
    Ferrule = [StayDia,3*FenderThick/cos(180/FerruleSides),6*StayDia + StayOffset]; // ID = stay rod OD
    //———————-
    // 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);
    }
    }
    }
    }
    //———————-
    // Ferrule body
    module FerruleBody() {
    translate([0,0,Ferrule[OD]/2 * cos(180/FerruleSides)])
    rotate([0,-90,0]) rotate(180/FerruleSides)
    difference() {
    cylinder(d=Ferrule[OD],h=Ferrule[LENGTH],$fn=FerruleSides,center=false);
    translate([0,0,StayOffset + Protrusion])
    PolyCyl(Ferrule[ID],Ferrule[LENGTH] – StayOffset + Protrusion,FerruleSides);
    }
    }
    //———————-
    // Generate entire clip at mounting angle
    module FenderClip() {
    union() {
    translate([FenderR,0,0])
    difference() { // angle and trim clip
    rotate([0,StayAngle,0])
    translate([-(FenderR + ClipThick),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(StayAngle)+ClipHeight]) // trim top
    cube(2*[FenderD,FenderD,ClipHeight],center=true);
    }
    for (j = [-1,1])
    translate([Ferrule[OD]*sin(StayAngle),j*(FenderR – FenderThick + FenderThick/2),0])
    FerruleBody();
    }
    }
    //———————-
    // Build it
    if (Layout == "Profile") {
    Profile();
    }
    if (Layout == "Ferrule") {
    FerruleBody();
    }
    if (Layout == "Clip") {
    FenderClip();
    }
    if (Layout == "Build") {
    FenderClip();
    }

     

     

  • Sharing the Road on NYS Bike Route 9: Right Hook

    I’m towing a trailer of groceries southbound on Rt 376 (a.k.a. Hooker Avenue in this section), intending to turn right onto Zack’s Way for a library stop.

    T=0.00 s, car @ 26.4 mph, me @ 19.8 mph

    The transverse cracks through the asphalt are a convenient 60 ft apart, with the last one 20 ft from the stop line, and the frame numbers tick along at 60 frame/sec, so you can easily compute distances, times, and speeds.

    I’ll be turning right at the intersection. The light is green.

    Zacks Way - Right Hook 2017-04-11 - 0624
    Zacks Way – Right Hook 2017-04-11 – 0624

    T= 2.07 s, car @ 26.7 mph, me @ 19.7 mph

    Now I can see the car’s right turn signal, so this might not end well. I can’t jam on the brakes and avoid a collision by dumping the bike at speed; I’ll slide under the car in the middle of the turn.

    Zacks Way - Right Hook 2017-04-11 - 0748
    Zacks Way – Right Hook 2017-04-11 – 0748

    T=4.15 s, 15.2 mph

    I’m 20 feet from the stop line and, suddenly, the driver also realizes this might not end well.

    What he doesn’t know is that my trajectory must use the traffic lane: the shoulder around the corner is deteriorated, with several potholes, and vanishes completely where the intersection paving ends.

    Zacks Way - Right Hook 2017-04-11 - 0873
    Zacks Way – Right Hook 2017-04-11 – 0873

    T=5.05 s

    The driver is turning wide, into the opposing traffic lane, but if I weren’t lining up for the turn, we’d be on a collision course. My line will take me just to the left of the seemingly tiny, but very deep, pothole just ahead.

    Zacks Way - Right Hook 2017-04-11 - 0927
    Zacks Way – Right Hook 2017-04-11 – 0927

    T=7.15 s

    Leaning hard into the turn, but our paths won’t cross.

    Zacks Way - Right Hook 2017-04-11 - 1053
    Zacks Way – Right Hook 2017-04-11 – 1053

    T=7.37 s

    I’m back upright in the middle of the lane, with the shoulder ending in a pothole to my right.

    Zacks Way - Right Hook 2017-04-11 - 1066
    Zacks Way – Right Hook 2017-04-11 – 1066

    T=8.31 s

    Remember, I’m wearing a fluorescent (“safety”) orange shirt, running a blinky light (which is also the rear camera), and towing a trailer with a fluttering flag: I am not inconspicuous!

    Zacks Way - Right Hook 2017-04-11 - 1123
    Zacks Way – Right Hook 2017-04-11 – 1123

    In case there’s any question:

    Zacks Way - Right Hook 2017-04-11 - rear
    Zacks Way – Right Hook 2017-04-11 – rear

    The rest of the ride proceeded without incident …

  • Planet Bike SuperFlash Case: PUSH Fatigue

    The blinky light on Mary’s bike became intermittent and, after a week or two, I figured out why:

    Planet Bike Superflash - fatigued PUSH
    Planet Bike Superflash – fatigued PUSH

    The white plastic case has a thin section labeled PUSH over the switch. After five years of exposure to the sun (it faces upward on her bike) and upwards of 2000 pushes (5 years x 200 rides/year x 2 pushes/ride), the edges of that little plate cracked, it slipped inward, and jammed the switch button.

    I swapped it for the one on my bike, which mounts with the switch downward and has seen much less use since I began running the Fly 6 rear camera + blinky light, and it was all good.

    The fractured plate slid snugly back in place, a few drops of IPS 3 solvent-bonded the broken edges, and a snippet of good 3M electrical tape inside the case should provide a bit of reinforcement:

    Planet Bike Superflash - reinforced cover
    Planet Bike Superflash – reinforced cover

    It’s now on my bike, just in case it’s needed.

    That was easy …

  • Honeybee Escort

    The first pleasant day after a long string of snow and rain got us outside again:

    Honeybee escort - 2017-03-29
    Honeybee escort – 2017-03-29

    The honeybee at Mary’s elbow escorted us for a bit, then flew between us and continued on her mission.

    Despite appearances, she passed a few inches from my helmet:

    Honeybee escort - detail 2x - 2017-03-29
    Honeybee escort – detail 2x – 2017-03-29

    We all agreed: it was a fine day for a ride and a flight!

  • 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