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

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

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

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

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

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.