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.

The New Hotness

  • Astable Multivibrator: NP-BX1 Base

    Adapting the NP-BX1 battery holder to use SMT pogo pins worked well:

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

    The next step is to add sockets for those 14 AWG wires:

    NP-BX1 Battery Holder - Wire Posts - solid model
    NP-BX1 Battery Holder – Wire Posts – solid model

    Start by reaming / hand-drilling all the holes to their nominal size and cleaning out the pogo pin pocket.

    Solder wires to the pogo pins and thread them through the holder and lid:

    Astable - NP-BX1 holder - pogo pin soldering
    Astable – NP-BX1 holder – pogo pin soldering

    That’s nice, floppy silicone-insulated 24 AWG wire, which may be a bit too thick for this purpose.

    The pogo pins will, ideally, seat with the end of the body flush at the holder wall. Make it so:

    Astable - NP-BX1 holder - pogo pin protrusion
    Astable – NP-BX1 holder – pogo pin protrusion

    Dress the wires neatly into their pocket:

    Astable - NP-BX1 holder - pogo pin wiring
    Astable – NP-BX1 holder – pogo pin wiring

    Butter the bottom of the lid with epoxy, clamp in place, set it up for curing, then fill the recess:

    Astable - NP-BX1 base - curing
    Astable – NP-BX1 base – curing

    While it’s curing, make a soldering fixture for the 14 AWG wires:

    Astable - drilling strut soldering fixture
    Astable – drilling strut soldering fixture

    The holes are on 5 mm centers, in the expectation other battery holders will need different spacing.

    Solder it up and stick the wires into the base:

    Astable - NP-BX1 base - detail
    Astable – NP-BX1 base – detail

    Jam a battery in and It Just Works™:

    Astable - NP-BX1 3.8V - 20ma-div - cap V
    Astable – NP-BX1 3.8V – 20ma-div – cap V

    The traces:

    • Green = supply current at 20 mA/div
    • Yellow = LED driver transistor base voltage
    • Purple = other transistor collector voltage
    • White = base – collector voltage = capacitor voltage

    The measurement setup was a bit of a hairball:

    Astable - NP-BX1 base - current probe
    Astable – NP-BX1 base – current probe

    For completeness, here’s the schematic-and-layout diagram behind the circuitry:

    Astable - NP-BX1 base - schematic
    Astable – NP-BX1 base – schematic

    I love it when a plan comes together!

    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 = "Fit"; // 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
    //- Basic dimensions
    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
    //- 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
    ContactOC = Contacts[1].y – Contacts[0].y;
    ContactCenter = Contacts[0].y + ContactOC/2;
    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
    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];
    //- Wire struts
    StrutDia = 1.6; // AWG 14 = 1.6 mm
    StrutOC = 45;
    StrutSides = 3*4;
    StrutBase = [StrutDia,StrutDia + 4*WallThick,CaseSize.z – TopThick]; // ID = wire, OD=buildable
    //———————-
    // 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
    // Does not include recess access into case
    module PinAssembly() {
    union() {
    for (p = Contacts)
    translate([0,p.y,p.z])
    PinShape();
    translate([-(PinShank[LENGTH] + OverTravel) + PinChannel/2, // solder space
    ContactCenter,
    Contacts[0].z])
    cube([PinChannel,(Contacts[1].y – Contacts[0].y),PinFlange[OD]],center=true);
    for (j=[-1,1]) // wire channels
    translate([-(PinShank[LENGTH] + OverTravel – PinChannel/2),
    j*ContactOC/4 + ContactCenter,
    Contacts[0].z – PinFlange[OD]/2])
    rotate(180/6)
    PolyCyl(PinFlange[OD],CaseSize.z,6);
    }
    }
    //– Case with origin at battery corner
    module Case() {
    difference() {
    union() {
    difference() {
    union() {
    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/cos(180/8),$fn=8); // cos() fixes undersize spheres!
    hull() // wire strut bases
    for (j=[-1,1])
    translate([0,j*StrutOC/2 + Battery.y/2,-BaseThick])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[LENGTH],$fn=StrutSides);
    translate([0,Battery.y/2,StrutBase[LENGTH]/2 – BaseThick])
    cube([2*StrutBase[OD],StrutOC,StrutBase[LENGTH]],center=true);
    }
    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([2*CaseOffset.x, // battery top access
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([2*CaseSize.x,(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    if (false)
    translate([(CaseOffset.x – Protrusion), // battery insertion allowance
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([(CaseSize.x + 2*Protrusion),(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    for (j=[-1,1]) // strut wires
    translate([0,j*StrutOC/2 + Battery.y/2,-(BaseThick + Protrusion)])
    PolyCyl(StrutBase[ID],StrutBase[LENGTH] + 2*Protrusion,6);
    for (i=[-1,1], j=[-1,1])
    translate([i*StrutBase[OD],j*StrutOC/2 + Battery.y/2,-(BaseThick + Protrusion)])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[OD],StrutBase[LENGTH] + 2*Protrusion,StrutSides);
    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 + Protrusion,(Contacts[1].y + Contacts[0].y)/2,Contacts[0].z])
    translate([-PinRecess,0,0])
    cube([2*PinRecess,
    (Contacts[1].y – Contacts[0].y + PinFlange[OD]),
    2*PinFlange[OD]],center=true);
    }
    }
    // Lid position offset to match case
    module Lid() {
    difference() {
    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]) // remove bottom
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),LidSize.z],center=true);
    translate([LidSize.x/8,0,0])
    cube([LidSize.x/4,0.75*LidSize.y,4*ThreadThick],center=true); // epoxy recess
    }
    translate([0,0,-(Contacts[0].z + PinFlange[OD])]) // punch wire holes
    PinAssembly();
    }
    }
    //——————-
    // 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]);
    }
    translate([0,0,Battery.z + Gap])
    Lid();
    color("Silver",0.15)
    PinAssembly();
    }
    if (Layout == "Build") {
    translate([-(CaseSize.x/2 + CaseOffset.x),-(CaseOffset.y – BuildOffset),BaseThick])
    Case();
    translate([CaseSize.x/2,-LidSize.x/2,0])
    rotate(90)
    Lid();
    }
    if (Layout == "Fit") {
    Case();
    translate([0,0,(Battery.z + Gap)])
    Lid();
    color("Silver",0.25)
    PinAssembly();
    }