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

  • PTC Fuses

    PTC Fuses

    Lithium battery packs have overcurrent protection cutouts, but alkaline cells depend on their internal resistance and may overheat in response to a serious short circuit. So adding a PTC fuse to the circuitry over an alkaline battery case seemed appropriate:

    Discrete LM3909 - Darl Q1 - 1X Q2 - blue LED test
    Discrete LM3909 – Darl Q1 – 1X Q2 – blue LED test

    That’s a test setup for a discrete-transistor version of an LM3909 LED blinker, about which more later. The PTC fuse looks a lot like a ceramic capacitor with one leg caught in an alligator clip.

    Two bags of PTC fuses recently arrived from halfway around the planet, rated at 100 mA and 170 mA. One allegedly came from JinKe and the other probably didn’t pass through a Littelfuse factory despite its part number, but the only datasheet I can find is for the Littelfuse RXEF PTC PolySwitch series, which is surely close enough.

    I set up a torture test involving a bench power supply and an ammeter, both offscreen and left to your imagination:

    PTC Polyfuse test setup
    PTC Polyfuse test setup

    At 75 °F:

    • 100 mA PTC – 4.75 Ω
    • 170 mA PTC – 2.80 Ω

    With a dead short simulated by 3 V from the supply, the current stabilized at:

    • 100 mA PTC – 125 mA
    • 170 mA PTC – 135 mA

    Cranked to 5 V for that good old TTL vibe:

    • 100 mA PTC – 70 mA
    • 170 mA PTC – 85 mA

    The datasheet says they’re good up to 60 V, but that’s just crazy talk.

    The abuse put a shiny gloss on the epoxy coating, sort of like when you overcooked one of those wax-insulated capacitors back in the day.

    Despite that, a PTC fuse is better than a dead short, if only because the plastic battery case won’t get all melty with the batteries supplying less than half a watt.

  • Outdoor Junction Box: Stretch to Fit

    Outdoor Junction Box: Stretch to Fit

    For whatever reason, a two-outlet junction box stands outside the Credit Union:

    Outdoor Junction Box - angled conduit
    Outdoor Junction Box – angled conduit

    The slanted conduit certainly looks in need of an elbow to line it up, doesn’t it?

    It seems whoever installed it, many years ago, simply forced the conduit to line up, no matter the consequences:

    Outdoor Junction Box - open wiring
    Outdoor Junction Box – open wiring

    The threaded entries on the die-cast outlet box were never intended to cope with that much misalignment; half the bottom has vanished. I think the round box on the top originally held a floodlight to wash the (uninspired) building facade at night, but those days are long gone.

    If the conduit has horizontal underground runs, both are certainly full of water by now. The white(-ish) “Romex” cable insulation looks like ordinary indoor wiring, not the grayish direct-burial sheath, but it may be sun-bleached after years of exposure.

    Surely there’s a tripped GFI on that circuit …

  • LTSpice Diode Models Sorted By Forward Voltage

    LTSpice includes a bunch of LEDs I’ll never own, so finding a tabulation of their forward voltages helped match them against various LEDs on hand. The table was sorted by the forward voltage at the diode’s rated average current, which wasn’t helpful for my simple needs, so I re-sorted it on the Vf @ If = 20 mA column over on the right:

    Part #       Mfg             Is         N      Iavg Vf@Iavg  Vd@If
    QTLP690C     Fairchild    1.00E-22    1.500    0.16   1.90    1.82
    PT-121-B     Luminous     4.35E-07    8.370   20.00   3.84    2.34
    LUW-W5AP     OSRAM        6.57E-08    7.267    2.00   3.26    2.39
    LXHL-BW02    Lumileds     4.50E-20    2.600    0.40   2.95    2.75
    W5AP-LZMZ-5K Lumileds     3.50E-17    3.120    2.00   3.13    2.76
    LXK2-PW14    Lumileds     3.50E-17    3.120    1.60   3.11    2.76
    AOT-2015     AOT          5.96E-10    6.222    0.18   3.16    2.80
    NSSW008CT-P  Nichia       2.30E-16    3.430    0.04   2.92    2.86
    NSSWS108T    Nichia       1.13E-18    3.020    0.04   2.99    2.94
    NSPW500BS    Nichia       2.70E-10    6.790    0.03   3.27    3.20
    NSCW100      Nichia       1.69E-08    9.626    0.03   3.60    3.50

    The currents come from plugging the various constants into the Schockley Diode Equation and turning the crank.

    One could, of course, measure the constants for the diodes on hand to generate a proper Spice model, but that seems like a lot of work for what’s basically a blinking LED.

  • Cheap LED Assortment: Forward Voltage

    Cheap LED Assortment: Forward Voltage

    Starting with a box of cheap LEDs from halfway around the planet:

    LED kit - case
    LED kit – case

    Measuring the forward voltages didn’t take much effort:

    5mm 3mm LED kit - Vf tests
    5mm 3mm LED kit – Vf tests

    The top array fed the LEDs from a bench power supply through a 470 Ω resistor, with the voltage adjusted to make the current come out right. The bottom array came from the Siglent SDM3045 multimeter’s diode test function, which goes up to 4 V while applying about 400 µA to the diode (the 20 µA header is wrong).

    These numbers come into play when blinking an LED from a battery, because a battery voltage much below the Vf value won’t produce much light. It’s a happy coincidence that a single lithium cell can light a white or blue LED …

    For comparison, the forward voltages from another batch of LEDs:

    ROYGBUIW - LED Color vs Vf
    ROYGBUIW – LED Color vs Vf

    Those all look a bit higher at 20 mA, but everything about the measurements is different, so who knows?

  • AA Alkaline Battery Holder

    AA Alkaline Battery Holder

    A battery holder for AA alkaline cells descends directly from the NP-BX1 version:

    Astable Multivibrator - Alkaline Batteries - solid model - Show layout
    Astable Multivibrator – Alkaline Batteries – solid model – Show layout

    The square recesses fit single contact pads on the left and a “positive-to-negative conversion” plate on the right, all secured with dabs of acrylic adhesive:

    Alkaline AA holder - contacts
    Alkaline AA holder – contacts

    Although the OpenSCAD code contains an array of battery dimensions, it only works for AA cells.

    The recess on the far left is where you solder the wires onto the contact tabs, with the wires leading outward through the holes in the lid. The case needs an indexing feature to hold the lid square while gluing it down.

    Alkaline cells cells do not have current-limiting circuitry, so a low-current PTC fuse seems like a Good Idea. I initially thought of hiding it in the recess, but the Brutalist nature of the astables suggests open air.

    The OpenSCAD source code as a GitHub Gist:

    // Astable Multivibrator
    // Holder for Alkaline cells
    // Ed Nisley KE4ZNU August 2020
    /* [Layout options] */
    CellName = "AA"; // [AA] — does not work with anything else
    NumCells = 2;
    Layout = "Case"; // [Build,Show,Lid]
    Struts = -1; // [0:None, -1:Dual, 1:Quad]
    // Extrusion parameters – must match reality! */
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    function IntegerLessMultiple(Size,Unit) = Unit * floor(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    //- Basic dimensions
    WallThick = IntegerMultiple(3.0,ThreadWidth);
    CornerRadius = WallThick/2;
    FloorThick = IntegerMultiple(3.0,ThreadThick);
    TopThick = IntegerMultiple(2.0,ThreadThick);
    WireOD = 1.7; // wiring from pins to circuitry
    Gap = 5.0;
    // Cylindrical cell sizes
    // https://en.wikipedia.org/wiki/List_of_battery_sizes#Cylindrical_batteries
    CELL_NAME = 0;
    CELL_OD = 1;
    CELL_OAL = 2;
    CellData = [
    ["AAAA",8.3,42.5],
    ["AAA",10.5,44.5],
    ["AA",14.5,50.5],
    ["C",26.2,50],
    ["D",34.2,61.5],
    ["A23",10.3,28.5],
    ["CR123A",17.0,34.5],
    ["18650",18.8,65.2], // bare 18650 with button end
    ["18650Prot",19.0,70.0], // protected 18650 = 19670 plus a bit
    ];
    CellIndex = search([CellName],CellData,1,0)[0];
    echo(str("Cell index: ",CellIndex," = ",CellData[CellIndex][CELL_NAME]));
    //- Contact dimensions
    CONTACT_NAME = 0;
    CONTACT_WIDE = 1;
    CONTACT_HIGH = 2;
    CONTACT_THICK = 3; // plate thickness
    CONTACT_TIP = 4; // tip to rear face
    CONTACT_TAB = 5; // solder tab width
    ContactData = [
    ["AA+",12.2,12.2,0.3,1.7,3.5], // pos bump
    ["AA-",12.2,12.2,0.3,5.0,3.5], // half-compressed neg spring
    ["AA+-",28.2,12.2,0.3,5.0,0], // pos-neg bridge
    ["Li+",18.5,16.0,0.3,2.8,5.5],
    ["Li-",18.5,16.0,0.3,6.0,5.5],
    ];
    function ConDat(name,dim) = ContactData[search([name],ContactData,1,0)[0]][dim];
    ContactRecess = 2*ConDat(str(CellName,"+"),CONTACT_THICK);
    ContactOC = CellData[CellIndex][CELL_OD];
    WireBay = 6.0; // room for wiring to contacts
    //- Wire struts
    StrutDia = 1.6; // AWG 14 = 1.6 mm
    StrutSides = 3*4;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    StrutBase = [StrutDia,StrutDia + 2*5*ThreadWidth, // ID = wire, OD = buildable
    FloorThick + CellData[CellIndex][CELL_OD]]; // base is flush with cell top
    //- Holder dimensions
    BatterySize = [CellData[CellIndex][CELL_OAL] + // cell
    ConDat(str(CellName,"+"),CONTACT_TIP) + // pos contact
    ConDat(str(CellName,"-"),CONTACT_TIP) – // neg contact
    2*ContactRecess, // sink into wall
    NumCells*CellData[CellIndex][CELL_OD],
    CellData[CellIndex][CELL_OD]
    ];
    echo(str("Battery space: ",BatterySize));
    CaseSize = [3*WallThick + // end walls + wiring partition
    BatterySize.x + // cell
    WireBay, // wiring bay
    2*WallThick + BatterySize.y,
    FloorThick + BatterySize.z
    ];
    BatteryOffset = (CaseSize.x – (2*WallThick +
    CellData[CellIndex][CELL_OAL] +
    ConDat(str(CellName,"-"),CONTACT_TIP))
    ) /2 ;
    ThumbRadius = 0.75 * CaseSize.z;
    StrutOC = [IntegerLessMultiple(CaseSize.x – 2*CornerRadius -2*StrutBase[OD],5.0),
    IntegerMultiple(CaseSize.y + StrutBase[OD],5.0)];
    StrutAngle = atan(StrutOC.y/StrutOC.x);
    echo(str("Strut OC: ",StrutOC));
    LidSize = [2*WallThick + WireBay + ConDat(str(CellName,"+"),CONTACT_THICK), CaseSize.y, FloorThick/2];
    //———————-
    // 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);
    }
    //– Overall case with origin at battery center
    module Case() {
    difference() {
    union() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(CaseSize.x/2 – CornerRadius),
    j*(CaseSize.y/2 – CornerRadius),
    0])
    cylinder(r=CornerRadius/cos(180/8),h=CaseSize.z,$fn=8); // cos() fixes undersize spheres!
    if (Struts)
    for (i = (Struts == 1) ? [-1,1] : -1) { // strut bases
    hull()
    for (j=[-1,1])
    translate([i*StrutOC.x/2,j*StrutOC.y/2,0])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[LENGTH],$fn=StrutSides);
    translate([i*StrutOC.x/2,0,StrutBase[LENGTH]/2])
    cube([2*StrutBase[OD],StrutOC.y,StrutBase[LENGTH]],center=true); // blocks for fairing
    for (j=[-1,1]) // hemisphere caps
    translate([i*StrutOC.x/2,
    j*StrutOC.y/2,
    StrutBase[LENGTH]])
    rotate(180/StrutSides)
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    }
    }
    translate([BatteryOffset,0,BatterySize.z/2 + FloorThick]) // cells
    cube(BatterySize + [0,0,Protrusion],center=true);
    translate([BatterySize.x/2 + BatteryOffset + ContactRecess/2 – Protrusion/2, // contacts
    0,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"+-"),CONTACT_WIDE),
    ConDat(str(CellName,"+-"),CONTACT_HIGH)
    ],center=true);
    translate([-(BatterySize.x/2 – BatteryOffset + ContactRecess/2 – Protrusion/2),
    ContactOC/2,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"+"),CONTACT_WIDE),
    ConDat(str(CellName,"+"),CONTACT_HIGH)
    ],center=true);
    translate([-(BatterySize.x/2 – BatteryOffset + ContactRecess/2 – Protrusion/2),
    -ContactOC/2,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"-"),CONTACT_WIDE),
    ConDat(str(CellName,"-"),CONTACT_HIGH)
    ],center=true);
    translate([-CaseSize.x/2 + WireBay/2 + WallThick, // wire bay
    0,
    BatterySize.z/2 + FloorThick + Protrusion/2])
    cube([WireBay,
    BatterySize.y,
    BatterySize.z + Protrusion
    ],center=true);
    for (j=[-1,1])
    translate([-(BatterySize.x/2 – BatteryOffset + WallThick/2), // contact tabs
    j*ContactOC/2,
    BatterySize.z + FloorThick – Protrusion])
    cube([2*WallThick,
    ConDat(str(CellName,"+"),CONTACT_TAB),
    (BatterySize.z – ConDat(str(CellName,"+"),CONTACT_HIGH))
    ],center=true);
    if (false)
    translate([0,0,CaseSize.z]) // finger cutout
    rotate([90,00,0])
    cylinder(r=ThumbRadius,h=2*CaseSize.y,center=true,$fn=22);
    if (Struts)
    for (i2 = (Struts == 1) ? [-1,1] : -1) { // strut wire holes and fairing
    for (j=[-1,1])
    translate([i2*StrutOC.x/2,j*StrutOC.y/2,FloorThick])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[ID],2*StrutBase[LENGTH],StrutSides);
    for (i=[-1,1], j=[-1,1]) // fairing cutaways
    translate([i*StrutBase[OD] + (i2*StrutOC.x/2),
    j*StrutOC.y/2,
    -Protrusion])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[OD],StrutBase[LENGTH] + 2*Protrusion,StrutSides);
    }
    }
    }
    module Lid() {
    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/cos(180/8),$fn=8);
    translate([0,0,-LidSize.z]) // remove bottom
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),2*LidSize.z],center=true);
    for (j=[-1,1]) // wire holes
    translate([0,j*LidSize.y/4,-Protrusion])
    PolyCyl(WireOD,2*LidSize.z,6);
    }
    }
    //——————-
    // Build it!
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "Build") {
    rotate(-90)
    translate([CaseSize.x/2 + Gap,0,0])
    Case();
    rotate(-90)
    translate([-LidSize.x/2 – Gap,0,0])
    Lid();
    }
    if (Layout == "Show") {
    Case();
    translate([-CaseSize.x/2 + LidSize.x/2,0,(CaseSize.z + Gap)])
    Lid();
    }

  • Round Soaker Hose Clamp

    Round Soaker Hose Clamp

    An aging round soaker hose sprang a leak large enough to gouge a crater under a tomato plant, so I conjured a short clamp from the longer round hose splints:

    Soaker Hose Clamp - round - installed
    Soaker Hose Clamp – round – installed

    The shiny stuff is the plastic backing on strips of silicone tape intended to prevent the high-pressure water from squirting through the porous 3D printed plastic. The fat drop hanging from the hose shows some leakage around the tape; an occasional drop is perfectly OK.

    The leak faces the round side of the bottom half of the clamp, which probably doesn’t make any difference.

    I hope the washers occupy enough of the minimal surface to render aluminum backing plates superfluous:

    Soaker Hose Clamp - round - kitted
    Soaker Hose Clamp – round – kitted

    Creating the 3D model required nothing more than shortening the original splint to 30 mm with two screws along each side. While I was at it, I had Slic3r make three clamps to put two in the Garden Dedicated Hydraulic Repair Kit for later use:

    Round Soaker Hose Splice - 30mm - Slic3r
    Round Soaker Hose Splice – 30mm – Slic3r

    Change two lines in the OpenSCAD code and it’s done.

    Also: clamps for flat soaker hoses.

  • Tour Easy Daytime Running Light: Second Fracture

    Tour Easy Daytime Running Light: Second Fracture

    While clearing some overhanging brush along the rail trail, I probably wedged a branch between the LC40 flashlight and the fairing:

    Fairing Flashlight Mount - brush clearing
    Fairing Flashlight Mount – brush clearing

    Aaaand twisted it enough to fracture the mount:

    Fairing Flashlight Mount - another fracture
    Fairing Flashlight Mount – another fracture

    A closer look shows the infill just ripped apart:

    Fairing Flashlight Mount - another failure - detail
    Fairing Flashlight Mount – another failure – detail

    I can’t be sure that’s what happened, because the mount actually failed several miles later, after I hit one of the potholes along Raymond Avenue. Fortunately, I saw it swinging away from the fairing, hanging by its last few threads, and managed to grab it before it vanished.

    Fairing Flashlight Mount - Catch a Falling Mount
    Fairing Flashlight Mount – Catch a Falling Mount

    I set Slic3r for 30% infill on the replacement, but the running light been riding my fairing for three years and seems strong enough under normal use.