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: Electronics Workbench

Electrical & Electronic gadgets

  • 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();
    }

  • Power Outage

    Power Outage

    Just before Tropical Storm Isaias rolled through, my hygrometer reached a new high:

    Pre-Isaias humidity
    Pre-Isaias humidity

    The National Weather Service reported 99% at the airport a few miles away, so the meter’s calibration seems about right.

    Shortly thereafter, the humidity dropped to the mid-70s as the wind picked up and, over the next few hours, falling branches took out vast swaths of Central Hudson’s electrical infrastructure. My little generator saved our refrigerator & freezer during 15 hours of outage; three days later, thousands of folks around us still have no power.

    A confluence of other events, none nearly so dramatic, will throttle my posting over the next two weeks.

    We’re OK and hope you’re OK, too …

  • EonSmoke Vape Debris

    EonSmoke Vape Debris

    Being the type of guy who uses metal bits & pieces, I thought this might be a useful aluminum rod:

    EonSmoke vape stick
    EonSmoke vape stick

    It turns out to be an aluminum tube holding a lithium cell and a reservoir of oily brown juice:

    EonSmoke - peeled open
    EonSmoke – peeled open

    The black plastic cap read “EonSmoke”, which led to a defunct website at the obvious URL. Apparently, EonSmoke went toes-up earlier this year after ten years of poisoning their customers, most likely due to “competitor litigation”.

    The black cap held what looks like a pressure switch:

    EonSmoke - switch
    EonSmoke – switch

    Suck on the icky end of the tube to activate the switch, pull air past the battery (?), pick up some toxic vapor around the heater, and carry it into your lungs:

    EonSmoke - reservoir heater
    EonSmoke – reservoir heater

    Maybe there’s a missing mouthpiece letting you suck on the icky end, activate the switch, pull vapor through the heater, and plate your lungs with toxic compounds. I admit certain aspects of my education have been sadly neglected.

    The lithium cell was down to 1.0 V, with no overdischarge protection and no provision for charging, so it’s a single-use item. I’m sure the instructions tell you to recycle the lithium cell according to local and state regulations, not toss it out the window of your car.

    I had to wash my hands so hard

  • NPN RGB Astable Multivibrator Timing Adjustment

    NPN RGB Astable Multivibrator Timing Adjustment

    Back in the beginning of July, I replaced the NP-BX1 battery in the RGB Piranha astable multivibrator with a 18650 lithium cell and a USB charge controller, then watched it blink for the next two weeks on the first charge:

    Astable - 10 11 12 uF tweak - 027
    Astable – 10 11 12 uF tweak – 027

    However, the blinks looked … odd and some poking around with a Tek current probe showed the red and blue astables had locked together, so they blinked in quick succession. Alas, I don’t have a scope shot to prove it.

    I built all three astables with the same parts, figuring the normal tolerance of electrolytic caps would make the astables run at slightly different rates, which they did at first.

    This being a prototype, I just soldered a 1 µF cap onto the blue channel’s existing 10 µF cap:

    Astable - 11 uF cap - detail
    Astable – 11 uF cap – detail

    You can barely make out the top of the additional 2.2 µF cap on the red channel, through the maze of components; now, they definitely have different periods.

    Aaaand the scope shot to prove it:

    Astable NPN - 10 11 12 uF tweak - 10 mA-div
    Astable NPN – 10 11 12 uF tweak – 10 mA-div

    The bottom trace shows the battery current at 10 mA/div. The first pulse, over on the left, has the red and blue LEDs firing in quick succession with some overlap, but they separate cleanly for their next pulses.

    You don’t want to build a battery-powered astable from NPN transistors, because the 8 mA current between blinks is murderously high. In round numbers, each of the three LEDs blinks twice a second for 30 ms at 20 mA, so they average 3.6 mA, less than half the current required to keep the astables running between blinks. Over the course of 14 days, the circuit drew 11.6 mA × 336 hr = 3900 mA·h until the protection circuit shut it down.

    The lead photo shows a harvested 18650 cell, but I started with a known-good Samsung 18650 cell rated at 2600 mA·h at a 0.2C = 520 mA rate to 2.75 V. It’s comforting to see more energy trickling out at a 0.005C rate!

    I must conjure a holder with contacts for an 18650 cell, support for a trio of 2N7000 MOSFET astables, and some kind of weird spider with the RGB Piranha LED on the top. Even a harvested 18650 cell should last a couple of months with a much longer blink period (500 ms is much too fast), less LED current (this one is shatteringly bright), and a lower average current.

    And, yeah, I’ve been misspelling “Piranha” for a while.

  • Harvesting 18650 Lithium Cells

    Harvesting 18650 Lithium Cells

    With a Dutchess County Household Hazardous Waste Collection Day coming up, I’ve been harvesting usable lithium cells from a variety of old batteries:

    Harvested 18650 3P lithium batteries
    Harvested 18650 3P lithium batteries

    The pile of 18650 3P blocks over on the right are some obviously corroded deaders and the lowest performers from these tests:

    Harvested 3P 18650 Packs
    Harvested 3P 18650 Packs

    I doubt they’ll get recycled, as there’s entirely too much overhead involved in dismantling boxes full of cells like these, but I hope they’ll get a decent burial somewhere.