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: November 2018

  • Astable Multivibrator vs. Charged NP-BX1 Lithium Battery

    Hitching a charged, albeit worn, NP-BX1 lithium battery to the astable multivibrator produces a blinding flash:

    NP-BX1 Holder - SMT pogo pins
    NP-BX1 Holder – SMT pogo pins

    The current pulse shows the wearable LED really takes a beating:

    Astable - NP-BX1 4V - 100mA-div
    Astable – NP-BX1 4V – 100mA-div

    The current trace is at 100 mA/div: the pulse starts at 400 mA, which seems excessive even to me, and tapers down to 200 mA. It’s still an order of magnitude too high at the end of the pulse.

    On the other paw, maybe a 14% duty cycle helps:

    Astable - NP-BX1 4V - base V - 100mA-div
    Astable – NP-BX1 4V – base V – 100mA-div

    The top trace shows the base drive voltage dropping slightly, although I suspect the poor little transistor can’t take the strain.

    The LED really does need a ballast resistor …

  • Sony NP-BX1 Battery Holder: SMT Pogo Pin Contacts

    The original camera battery test fixtures used contact pins conjured from hulking gold-plated connector pins and coil springs:

    Canon NB-6L holder - contact pin detail
    Canon NB-6L holder – contact pin detail

    The Sony HDR-AS30V camera chewed up and spat out a handful of batteries, all tested in the NP-BX1 test fixture:

    NP-BX1 Holder - show layout
    NP-BX1 Holder – show layout

    Nowadays, SMT pogo pins produce a much more compact holder, so I figured I could put all those batteries to good use:

    NP-BX1 Holder - SMT pogo pins
    NP-BX1 Holder – SMT pogo pins

    That’s the long-suffering astable multivibrator, still soldered to its CR123A holder.

    Obviously, the battery holder should grow ears to anchor the 14 AWG copper posts and would look better in black PETG:

    NP-BX1 Battery Holder - 1.5mm pins - solid model
    NP-BX1 Battery Holder – 1.5mm pins – solid model

    The battery lead wires get soldered to the ends of the pogo pins and are recessed into the slot in the end of the fixture. I used clear epoxy to anchor everything in place.

    Fits perfectly and works fine!

    The OpenSCAD source code as a GitHub Gist:

    // Holder for Sony NP-BX1 Li-Ion battery
    // Ed Nisley KE4ZNU January 2013
    // 2018-11-15 Adapted for wire leads from 1.5 mm test pins, added upright wire bases
    // Layout options
    Layout = "Show"; // Show Build Fit Case Lid Pins
    //- Extrusion parameters – must match reality!
    // Print with +2 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.35;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    BuildOffset = 3.0; // clearance for build layout
    Gap = 2.0; // separation for Fit parts
    //- Battery dimensions – rationalized from several samples
    // Coordinate origin at battery contact face with key openings below contacts
    Battery = [43.0,30.0,9.5]; // X = length, Y = width, Z = thickness
    Contacts = [[-0.75,6.0,6.2],[-0.75,16.0,6.2]]; // relative to battery edge, front, and bottom
    KeyBlocks = [[1.75,3.70,2.90],[1.75,3.60,2.90]]; // recesses in battery face set X position
    //- Pin dimensions
    ID = 0;
    OD = 1;
    LENGTH = 2;
    PinShank = [1.5,2.0,6.5]; // shank, flange, compressed length
    PinFlange = [1.5,2.0,0.5]; // flange, length included in PinShank
    PinTip = [0.9,0.9,2.5]; // extended spring-loaded tip
    PinChannel = PinFlange[LENGTH] + 0.5; // cut behind flange for solder overflow
    PinRecess = 3.0; // recess behind pin flange end for epoxy fill
    echo(str("Contact tip dia: ",PinTip[OD]));
    echo(str(" .. shank dia: ",PinShank[ID]));
    OverTravel = 0.5; // space beyond battery face at X origin
    //- Holder dimensions
    GuideRadius = ThreadWidth; // friction fit ridges
    GuideOffset = 7; // from compartment corners
    WallThick = 4*ThreadWidth; // holder sidewalls
    BaseThick = 6*ThreadThick; // bottom of holder to bottom of battery
    TopThick = 6*ThreadThick; // top of battery to top of holder
    ThumbRadius = 10.0; // thumb opening at end of battery
    CornerRadius = 3*ThreadThick; // nice corner rounding
    CaseSize = [Battery.x + PinShank[LENGTH] + OverTravel + PinRecess + GuideRadius + WallThick,
    Battery.y + 2*WallThick + 2*GuideRadius,
    Battery.z + BaseThick + TopThick];
    CaseOffset = [-(PinShank[LENGTH] + OverTravel + PinRecess),-(WallThick + GuideRadius),0]; // position around battery
    LidOverhang = 2.0; // over top of battery for retention
    LidSize = [-CaseOffset.x + LidOverhang,CaseSize.y,TopThick];
    LidOffset = [0.0,CaseOffset.y,0];
    //———————-
    // 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);
    }
    //——————-
    //– Guides for tighter friction fit
    module Guides() {
    translate([GuideOffset,-GuideRadius,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([GuideOffset,(Battery.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x – GuideOffset),-GuideRadius,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x – GuideOffset),(Battery.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x + GuideRadius),GuideOffset/2,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x + GuideRadius),(Battery.y – GuideOffset/2),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    }
    //– Contact pins
    // Rotated to put them in their natural oriention
    // Aligned to put tip base / end of shank at Overtravel limit
    module PinShape() {
    translate([-(PinShank[LENGTH] + OverTravel),0,0])
    rotate([0,90,0])
    rotate(180/6)
    union() {
    PolyCyl(PinTip[OD],PinShank[LENGTH] + PinTip[LENGTH],6);
    PolyCyl(PinShank[ID],PinShank[LENGTH] + Protrusion,6); // slight extension for clean cuts
    PolyCyl(PinFlange[OD],PinFlange[LENGTH],6);
    }
    }
    // Position pins to put end of shank at battery face
    // Add wire exit channel between pins
    // Does not include recess access
    module PinAssembly() {
    union() {
    for (p = Contacts)
    translate([0,p.y,p.z])
    PinShape();
    translate([-(PinShank[LENGTH] + OverTravel) + PinChannel/2,
    (Contacts[1].y + Contacts[0].y)/2,
    Contacts[0].z])
    cube([PinChannel,(Contacts[1].y – Contacts[0].y),PinFlange[OD]],center=true);
    }
    }
    //– Case with origin at battery corner
    module Case() {
    difference() {
    union() {
    difference() {
    translate([(CaseSize.x/2 + CaseOffset.x), // basic case shape
    (CaseSize.y/2 + CaseOffset.y),
    (CaseSize.z/2 – BaseThick)])
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(CaseSize.x/2 – CornerRadius),
    j*(CaseSize.y/2 – CornerRadius),
    k*(CaseSize.z/2 – CornerRadius)])
    sphere(r=CornerRadius,$fn=8);
    translate([-OverTravel,-GuideRadius,0])
    cube([(Battery.x + GuideRadius + OverTravel),
    (Battery.y + 2*GuideRadius),
    (Battery.z + Protrusion)]); // battery space
    }
    Guides(); // improve friction fit
    translate([-OverTravel,-GuideRadius,0]) // battery keying blocks
    cube(KeyBlocks[0] + [OverTravel,GuideRadius,0],center=false);
    translate([-OverTravel,(Battery.y – KeyBlocks[1].y),0])
    cube(KeyBlocks[1] + [OverTravel,GuideRadius,0],center=false);
    }
    translate([(-OverTravel), // battery top access
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([CaseSize.x,(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    translate([(CaseOffset.x – Protrusion), // battery insertion allowance
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([(CaseSize.x + 2*Protrusion),(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    translate([(Battery.x – Protrusion), // remove thumb notch
    (CaseSize.y/2 + CaseOffset.y),
    (ThumbRadius)])
    rotate([90,0,0])
    rotate([0,90,0])
    cylinder(r=ThumbRadius,
    h=(WallThick + GuideRadius + 2*Protrusion),
    $fn=22);
    PinAssembly();
    translate([CaseOffset.x + PinRecess/2 + Protrusion/2,(Contacts[1].y + Contacts[0].y)/2,Contacts[0].z])
    cube([PinRecess + Protrusion,
    (Contacts[1].y – Contacts[0].y + PinFlange[OD]),
    2*PinFlange[OD]],center=true);
    }
    }
    // Lid position offset to match case
    module Lid() {
    translate([-LidSize.x/2 + LidOffset.x + LidOverhang,LidSize.y/2 + LidOffset.y,0])
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(LidSize.x/2 – CornerRadius),
    j*(LidSize.y/2 – CornerRadius),
    k*(LidSize.z – CornerRadius)]) // double thickness for flat bottom
    sphere(r=CornerRadius,$fn=8);
    translate([0,0,-LidSize.z/2])
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),LidSize.z],center=true);
    cube([LidSize.x/4,0.75*LidSize.y,4*ThreadThick],center=true); // epoxy recess
    }
    }
    //——————-
    // Build it!
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "Pins") {
    color("Silver",0.5)
    PinShape();
    PinAssembly();
    }
    if (Layout == "Show") { // reveal pin assembly
    difference() {
    Case();
    translate([(CaseOffset.x – Protrusion),
    Contacts[1].y,
    Contacts[1].z])
    cube([(-CaseOffset.x + Protrusion),
    CaseSize.y,
    (CaseSize.z – Contacts[0].z + Protrusion)]);
    translate([(CaseOffset.x – Protrusion),
    (CaseOffset.y – Protrusion),
    0])
    cube([(-CaseOffset.x + Protrusion),
    Contacts[0].y + Protrusion – CaseOffset.y,
    CaseSize.z]);
    }
    color("Silver",0.15)
    PinAssembly();
    translate([0,0,Battery.z + Gap])
    Lid();
    }
    if (Layout == "Build") {
    translate([-(CaseSize.x/2 + CaseOffset.x),-(CaseOffset.y – BuildOffset),BaseThick])
    Case();
    translate([CaseSize.y/2,(CaseOffset.x/2 – BuildOffset),0])
    rotate([0,0,90])
    Lid();
    }
    if (Layout == "Fit") {
    Case();
    translate([0,0,(Battery.z + Gap)])
    Lid();
    color("Silver",0.25)
    PinAssembly();
    }
  • Makergear M2 V4 Nozzle: More Silicone!

    A Makergear forum discussion on PETG hair and the prevention thereof prompted me to take a look at the silicone coating I’d applied to the nozzle:

    M2 - nozzle silicone - applied
    M2 – nozzle silicone – applied

    That was ten months ago. This is now:

    M2 Nozzle - worn silicone coat
    M2 Nozzle – worn silicone coat

    The camera sees the nozzle in a mirror laid flat on the platform, making the image less crisp than a direct view.

    So the silicone seems a bit worn around the tip, has acquired a few firmly adhered globs, and definitely isn’t as shiny.

    Rather than (try to) peel it off and reapply a new coating, I picked off the globs, cleaned around the nozzle, and slobbered a thin layer atop the existing silicone:

    M2 Nozzle - more silicone
    M2 Nozzle – more silicone

    Extruding a few millimeters of filament pushed the film off the nozzle opening and it now works as well as it ever did.

     

  • Electrolux Vacuum Cleaner: Cord Protection

    The ancient (Came With The House™) Electrolux canister vacuum cleaner long ago lost the plastic bushing around the opening passing its retractable cord, which I’d long sworn to replace.  A recent trip around the Basement Laboratory paused near the recently relocated Box o’ Wire Loom & Braid, whereupon I snipped off a few inches of split loom and tucked it in place:

    Electrolux Vacuum - cushioned cord cutout
    Electrolux Vacuum – cushioned cord cutout

    Looks and works better than before, anyhow.

    The blue flap dangling off the back should latch over the exhaust port, but failed long ago when the latch tab eroded. I attempted a repair, which never worked quite right, and won’t get around to attempting another for quite a while.

  • Kindle Fire Picture Frame: Copying the Pictures

    Being a bear of unbearable consistency, I save edited picture files with a description following the original camera-assigned sequence number:

    IMG_20181108_190041 - Kindle Fire Picture Frame - Another Test Image.jpg
    

    Yup, spaces and all.

    Kindle Fire Picture Frame - Another Test Image
    Kindle Fire Picture Frame – Another Test Image

    I store my general-interest pix chronologically by year, in subdirectories for interesting categories, so copying all the edited (a.k.a. “interesting”) pictures to the Kindle Fire becomes a one-liner:

    cd /mnt/bulkdata/Cameras
    find 20?? -iname \*\ \*jpg -print0 | xargs -0 cp --parents -t /mnt/part/Pictures
    

    The --parents parameter tells cp to recreate the directory structure holding the picture in the target directory, thereby keeping the pix neatly sorted in their places, rather than creating one heap o’ pictures.

    Come to find out I’ve edited slightly over 7 k general-interest pictures in the eighteen years I’ve been using digital cameras, of maybe 27 k total pictures. Call it a 25% hit ratio; obviously I’m not nearly fussy enough.

    Then there’s another 16 k project-related pictures, of which 10 k were edited into something useful. With an emphasis on utility, rather than aesthetics, a 60% hit ratio seems OK.

    Which works out to half a dozen pictures a day, every day, for eighteen years. I loves me some good digital camera action!

  • Badger Propel Air Fittings: DIY Rubber Washers

    Unlike ordinary bike tubes, Michelin ProTek tubes have a square-ish cross section:

    Michelin Protek Max Tube - carton
    Michelin Protek Max Tube – carton

    So, having a defunct Michelin ProTek tube in hand, I cut, cleaned, and dissected a few inches, then punched out a set of (flat!) washers for the Badger Propel air fittings:

    Badger 260 Propel fitting seals - inner-tube version
    Badger 260 Propel fitting seals – inner-tube version

    As with the cork version, they fit fine:

    Badger Propel fitting - DIY rubber seal
    Badger Propel fitting – DIY rubber seal

    The picture is slightly fuzzy, because zooming a Pixel photo doesn’t magically create any new mmmm pixels.

    I tested the washer with 45 psi air (the recommended maximum) and it holds the pressure fine. Better than a fouled ProTek valve, anyway.

    Flushed with success, I preemptively replaced both OEM cork washers, an action which will surely come back to haunt me.

     

     

  • Michelin ProTek Max Tube: Autopsy

    The Michelin ProTek Max tube I installed two years ago developed a slow leak this year, which I eventually ascribed to the valve stem, because the sealant should plug any other leak.

    Cutting it open reveals the perfectly good greenish-yellow sealant:

    Michelin ProTek tube - sealant
    Michelin ProTek tube – sealant

    The sealant also carries black rubbery grit / shavings / dust, perhaps intended to jam inside larger gashes while the sealant coagulates and binds it together.

    There’s a lot of rubber floating around in there:

    Michelin ProTek tube - rubber fragments
    Michelin ProTek tube – rubber fragments

    Dismantling the Presta valve stem show the rubbery crud on and around the valve seal and seat:

    Michelin ProTek tube - fouled valve seal
    Michelin ProTek tube – fouled valve seal

    Whenever I pumped up the tires, I finger-tightened the nut to ensure a good seal, as you do with all Presta valves. Obviously, finger-tight can’t handle that much crud between the sealing surfaces.

    I’m sorry to say I was right about the leaky valve stem, because I think all the ProTek tubes will fail in exactly the same way.

    The valve has small wrench flats making it easy to remove, so I can at least attempt to de-gunk them when they develop slow leaks.

    Color me unimpressed.