High Impact Art(ifact)

At first we thought a mighty crunch in the morning meant the trash collection truck had dropped a garbage bin from a great height, but the sound of sirens and a myriad flashing lights revealed the true cause in our neighbor’s front yard:

NHR Crash - frontal view
NHR Crash – frontal view

The extent of the damage was more apparent from the road side:

NHR Crash - passenger side
NHR Crash – passenger side

Another one that ain’t gonna buff right out.

The driver was walking around uninjured and the ambulance left quietly.

A day later, the trajectory became apparent:

NHR Crash - trajectory
NHR Crash – trajectory

The right side barely kissed the tree on the right, but the front wheel hooked the utility pole (that’s the new pole in the picture), snapped it off at ground level in addition to the usual break maybe ten feet up, and bounced a piece off the other tree:

NHR Crash - utility pole
NHR Crash – utility pole

I didn’t know you could shatter a cast aluminum alloy wheel, but the missing half of the outer face was lying amid the rather scrambled stone wall along driveway.

We’re reasonably sure we know the cause. Feel free to draw your own conclusions.

After the flatbed hauled away the car and everybody left, I harvested a few pounds of interesting debris from the lawn:

NHR Crash - tempered glass
NHR Crash – tempered glass

It’s tempered glass from the driver-side windows, shattered into small chunks and barely hanging together in those sheets. Laminated windshield glass is entirely different stuff.

The smaller chunks glitter like jewels:

NHR Crash - tempered glass fragments
NHR Crash – tempered glass fragments

Obviously, the window had a bit of tint.

The smallest chunk, seen from its flat surface, shows the cuboid fragments:

NHR Crash - tempered glass fragment - front
NHR Crash – tempered glass fragment – front

A side view shows more complexity:

NHR Crash - tempered glass fragment - side
NHR Crash – tempered glass fragment – side

Tempering prevents a glass sheet from shattering into long knife-blade shards. Although the edges of the fragments are not keen, we are dealing with broken glass: they are sharp.

How sharp? They make glass knives for slicing eyes and cells.

Broken tempered glass also sheds razor-edged flakes perfectly shaped to penetrate bike tires, although most roadside glass comes from ordinary beverage bottles. The tiniest flakes can make a mess of your eyes, so exercise at least some rudimentary shop safety practices.

Those slabs ought to be good for something, even if they fall apart at the slightest touch …

Vacuum Tube Lights: Urethane Coated Plate Cap

With a generous dollop of JB Plastic Bonder left over from a set of Bafang brake sensor magnets, I tried coating the ersatz plate cap of a triode tube:

Triode - urethane coated plate cap
Triode – urethane coated plate cap

That’s the result after leaving it hanging upside-down while it cured to push all the drips to the top.

For comparison, the uncoated cap back in the day:

Triode - plate cap plug
Triode – plate cap plug

Seeing as how the urethane is an adhesive, not a coating, I’d say it looks about as bad as expected.

As with all 3D printed things, one must embrace imperfections and striations, rather than endlessly strive for perfection.

Now, if I had a resin printer …

Discrete LM3909: Blue LED

Once again, the discrete LM3909 circuitry can blink a blue LED while running a pair of alkaline cells all the way down to about 1 V, with one cell ending at 0.2 V and the other at 0.8 V. They started out discharged to 1.2 V each during their useful life, then blinked for a month; it’s as good a use for dead cells as I can think of.

With another pair of not-dead-yet cells providing 2.4 V, it started up again:

Blue LM3909 2.4V alkaline - 042
Blue LM3909 2.4V alkaline – 042

That’s a frame from a short video taken in subdued light, just to show it really does work.

Astable Multivibrator: Red RGB Piranha

A red LED has a sufficiently low forward voltage to run with a MOSFET astable multivibrator and a pair of run-down AA alkaline cells:

Astable AA Alkaline - red
Astable AA Alkaline – red

The red LED is actually part of an RGB Piranha, just to see how it compares to an as-yet-unbuilt version with a single red LED in the same package.

The LED drops 1.9 V of the 2.75 V from the mostly used-up AA cells:

Astable Piranha Red - 2.75 alkaline - V LED
Astable Piranha Red – 2.75 alkaline – V LED

The original 33 Ω ballast resistor showed a peak current of 11 mA in a 30 ms pulse:

Astable Piranha Red - 2.75 alkaline - V 33 ohm
Astable Piranha Red – 2.75 alkaline – V 33 ohm

Replacing it with a 12 Ω resistor boosts the current all the way to 12 mA:

Astable Piranha Red - 2.75 alkaline - V 12 ohm
Astable Piranha Red – 2.75 alkaline – V 12 ohm

The 2N7000 gate sees a just bit more than 2 V, barely enough to get the poor thing conducting, which makes the ballast resistor mostly decorative. The MOSFET datasheet puts its 1 mA threshold somewhere between 0.8 and 3 V, so it could be worse.

Keep in mind the DSO150’s 1 MΩ input impedance sat in parallel with the 1 MΩ gate pulldown resistor forming the RC differentiator when I measured the gate voltage; I’ll leave the simulation as an exercise for the interested reader. The blinks were noticeably dimmer and perhaps a bit shorter, although eyeballometric calibration is notoriously hard.

The slightly revised schematic-layout doodle stacks the transistors along the negative bus bar:

Astable wiring layout - stacked 2N7000
Astable wiring layout – stacked 2N7000

Flipping the bottom transistor over to snuggle the two timing caps next to each other would eliminate the long jumper wire and probably look better.

Astable Multivibrator: Amber LED

Adding an amber LED to the collection:

Astable AA - Amber - overview
Astable AA – Amber – overview

Because a yellow / amber LED runs at a lower voltage than blue and green LEDs, it sits atop an astable multivibrator, rather than a discrete LM3909. The battery holder has a pair of carbon-zinc “Extra-Heavy Duty” AAA cells, so corrosion and leakage pose a foreseeable hazard.

The voltage across the 100 Ω LED ballast indicates a 9 mA peak LED current, which is somewhat dim in ordinary room light:

Astable AA - Amber - LED current 100 ohm
Astable AA – Amber – LED current 100 ohm

The corresponding LED voltage says the LED runs at 2.1 V for that much current:

Astable AA - Amber - LED V
Astable AA – Amber – LED V

Something around 39 Ω should make it more visible.

Astable Multivibrator: Dressed-up LED Spider

Adding a bit of trim to the bottom of the LED spider makes it look better and helps keep the strut wires in place:

Astable Multivibrator - Alkaline - Radome trim
Astable Multivibrator – Alkaline – Radome trim

It’s obviously impossible to build like that, so it’s split across the middle of the strut:

Astable Multivibrator - Alkaline - Radome trim
Astable Multivibrator – Alkaline – Radome trim

Glue it together with black adhesive and a couple of clamps:

LED Spider - glue clamping
LED Spider – glue clamping

The aluminum fixtures (jigs?) are epoxied around snippets of strut wire aligning the spider parts:

LED Spider - gluing fixture
LED Spider – gluing fixture

Those grossly oversized holes came pre-drilled in an otherwise suitable aluminum rod from the Little Tray o’ Cutoffs. I faced off the ends, chopped the rod in two, recessed the new ends, and declared victory. Might need better ones at some point, but they’ll do for now.

Next step: wire up an astable with a yellow LED to go with the green and blue boosted LEDs.

Discrete LM3909: Blue LED Radome

Dropping a simplified ping-pong ball radome for a Piranha RGB LED atop a discrete LM3909 on the AA alkaline cell holder:

Discrete LM3909 Radome - AA alkaline
Discrete LM3909 Radome – AA alkaline

The solid model has screw holes for the lid and the revised LED spider:

Astable Multivibrator - Alkaline AA Base - radome - solid model
Astable Multivibrator – Alkaline AA Base – radome – solid model

The RGB LED needs only two wires, as the LM3909 circuit can blink only one LED. I tried all three colors, but only blue and green justify the LM3909 hairball; red can get along with the astable circuit.

The LED wires connect across a 1 MΩ resistor serving as a mechanical strut between the 9.1 kΩ resistor on the left and the 10 Ω ballast resistor on the right.

Fresh alkaline cells at 3.0 V put 3.3 V across the blue LED with a 37 mA peak current. Older cells at 2.3 V produce 2.9 V at 15 mA. Dead cells at 1.9 V still fire the LED with 2.7 V at 4.2 mA, although the flash is barely visible in ordinary room light.

The lovely blue ball looks better in person!

The OpenSCAD source code as a GitHub Gist:

// Astable Multivibrator
// Holder for Alkaline cells
// Ed Nisley KE4ZNU August 2020
// 2020-09 add LED radome
/* [Layout options] */
Layout = "Build"; // [Build,Show,Lid,Spider]
/* [Hidden] */
CellName = "AA"; // [AA] -- does not work with anything else
NumCells = 2; // [2] -- likewise
Struts = -1; // [0:None, -1:Dual, 1:Quad] -- Quad is dead
// Extrusion parameters
/* [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.5; // battery & LED wiring
WireOC = 4;
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;
// FIXME search() needs special-casing to properly find AAA and AAAA
// Which is why CellName is limited to AA
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]]; // LENGTH = 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];
LidScrew = [2.0,3.8,7.0]; // M2 pan head screw (LENGTH = threaded)
LidScrewOC = CaseSize.y/2 - CornerRadius - LidScrew[OD]; // allow space around screw head
//- Piranha LEDs
PiranhaBody = [8.0,8.0,8.0]; // Z = heatsink fins + body + lens height
PiranhaPin = 0.0; // trimmed pin length beyond heatsink
PiranhaPinsOC = [5.0,5.0]; // pin XY distance
PiranhaRecess = PiranhaBody.z + PiranhaPin/2; // minimum LED recess depth
BallOD = 40.0; // radome sphere
BallSides = 4*StrutSides; // nice smoothness
BallPillar = [norm([PiranhaBody.x,PiranhaBody.y]), // ID
norm([PiranhaBody.x,PiranhaBody.y]) + 3*WallThick, // OD
StrutBase[OD] + PiranhaBody.z]; // height to base of chord
echo(str("Pillar OD: ",BallPillar[OD]));
BallChordM = BallOD/2 - sqrt(pow(BallOD/2,2) - (pow(BallPillar[OD],2))/4);
echo(str("Ball chord depth: ",BallChordM));
//----------------------
// 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);
}
// Spider for single LED atop struts, with the ball
module DualSpider() {
difference() {
union() {
for (j=[-1,1]) {
translate([0,j*StrutOC.y/2,StrutBase[OD]/2])
rotate(180/StrutSides)
sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
translate([0,j*StrutOC.y/2,0])
rotate(180/StrutSides)
cylinder(d=StrutBase[OD],h=StrutBase[OD]/2,$fn=StrutSides);
}
translate([0,0,StrutBase[OD]/4]) // connecting bars
cube([StrutBase[OD]*cos(180/StrutSides),StrutOC.y,StrutBase[OD]/2],center=true);
cylinder(d=BallPillar[OD],h=BallPillar[LENGTH],$fn=BallSides);
}
for (j=[-1,1]) // strut wires
translate([0,j*StrutOC.y/2,-Protrusion])
PolyCyl(StrutBase[ID],StrutBase[OD]/2,6);
for (n=[-1,1]) // LED wiring
rotate(n*90)
translate([StrutOC.x/3,0,-Protrusion])
PolyCyl(StrutBase[ID],StrutBase[OD],6);
translate([0,0,BallOD/2 + BallPillar[LENGTH] - BallChordM]) // ball inset
sphere(d=BallOD);
translate([0,0,BallPillar.z - PiranhaRecess + BallPillar.z/2]) // LED inset
cube(PiranhaBody + [HoleWindage,HoleWindage,BallPillar.z],center=true); // XY clearance
translate([0,0,StrutBase[OD]/2 + WireOD/2 + 0*Protrusion]) // wire channels
cube([WireOD,BallPillar[OD] + 2*WallThick,WireOD],center=true);
}
}
//-- Overall case with origin at battery center
module Case() {
union() {
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 with screw bosses
0,
BatterySize.z/2 + FloorThick + Protrusion/2])
cube([WireBay,
2*LidScrewOC - LidScrew[ID] - 2*4*ThreadWidth,
BatterySize.z + Protrusion
],center=true);
for (j=[-1,1]) // screw holes
translate([-CaseSize.x/2 + WireBay/2 + WallThick,
j*LidScrewOC,
CaseSize.z - LidScrew[LENGTH] + Protrusion])
PolyCyl(LidScrew[ID],LidScrew[LENGTH],6);
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);
translate([0,0,ThreadThick - Protrusion]) // recess around name
cube([0.6*CaseSize.x,8,2*ThreadThick],center=true);
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);
}
}
translate([0,0,0])
linear_extrude(height=2*ThreadThick + Protrusion,convexity=10)
mirror([0,1,0])
text(text="KE4ZNU",size=6,spacing=1.20,font="Arial:style:Bold",halign="center",valign="center");
}
}
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*WireOC,-Protrusion])
PolyCyl(WireOD,2*LidSize.z,6);
for (j=[-1,1])
translate([0,j*LidScrewOC,-Protrusion])
PolyCyl(LidScrew[ID],2*LidSize.z,6);
}
}
//-------------------
// Build it!
if (Layout == "Case")
Case();
if (Layout == "Lid")
Lid();
if (Layout == "Spider")
if (Struts == -1)
DualSpider();
else
cube(10,center=true);
if (Layout == "Build") {
rotate(90)
Case();
translate([0,-(CaseSize.x/2 + LidSize.x/2 + Gap),0])
rotate(90)
Lid();
if (Struts == -1)
translate([CaseSize.x/2,0,0])
DualSpider();
}
if (Layout == "Show") {
Case();
translate([-CaseSize.x/2 + LidSize.x/2,0,(CaseSize.z + Gap)])
Lid();
}