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.

Author: Ed

  • Sandisk Extreme Pro MicroSD Card: End of Life?

    The Sandisk Extreme Pro 64 GB MicroSD card in the Sony HDR-AS30V died on the road once more, got reformatted, worked OK for a while, then kicked out catastrophic I/O errors after being mounted, so I swapped in the High Endurance card:

    Sandisk - 64 GB MicroSDXC cards
    Sandisk – 64 GB MicroSDXC cards

    The Extreme Pro still passes the f3probe tests, so it’s not completely dead, but if I can’t trust it in the helmet camera, it’s dead to me.

    It survived 17 months of more-or-less continuous use, although we didn’t do nearly enough riding for three months early this year. Call it 14 months x five rides / week x 1 hour / ride = 300 hours of recording. Multiply by 4 GB / 22.75 minutes to get 3 TB of video, about 50 times its total capacity.

    The never-sufficiently-to-be-damned Sony cards failed after less than 1 TB and 15-ish times capacity, making the Sandisk Extreme Pro much better. However, it’s painfully obvious these cards work better for low-intensity still-image recording, rather than continuous HD video.

    Using them as Raspberry Pi “hard drives” surely falls somewhere between still cameras and video, although Octoprint’s video snapshots and streaming media must make ’em sweat.

    We’ll see how Sandisk’s High Endurance memory works in precisely the application it’s labeled for.

  • Tenergy 18650 Lithium Cells: Initial Capacity

    The daytime running lights on the bikes get noticeably dimmer when the 18650 lithium cell voltage drops below 3.6 V, so I picked up a quartet of Tenergy protected cells and ran ’em through the battery tester:

    Tenergy 18650 Protected - 2017-08-04
    Tenergy 18650 Protected – 2017-08-04

    As with the ATX cells, the voltage decreases almost linearly with charge until it falls off the cliff near the end, but these have a higher terminal voltage throughout most of the curve, which is a Good Thing for LED flashlights.

    These four seem to have about the same overall capacity as the ATX cells, so we’ll run ’em all in sequence and see how long they last.

     

  • Mystery Caterpillars

    This being caterpillar season, we put a mystery egg mass on a Swiss Chard leaf into a small container:

    Mystery Caterpillar - eggs on Swiss Chard
    Mystery Caterpillar – eggs on Swiss Chard

    I think the darker egg was a dud, because two days later they all hatched and ate their egg cases, leaving that one behind:

    Mystery Caterpillar - hatched
    Mystery Caterpillar – hatched

    Mary deported them to the trash, put two on a leaf in an aquarium on the kitchen table, and, eight days later:

    Mystery Caterpillar - 8 days
    Mystery Caterpillar – 8 days

    They’ve been chowing down on spare garden greenery; unlike Monarchs, they eat what’s set before them.

    One has dark “fur”:

    Mystery Caterpillar - black morph
    Mystery Caterpillar – black morph

    The second is lighter:

    Mystery Caterpillar - brown morph
    Mystery Caterpillar – brown morph

    A third caterpillar escaped the trash can apocalypse and also resides in the aquarium, albeit stunted by its ordeal:

    Mystery Caterpillar - pale morph
    Mystery Caterpillar – pale morph

    They’re too bristly to be Wooly Bears. I’m sure they’ll turn into nondescript brown moths.

  • Milkweed Tussock Moth Caterpillars By The Handful

    Monarch butterfly eggs occur in onesie-twosies on each milkweed plant, but Tussock Moths carpet-bomb the leaves with eggs that hatch pretty much all at once:

    Milkweed Tussock Moth Caterpillars - detail
    Milkweed Tussock Moth Caterpillars – detail

    With a population density like that, the plant doesn’t stand a chance:

    Milkweed Tussock Moth Caterpillars
    Milkweed Tussock Moth Caterpillars

    A few hours later, they were gone and so were the leaves! Presumably, they’re traveling across the ground to the adjacent milkweed plants; one or two may find our patio.

    Despite all the egg-laying we saw, we haven’t seen any Monarch caterpillars out there.

  • Tour Easy Daytime Running Light: Pile of Prototypes

    Although I wish I could come up with a finished design in one pass, usually I end up with a big pile of nope before producing the one I want:

    Fairing Flashlight Mount - Iterations
    Fairing Flashlight Mount – Iterations

    The mounts on the left show the progression from large hemisphere balls to the same-size finger ball to the smaller finger ball, with the smaller cyan arch clamp in the foreground still festooned with its support structure. The stack of plates to the right (with the original faded & distintegrating ABS plates in the bag) comes from reprinting in cyan to match the small mounts now on the bikes:

    Fairing Flashlight Mount - rounded
    Fairing Flashlight Mount – rounded

    Hey, it’s time for a ride!

  • Tour Easy Daytime Running Light: Annotation

    The flashlight mount need not be symmetric after applying all the rotations, so recording how it’s aimed and which end goes forward seemed appropriate:

    Fairing Flashlight Mount - Mount Annotation
    Fairing Flashlight Mount – Mount Annotation

    Optionally, with rounded ends just for pretty:

    Fairing Flashlight Mount - Mount Annotation - rounded
    Fairing Flashlight Mount – Mount Annotation – rounded

    Because the rounding comes from resized spheres, the plate gets a ridge along the top to (maybe) lock the nylon screws / wing nuts in place:

    Fairing Flashlight Mount - Mount - rounded
    Fairing Flashlight Mount – Mount – rounded

    Or discourage them from turning, which would be OK, too. After the second tightening, they don’t seem to come loose, so this may be overthinking the problem.

    All in all, they look pretty good in cyan PETG:

    Fairing Flashlight Mount - rounded
    Fairing Flashlight Mount – rounded

    Believe it or not, that’s aimed so the top edge of the beam is roughly horizontal to keep the hot spot out of oncoming traffic. They’re plenty bright, even on the “low power” setting.

    The flashlight mounting balls produce a decorative brim that ought to be useful for something:

    Slotted ball on platform
    Slotted ball on platform

    Maybe earrings?

    The OpenSCAD source code as a GitHub Gist:

    // Tour Easy Fairing Flashlight Mount
    // Ed Nisley KE4ZNU – July 2017
    // August 2017 –
    /* [Build Options] */
    FlashName = "AnkerLC40"; // [AnkerLC40,AnkerLC90,J5TactV2,InnovaX5]
    Component = "Plates"; // [Ball, BallClamp, Mount, Plates, Bracket]
    Layout = "Build"; // [Build, Show]
    Support = false;
    MountSupport = true;
    /* [Extrusion] */
    ThreadThick = 0.25; // [0.20, 0.25]
    ThreadWidth = 0.40; // [0.40]
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.01; // [0.01, 0.1]
    HoleWindage = 0.2;
    /* [Fairing Mount] */
    ToeIn = 0; // inward from ahead
    Tilt = 20; // upward from forward (M=20 E=15)
    Roll = 0; // outward from top
    Shift = 0; // Finagle Constant for support ribs
    //- Screws *c
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    /* [Screws and Inserts] */
    ClampInsert = [3.0,4.2,8.0];
    ClampScrew = [3.0,5.9,35.0]; // thread dia, head OD, screw length
    ClampScrewWasher = [3.0,6.75,0.5];
    ClampScrewNut = [3.0,6.1,4.0]; // nyloc nut
    /* [Hidden] */
    F_NAME = 0;
    F_GRIPOD = 1;
    F_GRIPLEN = 2;
    LightBodies = [
    ["AnkerLC90",26.6,48.0],
    ["AnkerLC40",26.6,55.0],
    ["J5TactV2",25.0,30.0],
    ["InnovaX5",22.0,55.0]
    ];
    //- Fairing Bracket
    // Magic numbers taken from the actual fairing mount
    /* [Hidden] */
    inch = 25.4;
    BracketHoleOD = 0.25 * inch; // 1/4-20 bolt holes
    BracketHoleOC = 1.0 * inch; // fairing hole spacing
    // usually 1 inch, but 15/16 on one fairing
    Bracket = [48.0,16.3,3.6 – 0.6]; // fairing bracket end plate overall size
    BracketHoleOffset = (3/8) * inch; // end to hole center
    BracketM = 3.0; // endcap arc height
    BracketR = (pow(BracketM,2) + pow(Bracket[1],2)/4) / (2*BracketM); // … radius
    //- Base plate dimensions
    Plate = [100.0,30.0,6*ThreadThick + Bracket[2]];
    PlateRad = Plate[1]/4;
    RoundEnds = true;
    echo(str("Base plate thick: ",Plate[2]));
    //- Select flashlight data from table
    echo(str("Flashlight: ",FlashName));
    FlashIndex = search([FlashName],LightBodies,1,0)[F_NAME];
    //- Set ball dimensions
    BallWall = 5.0; // max ball wall thickness
    echo(str("Ball wall: ",BallWall));
    BallOD = IntegerMultiple(LightBodies[FlashIndex][F_GRIPOD] + 2*BallWall,1.0);
    echo(str(" OD: ",BallOD));
    BallLength = IntegerMultiple(min(sqrt(pow(BallOD,2) – pow(LightBodies[FlashIndex][F_GRIPOD],2)) – 2*4*ThreadThick,
    LightBodies[FlashIndex][F_GRIPLEN]),1.0);
    echo(str(" length: ",BallLength));
    BallSides = 8*4;
    //- Set clamp ring dimensions
    ClampOD = 50;
    echo(str("Clamp OD: ",ClampOD));
    ClampLength = min(20.0,0.75*BallLength);
    echo(str(" length: ",ClampLength));
    ClampScrewOC = IntegerMultiple((ClampOD + BallOD)/2,1);
    echo(str(" screw OC: ",ClampScrewOC));
    //- 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);
    }
    //- Fairing Bracket
    // This part of the fairing mount supports the whole flashlight mount
    // Centered on screw hole
    module Bracket() {
    linear_extrude(height=Bracket[2],convexity=2)
    difference() {
    translate([(Bracket[0]/2 – BracketHoleOffset),0,0])
    offset(delta=ThreadWidth)
    intersection() {
    square([Bracket[0],Bracket[1]],center=true);
    union() {
    for (i=[-1,0,1]) // middle circle fills gap
    translate([i*(Bracket[0]/2 – BracketR),0])
    circle(r=BracketR);
    }
    }
    circle(d=BracketHoleOD/cos(180/8),$fn=8); // dead center at the origin
    }
    }
    //- General plate shape
    // Centered on the hole for the fairing bracket
    module PlateBlank() {
    difference() {
    translate([BracketHoleOC,0,0])
    intersection() {
    translate([0,0,Plate[2]/2]) // select upper half of spheres
    cube(Plate,center=true);
    hull()
    if (RoundEnds)
    for (i=[-1,1])
    translate([i*(Plate[0]/2 – PlateRad),0,0])
    resize([Plate[1]/2,Plate[1],2*Plate[2]])
    sphere(r=PlateRad); // nice round ends!
    else
    for (i=[-1,1], j=[-1,1])
    translate([i*(Plate[0]/2 – PlateRad),j*(Plate[1]/2 – PlateRad),0])
    resize([2*PlateRad,2*PlateRad,2*Plate[2]])
    sphere(r=PlateRad); // nice round corners!
    }
    translate([2*BracketHoleOC,0,-Protrusion]) // punch screw holes
    PolyCyl(BracketHoleOD,2*Plate[2],8);
    translate([0,0,-Protrusion])
    PolyCyl(BracketHoleOD,2*Plate[2],8);
    }
    }
    //- Inner plate
    module InnerPlate() {
    difference() {
    PlateBlank();
    translate([0,0,Plate[2] – Bracket[2] + Protrusion]) // punch fairing bracket
    Bracket();
    }
    }
    //- Slotted ball around flashlight
    // Print with brim to ensure adhesion!
    module SlotBall() {
    NumSlots = 8*2; // must be even, half cut from each end
    SlotWidth = 2*ThreadWidth;
    SlotBaseThick = 10*ThreadThick; // enough to hold finger ends together
    RibLength = (BallOD – LightBodies[FlashIndex][F_GRIPOD])/2;
    translate([0,0,BallLength/2])
    difference() {
    intersection() {
    sphere(d=BallOD,$fn=2*BallSides); // basic ball
    cube([2*BallOD,2*BallOD,BallLength],center=true); // trim to length
    }
    translate([0,0,-LightBodies[FlashIndex][F_GRIPOD]])
    rotate(180/BallSides)
    PolyCyl(LightBodies[FlashIndex][F_GRIPOD],2*BallOD,BallSides); // remove flashlight body
    for (i=[0:NumSlots/2 – 1]) { // cut slots
    a=i*(2*360/NumSlots);
    SlotCutterLength = LightBodies[FlashIndex][F_GRIPOD];
    rotate(a)
    translate([SlotCutterLength/2,0,SlotBaseThick])
    cube([SlotCutterLength,SlotWidth,BallLength],center=true);
    rotate(a + 360/NumSlots)
    translate([SlotCutterLength/2,0,-SlotBaseThick])
    cube([SlotCutterLength,SlotWidth,BallLength],center=true);
    }
    }
    color("Yellow")
    if (Support) {
    for (i=[0:NumSlots-1]) {
    a = i*360/NumSlots;
    rotate(a + 180/NumSlots)
    translate([(LightBodies[FlashIndex][F_GRIPOD] + RibLength)/2 + ThreadWidth,0,BallLength/(2*4)])
    cube([RibLength,2*ThreadWidth,BallLength/4],center=true);
    }
    }
    }
    //- Clamp around flashlight ball
    module BallClamp() {
    BossLength = ClampScrew[LENGTH] – 1*ClampScrewWasher[LENGTH];
    BossOD = ClampInsert[OD] + 2*(6*ThreadWidth);
    difference() {
    union() {
    intersection() {
    sphere(d=ClampOD,$fn=BallSides); // exterior ball clamp
    cube([ClampLength,2*ClampOD,2*ClampOD],center=true); // aiming allowance
    }
    hull()
    for (j=[-1,1])
    translate([0,j*ClampScrewOC/2,-BossLength/2])
    cylinder(d=BossOD,h=BossLength,$fn=6);
    }
    sphere(d=(BallOD + 1*ThreadThick),$fn=BallSides); // interior ball with minimal clearance
    for (j=[-1,1]) {
    translate([0,j*ClampScrewOC/2,-ClampOD]) // screw clearance
    PolyCyl(ClampScrew[ID],2*ClampOD,6);
    translate([0,j*ClampScrewOC/2, // insert clearance
    -(BossLength/2 – ClampInsert[LENGTH] – 3*ThreadThick)])
    rotate([0,180,0])
    PolyCyl(ClampInsert[OD],2*ClampOD,6);
    translate([0,j*ClampScrewOC/2, // insert transition
    -(BossLength/2 – ClampInsert[LENGTH] – 3*ThreadThick)])
    cylinder(d1=ClampInsert[OD]/cos(180/6),d2=ClampScrew[ID],h=6*ThreadThick,$fn=6);
    }
    }
    color("Yellow")
    if (Support) { // ad-hoc supports for top half
    NumRibs = 6;
    RibLength = 0.5 * BallOD;
    RibWidth = 1.9*ThreadWidth;
    SupportOC = ClampLength / NumRibs;
    cube([ClampLength,RibLength,4*ThreadThick],center=true); // base plate for adhesion
    render(convexity=2*NumRibs)
    intersection() {
    sphere(d=BallOD – 0*ThreadWidth); // cut at inner sphere OD
    cube([ClampLength + 2*ThreadWidth,RibLength,BallOD],center=true);
    union() { // ribs for E-Z build
    for (j=[-1,0,1])
    translate([0,j*SupportOC,0])
    cube([ClampLength,RibWidth,1.0*BallOD],center=true);
    for (i=[0:NumRibs]) // allow NumRibs + 1 to fill the far end
    translate([i*SupportOC – ClampLength/2,0,0])
    rotate([0,90,0])
    cylinder(d=BallOD – 2*ThreadThick,
    h=RibWidth,$fn=BallSides,center=true);
    }
    }
    }
    }
    //- Mount between fairing plate and flashlight ball
    // Build with support for bottom of clamp screws!
    module Mount() {
    difference() {
    translate([-BracketHoleOC,0,0]) // put bracket center at origin
    PlateBlank();
    mirror([0,1,0])
    translate([0,0,-Protrusion])
    linear_extrude(height=3*ThreadThick + Protrusion) {
    translate([BracketHoleOC + 15,0,0])
    text(text=">>>",size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([-BracketHoleOC,8,0])
    text(text=str("Toe ",ToeIn),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([-BracketHoleOC,-8,0])
    text(text=str("Tilt ",Tilt),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([BracketHoleOC,8,0])
    text(text=str("Roll ",Roll),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([-(BracketHoleOC + 15),0,0])
    rotate(90)
    text(text="KE4ZNU",size=4,spacing=1.20,font="Arial",halign="center",valign="center");
    }
    }
    rotate([0,ToeIn,Tilt])
    translate([0,0,ClampOD/2])
    rotate([-Roll,0,0])
    intersection() {
    translate([0,0,-ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    BallClamp();
    }
    color("Yellow")
    if (MountSupport) { // anchor outer corners at worst overhang
    RibWidth = 1.9*ThreadWidth;
    SupportOC = 0.1 * ClampLength;
    difference() {
    rotate([0,0,Tilt])
    translate([Shift,0,0])
    for (i=[-4.5,-2.5,0,2.0,4.5])
    translate([i*SupportOC – 0.0,0,(5 + Plate[2])/2])
    cube([RibWidth,0.7*ClampOD,(5 + Plate[2])],center=true);
    rotate([0,ToeIn,Tilt])
    translate([Shift,0,ClampOD/2])
    rotate([-Roll,0,0])
    sphere(d=ClampOD – 2*ThreadWidth,$fn=BallSides);
    }
    }
    }
    //- Build things
    if (Component == "Ball")
    SlotBall();
    if (Component == "BallClamp")
    if (Layout == "Show")
    BallClamp();
    else if (Layout == "Build") {
    Both = false;
    difference() {
    union() {
    translate([Both ? ClampLength : 0,0,0])
    BallClamp();
    if (Both)
    translate([-ClampLength,0,0])
    rotate([180,0,0])
    BallClamp();
    }
    translate([0,0,-ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    }
    }
    if (Component == "Mount")
    Mount();
    if (Component == "Plates") {
    translate([0,0.7*Plate[1],0])
    InnerPlate();
    translate([0,-0.7*Plate[1],0])
    PlateBlank();
    }
    if (Component == "Bracket")
    Bracket();

     

  • Wasabi NP-BX1 Batteries: FAIL

    I just got a new pair of Wasabi Power NP-BX1 batteries for the HDR-AS30V helmet camera, charged them up, and ran them through the CBA:

    Sony NP-BX1 - Wasabi FGHI - 2017-08-08
    Sony NP-BX1 – Wasabi FGHI – 2017-08-08

    Huh.

    The new ones (H and I) cost near twice as much as the 2-1/2 year old previous pair (F and G), while delivering less energy at a lower terminal voltage. Tested ’em twice to be sure and the curves overlay just about perfectly, so it’s not a fluke.

    The rep assures me the replacement cells will deliver their promised capacity. We shall see.