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

  • Auto Parking Light LED Bulbs: FAIL

    Auto Parking Light LED Bulbs: FAIL

    After about eight years and a similar failure last year, this came as no surprise:

    White W5W Parking Light - failed chips
    White W5W Parking Light – failed chips

    It’s a W5W “parking light” in the same fixture as the melty halogen high-beam bulbs (used as daytime running lights at half power), so it gets toasted on those occasions when we drive somewhere.

    The adhesive holding the LED strip to the aluminum shell fossilized and came loose:

    White W5W Parking Light - failed adhesive
    White W5W Parking Light – failed adhesive

    Now that I know what to look for, I’d get LED bulbs with chips soldered directly to the PCB, although it’s not obvious what holds the PCB to the aluminum frame.

    I reinstalled the original incandescent bulbs.

  • LED Garage Light: Desk Lamp Upcycling

    LED Garage Light: Desk Lamp Upcycling

    One of the heatsink panels from the defunct LED garage light now casts a uniform warm-white glow on my desk:

    LED Garage Light - desk light
    LED Garage Light – desk light

    A PCB intended as a lithium battery charger serves as a constant-current supply:

    LED Garage Light - constant current driver
    LED Garage Light – constant current driver

    The three trimpots, from left to right:

    • Constant-voltage limit adjustment
    • Full-charge current setpoint (irrelevant here)
    • Constant-current limit adjustment

    The as-received trimpot settings will be wildly inappropriate for a nominal 10 W COB LED array, so:

    • Connect the output to about 10 Ω of power resistors
    • … with an ammeter in series
    • Connect the input to a 12 VDC / 1-ish A wall wart
    • Adjust the output voltage to 10 V
    • Adjust the output current to 900 mA

    As long as the voltage limit is over about 10 V, it will (likely) never matter, as the LED forward drop doesn’t vary much with temperature. Setting it to something sensible keeps it out of the way.

    The middle trimpot apparently sets a voltage for a comparator to light an LED when the battery current drops below that level as it reaches full charge.

    Although the regulator touts its high efficiency, it does run hot and a heatsink seemed in order:

    LED Garage Light - heatsink
    LED Garage Light – heatsink

    Stipulated: the fins run the wrong way and it’s sitting in the updraft from the main heatsink. It’s Good Enough™.

    The switch on the top comes from the collection of flashlight tailcap switches and controls the 12 V input power. It’s buried up to its button in a generous dollop of JB Kwik epoxy, which seemed the least awful way to get that done.

    The solid model looks about like you’d expect:

    LED Lamp Driver case - switch housing - show solid model
    LED Lamp Driver case – switch housing – show solid model

    The OpenSCAD code exports the (transparent) lid as an SVG so I can import it into LightBurn and laser-cut some thin acrylic. Two tape snippets hold the lid in place pending more power-on hours, after which I’ll apply a few dots of cyanoacrylate adhesive and call it done.

    The case builds in two pieces that glue together to avoid absurd support structures:

    LED Lamp Driver case - switch housing - build solid model
    LED Lamp Driver case – switch housing – build solid model

    A 3D printed adapter goes between the desk lamp arm and the lamp heatsink bolt:

    LED Lamp Driver case - arm adapter - solid model
    LED Lamp Driver case – arm adapter – solid model

    The OpenSCAD source code files for the case and adapter arm as a GitHub Gist:

    // LED Lamp arm adapter
    // Ed Nisley – KE4ZNU
    // 2026-03-18
    include <BOSL2/std.scad>
    Layout = "Adapter"; // [Show,Build,ArmClamp,SinkClamp,Adapter]
    /* [Hidden] */
    HoleWindage = 0.2;
    Protrusion = 0.01;
    Gap = 5.0;
    $fn=5*3*4;
    HoleOC = 45.0;
    ArmRad = 7.5;
    ArmWidth = 11.3;
    SinkOD = 11.5;
    SinkThick = 3.2;
    SinkOC = 20.0;
    ClampThick = 5.0; // outside sink, watch thinning due to hull()
    // Define things
    // Screw & bushings in lamp arm bracket
    // … over-long bushings to prevent coincident surfaces
    module ArmClamp() {
    BushingThick = 1.5;
    BushingOD = 9.0;
    union() {
    ycyl(ArmWidth,d=4.0 + HoleWindage); // central M4 screw
    for (j=[-1,1]) {
    back(j*(ArmWidth – BushingThick + Protrusion)/2)
    ycyl(BushingThick + Protrusion,d=BushingOD);
    back(j*(ArmWidth + 10)/2)
    cuboid([2*ArmRad,10,2*ArmRad]);
    }
    }
    }
    module SinkClamp() {
    union() {
    ycyl(2*SinkOC,d=6.0 + HoleWindage); // central M6 screw
    for (j=[-1,1])
    back(j*SinkOC/2) {
    ycyl(SinkThick + Protrusion,d=SinkOD);
    cuboid([SinkOD,SinkThick + Protrusion,2*SinkOD]);
    }
    }
    }
    module Adapter() {
    difference() {
    hull() {
    right(HoleOC)
    ycyl(ArmWidth,r=ArmRad);
    ycyl(SinkOC + SinkThick + 2*ClampThick,d=SinkOD);
    }
    right(HoleOC)
    ArmClamp();
    SinkClamp();
    }
    }
    // Build it
    if (Layout == "ArmClamp")
    ArmClamp();
    if (Layout == "SinkClamp")
    SinkClamp();
    if (Layout == "Adapter")
    Adapter();
    if (Layout == "Build")
    up(SinkOD/2)
    yrot(-atan((ArmRad – SinkOD/2)/HoleOC))
    Adapter();
    // LED Constant-current driver case
    // Ed Nisley – KE4ZNU
    // 2026-03-15
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build,Case,Lid,LidSVG,Switch]
    /* [Hidden] */
    ThreadThick = 0.2;
    HoleWindage = 0.2;
    Protrusion = 0.01;
    Gap = 5.0;
    WallThick = 1.8;
    TapeThick = 1.5;
    DriverOA = [48.5,13.5 + TapeThick,23.5]; // PCB forward Y, pots along top to rear
    SinkOA = [31.5,12.0,15.5]; // fins forward
    SinkOffset = [(DriverOA.x – SinkOA.x)/2,0,2.0]; // from lower left front corner of PCB
    AdjPots = [14,24,34]; // screwdriver adjust offsets
    AdjOD = 3.0; // … access hole dia
    CaseOA = DriverOA + [2*WallThick,2*WallThick,2*WallThick];
    echo(CaseOA=CaseOA);
    LidOA = [CaseOA.x – WallThick,CaseOA.z – WallThick,1.0];
    Cables = [8.0,3.0 + WallThick/2,LidOA.z];
    SwitchWireOC = DriverOA.x – 6.0;
    SwitchCapBase = [DriverOA.x + WallThick,DriverOA.y + WallThick];
    SwitchCapTop = [DriverOA.x,12.0];
    SwitchCavity = [25.0,10.5,5.5];
    // Define things
    module Lid() {
    difference() {
    cuboid(LidOA,anchor=BOTTOM+FWD+LEFT);
    for (i = AdjPots)
    translate([i,LidOA.y – AdjOD/2 – WallThick/2,-Protrusion])
    cyl(LidOA.z + 2*Protrusion,d=AdjOD,anchor=BOTTOM,$fn=8,spin=180/8);
    translate([LidOA.x/2,-Protrusion,-Protrusion])
    cuboid(Cables + [0,Protrusion,2*Protrusion],rounding=1.0,edges=[BACK+LEFT,BACK+RIGHT],anchor=BOTTOM+FWD);
    }
    }
    module SwitchBox() {
    difference() {
    prismoid(SwitchCapBase,SwitchCapTop,SwitchCavity.z,anchor=BOTTOM);
    down(Protrusion)
    cuboid(SwitchCavity + [0,0,2*Protrusion],anchor=BOTTOM);
    hull()
    for (i=[-1,1])
    right(i*SwitchWireOC/2)
    zcyl(CaseOA.z,d=3.0,$fn=8,spin=180/8);
    }
    }
    module Case() {
    difference() {
    cuboid(CaseOA,chamfer=WallThick/2,anchor=BOTTOM+FWD+LEFT);
    translate([WallThick,WallThick + Protrusion,WallThick])
    cuboid(DriverOA + [0,WallThick + Protrusion,0],anchor=BOTTOM+FWD+LEFT);
    translate(SinkOffset + [WallThick,WallThick + 2*Protrusion,WallThick])
    cuboid(SinkOA,anchor=BOTTOM+BACK+LEFT);
    for (i=[-1,1])
    translate([i*SwitchWireOC/2 + CaseOA.x/2,CaseOA.y/2,CaseOA.z/2])
    zcyl(CaseOA.z,d=2.0,anchor=BOTTOM,$fn=8,spin=180/8);
    translate([WallThick/2,(CaseOA.y + LidOA.z),WallThick/2])
    xrot(90)
    scale([1,1,2])
    Lid();
    }
    }
    // Build it
    if (Layout == "Switch")
    SwitchBox();
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "LidSVG")
    projection(cut=true)
    Lid();
    if (Layout == "Show") {
    Case();
    translate(SinkOffset + [WallThick,WallThick + 2*Protrusion,WallThick])
    color("Gray",0.7)
    cuboid(SinkOA,anchor=BOTTOM+BACK+LEFT);
    translate([CaseOA.x/2,CaseOA.y/2,CaseOA.z])
    SwitchBox();
    translate([WallThick/2,CaseOA.y,WallThick/2])
    xrot(90)
    color("Gray",0.7)
    Lid();
    }
    if (Layout == "Build") {
    fwd(Gap)
    xrot(90)
    Case();
    translate([CaseOA.x/2,(Gap + CaseOA.y/2),0])
    SwitchBox();
    }
  • Magnetic Stirrer: Interior

    Magnetic Stirrer: Interior

    Of late, the magnetic stirrer mixing my morning cocoa occasionally doesn’t start spinning when I turn it on, which calls for some investigation.

    Removing the four obvious screws concealed under the rubber feet and prying off bottom cover reveals the trivial innards:

    Magnetic stirrer - interior
    Magnetic stirrer – interior

    The speed adjustment pot holds the little circuit board in place, with the green LED setting its jaunty angle.

    The motor spins a pair of neodymium magnets:

    Magnetic stirrer - magnet holder
    Magnetic stirrer – magnet holder

    I expected a gearbox instead of the direct drive setup.

    Perhaps those whirling neodymium magnets have been slowly demagnetizing the motor’s internal (alnico?) magnets.

    The motor brushes seem to be a pair of stiff wires, rather than carbon blocks, contacting the commutator, the wear from which may account for motor’s decreasing startup enthusiasm. Even though I didn’t expect a BLDC motor, this one may have been overly cheapnified.

    Perhaps kickstarting the motor with the steel fork I use to fish the stirrer magnet out of the mug will get the thing going.

  • LED Garage Light: Autopsy

    LED Garage Light: Autopsy

    The hidden part of all three LED arrays in the dead garage light looked like this:

    LED Garage Light - inadequate heatsink compound
    LED Garage Light – inadequate heatsink compound

    Although the compound was still gooey, there wasn’t nearly enough of it. The few tendrils on the heatsink suggest the LED array had bowed upward, pulled away from the cast aluminum, and eliminated any direct conduction.

    A bit of probing showed each LED array had 16 series groups of 4 parallel LEDS, with one group in each array failed open. That group was toward the end away from the inadequate heatsink compound: the LEDs died from heatstroke brought on by neglect.

    The Drawer o’ LED Arrays disgorged a bag of surplus LEDs labeled “10 W 9-12 V 750 mA”:

    LED Garage Light - epoxy replacement
    LED Garage Light – epoxy replacement

    It’s sitting on a generous blob of steel-filled JB Kwik epoxy that should do a great job of conducting heat. A bag of cheap constant-current supplies is on order.

    Amazon has similar “10 W 9-12 V 350-450 mA” arrays.

    Try as I might, I can’t get 10 W from those numbers, but I’ve never understood advertising math.

  • LED Garage Light: FAIL

    LED Garage Light: FAIL

    A three-wing garage light Came With The House in the basement, where it served to light up the foot of the stairs. One of the 48 LEDs in one of the three LED panels began flickering brightly and, over the course of a few days, that panel went dark. The next time I turned on the basement lights, all three panels were dark.

    Removing the screw-in lamp base:

    LED Garage Light - overview
    LED Garage Light – overview

    A closer look inside:

    LED Garage Light - detail
    LED Garage Light – detail

    The middle of the PCB is darker than the perimeter, with the darkest area around the black inductor standing up near the green filter cap. A blackened lump on the solder side that may have once been an SMD resistor evidently served as a fuse.

    All three panels are in wired parallel, so the failed panel reduced the load on the supply, thus increasing the voltage on the remaining two panels enough to kill them off, too.

    Worth noting: the black wire goes to the positive side of the LED panel. You can just see the + mark near the two connectors on the left side.

    I wired each panel to a lashed-up bridge rectifier with a widowmaker extension cord from a variable transformer controlling the voltage, but none of them responded to the 150 VDC peaks: they’ve suffered Real Death.

    The electronics landed in the recycling box and the three heatsinks are now in the Big Box o’ Heatsinkery, where they will surely come in handy for something.

    The surprisingly readable 09/21 date code on the case says it’s just over four years old. Similar garage lights now run around ten bucks each and I wouldn’t expect them to last more than a couple of years.

  • Cheap HD USB Camera: Base Disassembly

    Cheap HD USB Camera: Base Disassembly

    A brace of cheap HD USB cameras may improve the scenery around here during video meetings. They were $16, marked down from an absurd $130:

    HD USB Camera price history
    HD USB Camera price history

    Some poor schlubs certainly dropped more than twice the price of a Genuine Logitech camera on these critters, but a nearly total lack of demand must have had some effect.

    They do take their stylin’ cues from Logitech, although the speckled pattern on a shiny plastic sheet is amusing:

    HD USB Camera - styling vs Logitech C920
    HD USB Camera – styling vs Logitech C920

    Unsurprisingly, the lens is fixed / manual focus. What looked like focus rings were in different positions on the two cameras:

    HD USB Camera - lens focus notches
    HD USB Camera – lens focus notches

    It turns out the rings were not glued in place, perhaps because they have absolutely no effect on the camera’s focus. Maybe there’s another camera model where they rotate the lens in a threaded socket, but this ain’t that.

    The front panel has three pores:

    • A red Power LED is always on when it’s plugged in
    • A green On the air LED lights up when the camera is selected; I have no idea what the WiFi-ish glyph is supposed to represent
    • The “advanced noise canceling microphone” sits behind a pore offscreen left; the claim seems dubious.

    Because these may go into smaller spaces, I dismantled the base to see what was involved. Most of the screws lie underneath thin foam sheets:

    HD USB Camera - ball mount interior
    HD USB Camera – ball mount interior

    The lower plate has a tripod mount and a folding bracket:

    HD USB Camera - baseplate interior
    HD USB Camera – baseplate interior

    The camera body has a ball mount with a few degrees of movment:

    HD USB Camera - ball mount detail
    HD USB Camera – ball mount detail

    Reassembled and stuck inside the laser cabinet with some good double-sided foam tape, it definitely produces a better image than the previous camera:

    Platform camera view
    Platform camera view

    Whatever noise cancellation the mic may provide is irrelevant in there: nobody’s listening.

  • ResMed ClimateLine Heated Hose: DOA

    ResMed ClimateLine Heated Hose: DOA

    Mary plugged a new ClimateLine heated hose into her Resmed Airsense 11 CPAP machine, spent the night feeling a bit chilly, and got an error message in the morning that boiled down to “Bad Hose”.

    Unsurprisingly, the new hose looks just like the previous ones and the old picture remains relevant:

    ResMed ClimateLine heated hose ends
    ResMed ClimateLine heated hose ends

    The new hose has the same 12 kΩ resistance between the two outer contacts: the thermistor is fine.

    The two inner contacts are an open circuit, not the expected 10 Ω: the heater element or (more likely) a connector joint failed. We don’t know if it was DOA or failed during the first use, but it does not respond to the usual wiggling and poking.

    Her experience with Lincare’s Customer Disservice has been so terrible she refuses to start a warranty claim. She’ll continue using the old hose until it’s time for the next replacement and we’ll hope for the best.

    As I understand the arrangement, she must get all the consumables (masks, hoses, filters, tanks) from Lincare for five years from the date of the original prescription. After that, she can order supplies from elsewhere, although that seller must have a new prescription.

    Basically, Lincare gets five years of guaranteed business and, like the phone company of old, they don’t care about you because they don’t have to.

    So: they win.