Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
The rise and fall times for the LEDs on the over-the-top blinky taillight left a bit to be desired, at least if you were interested in short pulses:
VG 1193 mV – ID 50 mA-div – 1 ms PWM filter – overview
So I spliced an analog switch between the PWM filter and the gate, with inputs selecting ground and the PWM drive voltage:
Switched MOSFET gate – analog switch schematic
This being a prototype, that involved epoxying a SOT23-6 package atop the MOSFET, with flying wires all over the PCB:
Hall Effect PCB – MOSFET gate switch
It works pretty well:
VDS ILED 50 mA div – 200 50 150 25 mA
The top trace is the drain voltage and the bottom trace is the LED current at 50 mA/div.
The current transitions come out vertical at any sweep speed, even through the Tek Hall effect current probe. The pulses would be rectangular if I weren’t changing the current for each one.
The current feedback loop closes through the Arduino’s main loop, which includes a 1 ms delay after each PWM output change before measuring the LED current. As a result, each loop takes just under 2 ms, but, fortunately, ramping from 25 mA (in the last pulse) to 200 mA (in the first pulse) requires less than 10 PWM increments; you can see the stairsteps if you squint.
Next up: bump the PWM to 64 kHz (from 32 kHz) and rip out the pull-down resistor that used to hold the gate near ground when the output floated as an input. That should improve the output ripple caused by the MOSFET’s bias as a perfectly serviceable linear amplifier.
The control loop now fetches durations & currents from an array-of-structs, so I can customize each pulse. An obvious enhancement: remember the gate drive that produced a given current, then restore the corresponding value as the starting PWM setting for each pulse, so the loop begins closer to reality. The gate drive varies with temperature and suchlike, so it can’t be a compile-time constant, but maybe the startup routine could preload the array / list / cache with values taken from a “lamp test”.
Four close-together flashes repeating at about 2 Hz, even with two runt pulses, turn the LEDs into a real eye magnet…
The whole reason I got a 3D printer in the first place was to make things that would otherwise be too difficult or tedious by hand or on a CNC mill. Most of the things I make look like brackets and I don’t do sculptures … this stuff solves problems!
Being able to go from “I need a part shaped like that” to holding the thing in my hand a few hours (or, for complex designs, days) later is empowering. Being able to adjust a dimension by changing the source code and “recompiling” to get a new part is wonderful.
These five slides from the presentation show my answers to the question “Why would anyone want a 3D printer?” Clicky for more dots.
Things I Designed – 1Things I Designed – 2Things I Designed – 3Things I Designed – 4Things I Designed – 5
One of my headband magnifiers has a headlight above the brim, an incandescent flashlight bulb powered by a pair of AAA alkaline cells, that hasn’t worked well since the day I bought it. This being a time of finishing small projects, I finally tore it apart and discovered that the cells and contacts were in fine shape (!), the bulb (remember bulbs?) worked, the wiring was OK, but the switch was bad.
Magnifying headband – lamp switch
The switch body seems to be firmly anchored in place, so I pried that red base plate off in situ, un-bent the silver-plated (!) spring-contact-actuator, and reassembled it in reverse order. No pictures, as it took less time to do than to tell, but it now works perfectly… most likely, for the first time ever.
Stop squirming! This can be much more painful…
Magnifying headband – in action
I’m mildly tempted to hotwire the guts of a white LED flashlight into the thing, but that would require either another AA cell or a booster circuit and I’m not ready for that just yet.
The Whirlpool water heater anode rod is corroding nicely:
Whirlpool anode rod – 2014-04
The new GE water heater anode rod seems to be passivating:
GE anode rod – coated – 2014-04
There’s some corrosion up near the bolt head, so it’s not entirely asleep:
GE anode rod – bolt – 2014-04
I hammered the coating off the rod, scuffed the shiny parts with coarse sandpaper, wiped off the dust, and stuck it back in its socket. We’ll see what it looks like next year.
Both tanks flushed nicely without too much sediment.
Two 40 W incandescent bulbs in the front bathroom burned out within a few days of each other. Being that type of guy, I know that I installed this bulb nine years ago:
Bulb base – install date
The date is easier to read with the bulb in hand: 13 Feb 05. The (5 yrs) indicates the previous bulb in that socket lasted five years.
The other bulb date went in during March 09, so it survived only five years; the previous bulb lasted 6 years.
Even though 40 W incandescent bulbs are history, maybe I have enough spares on the shelf that the next owner can replace ’em with cheap LEDs.
This may not be science, but it does have numbers…
The knockoff Arduino Pro Mini I used for the strobe photography controller ran the default Blink sketch perfectly, but didn’t respond to its own Reset pushbutton. Probing the Reset line at pin 29 on the microcontroller showed that the pushbutton didn’t pull the +5 V line to ground, so the switch was broken, a trace was broken, or …
Touching the soldering iron to a switch pin caused the whole thing to pop loose. One glance at the pads tells you something’s badly wrong:
Arduino Pro Mini Knockoff – cold solder joints
A closeup, rotated a quarter-turn clockwise:
Arduino Pro Mini Knockoff – cold solder joint – detail
That’s the nicest picture of cold solder joints you’ve seen in a while, isn’t it?
Resoldering the switch solved the problem and, while the iron was hot, I touched all the microcontroller pins, too, just in case…
Natural PLA provides a nice, crystalline appearance:
Kenmore 158 Sewing Machine – Cool white LEDs – rear no flash
Cool white LEDs have somewhat higher lumen/watt efficiency, but the real gain came from doubling the number of LEDs:
Kenmore 158 Sewing Machine – Cool white LEDs – front flash
I overvolted the warm white LEDs to 14 V to get closer to 20 mA/segment, but the cool white ones run pretty close to 20 mA at 12 V, so I didn’t bother.
Commercial versions of this hack secure the wiring with little white clips and foam tape, so I should conjure up something like that. Mary specifically did not want the lights affixed under the arm, though, so those things weren’t even in the running.
The OpenSCAD source code widens the mount and moves the wiring conduit a little bit, to simplify the connections to both strips, but is otherwise identical to the earlier version:
// LED Strip Lighting Brackets for Kenmore Model 158 Sewing Machine
// Ed Nisley - KE4ZNU - March 2014
Layout = "Build"; // Build Show Channels Strip
//- Extrusion parameters must match reality!
// Print with 2 shells and 3 solid layers
ThreadThick = 0.20;
ThreadWidth = 0.40;
HoleWindage = 0.2; // extra clearance
Protrusion = 0.1; // make holes end cleanly
AlignPinOD = 1.70; // assembly alignment pins: filament dia
inch = 25.4;
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
//----------------------
// Dimensions
Segment = [25.0,10.0,3.0]; // size of each LED segment
SEGLENGTH = 0;
SEGWIDTH = 1;
SEGHEIGHT = 2;
WireChannel = 3.0; // wire routing channel
StripHeight = 12.0; // sticky tape width
StripSides = 8*4;
DefaultLayout = [1,2,"Wire","NoWire"];
NUMSEGS = 0;
NUMSTRIPS = 1;
WIRELEFT = 2;
WIRERIGHT = 3;
EndCapSides = StripSides;
CapSpace = 2.0; // build spacing for endcaps
BuildSpace = 3.0; // spacing between objects on platform
//----------------------
// 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);
}
module ShowPegGrid(Space = 10.0,Size = 1.0) {
RangeX = floor(100 / Space);
RangeY = floor(125 / Space);
for (x=[-RangeX:RangeX])
for (y=[-RangeY:RangeY])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);
}
//-- The negative space used to thread wires into the endcap
module MakeWireChannel(Layout = DefaultLayout,Which = "Left") {
EndCap = [(2*WireChannel + 1.0),Layout[NUMSTRIPS]*Segment[SEGWIDTH],StripHeight]; // radii of end cap spheroid
HalfSpace = EndCap[0] * ((Which == "Left") ? 1 : -1);
render(convexity=2)
translate([0,Segment[SEGWIDTH]/2,0])
intersection() {
union() {
cube([2*WireChannel,WireChannel,EndCap[2]],center=true);
translate([-2*EndCap[0],0,EndCap[2]/2])
rotate([0,90,0]) rotate(180/6)
PolyCyl(WireChannel,4*EndCap[0],6);
}
translate([HalfSpace,0,(EndCap[2] - Protrusion)]) {
cube(2*EndCap,center=true);
}
}
}
//-- The whole strip, minus wiring channels
module MakeStrip(Layout = DefaultLayout) {
EndCap = [(2*WireChannel + 1.0),Layout[NUMSTRIPS]*Segment[SEGWIDTH],StripHeight]; // radii of end cap spheroid
BarLength = Layout[NUMSEGS] * Segment[SEGLENGTH]; // central bar length
hull()
difference() {
for (x = [-1,1]) // endcaps as spheroids
translate([x*BarLength/2,0,0])
resize(2*EndCap) rotate([0,90,0]) sphere(1.0,$fn=EndCapSides);
translate([0,0,-EndCap[2]])
cube([2*BarLength,3*EndCap[1],2*EndCap[2]],center=true);
translate([0,-EndCap[1],0])
cube([2*BarLength,2*EndCap[1],3*EndCap[2]],center=true);
}
}
//-- Cut wiring channels out of strip
module MakeMount(Layout = DefaultLayout) {
BarLength = Layout[NUMSEGS] * Segment[SEGLENGTH];
difference() {
MakeStrip(Layout);
if (Layout[WIRELEFT] == "Wire")
translate([BarLength/2,0,0])
MakeWireChannel(Layout,"Left");
if (Layout[WIRERIGHT] == "Wire")
translate([-BarLength/2,0,0])
MakeWireChannel(Layout,"Right");
}
}
//- Build it
ShowPegGrid();
if (Layout == "Channels") {
translate([ (2*WireChannel + 1.0),0,0]) MakeWireChannel(DefaultLayout,"Left");
translate([-(2*WireChannel + 1.0),0,0]) MakeWireChannel(DefaultLayout,"Right");
}
if (Layout == "Strip") {
MakeStrip(DefaultLayout);
}
if (Layout == "Show") {
MakeMount(DefaultLayout);
}
if (Layout == "Build") {
translate([0,(3*Segment[SEGWIDTH]),0]) MakeMount([1,2,"Wire","Wire"]); // rear left side, vertical
translate([0,0,0]) MakeMount([5,2,"Wire","NoWire"]); // rear top, across arm
translate([0,-(3*Segment[SEGWIDTH]),0]) MakeMount([6,2,"NoWire","Wire"]); // front top, across arm
}