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

  • USB Testers vs. Reality

    USB Testers vs. Reality

    Set up a test harness with a USB tester between two amputated USB cables:

    USB Testers - Keweisi plugged
    USB Testers – Keweisi plugged

    Input comes from a bench power supply:

    USB Testers - 5 V input clips
    USB Testers – 5 V input clips

    Output goes to an 8 Ω power resistor:

    USB Testers - 8 ohm load
    USB Testers – 8 ohm load

    Yes, it’s mounted on the isothermal block from back in the Thing-O-Matic days, disspating barely 2 W. Thermocouples FTW, but in this case I don’t care about the temperature.

    Because these are random USB cables, they come with the usual caveats. Indeed, I measured a 9.8 Ω loop resistance (!) at the input end. The resistor is slightly under 8Ω, so the 1.8 Ω wire resistance suggests (at least) one of the USB cables contains very little copper. That’s measured without the USB tester in series, because it tries really hard to power up from the ohmmeter’s source voltage and basically shorts out the resistor.

    Both testers accurately report the source voltage with no load, so I presume the voltage shown with current flowing through the resistor represents the actual voltage at the tester. The source cable drops a substantial voltage under load.

    The 8 Ω resistor should draw 5 V / 8 Ω = 625 mA at 5 V. The voltmeter probes provide a non-intrusive way to measure the actual current by working backwards: current = volts / 8 Ω. As it turned out, the resistor sees less than 4 V with the bench supply set to 5.0 V.

    So, we begin.

    With the bench supply at 5.5 V, the Keweisi meter shows 4.9 V and 0.48 A with 4.0 V across the resistor for an actual 500 mA current. The source cable drops 600 mV, indicating a wire resistance of 1.2 Ω, about 2/3 of the total wire resistance.

    The anonymous meter produces two different results for an actual 500 mA current, depending on nothing under my control:

    • Supply 5.3 V, indicated 5.0 V and 0.5 A
    • Supply 5.1 V, indicated 4.76 V and 0.5 A

    Both results show about 300 mV source drop, half of the Keweisi meter’s 600 mV, suggesting a wire resistance of 0.6 Ω. The meter displays a blinking ▲, presumably indicating the input voltage is kinda high.

    I have no explanation.

    After the meters measured an actual 500 mA load for about an hour:

    • Keweisi: 1.6 hr → 782 mA·h, should be 800 mA·h
    • Anonymous: 1.0 hr → 515 mA·h, should be 500 mA·h

    Which roughly agrees with the battery charge data:

    USB Testers - Charge vs Runtime
    USB Testers – Charge vs Runtime

    The anonymous meter seems reasonably accurate, the Keweisi meter undershoots by 2.5%, and they’re both Close Enough for simple measurements.

    There’s probably a duty cycle effect, too, because the battery charger presents a pulsed load, but I’m just not going to worry about it.

  • More WS2812 Failures

    More WS2812 Failures

    Even though I’m using what seem to be good-quality parts, one of the WS2812 RGB LEDs in a Glass Tile frame died:

    Glass Tile - 2x2 - first WS2812B failure
    Glass Tile – 2×2 – first WS2812B failure

    It passed the Josh Sharpie Test:

    Glass Tile - WS2812 failure - PCB unknown
    Glass Tile – WS2812 failure – PCB unknown

    After building the third Glass Tile unit, one of the LEDs didn’t light up due to an easily diagnosed problem:

    Glass Tile - WS2812 failure - PCB cold solder - as found
    Glass Tile – WS2812 failure – PCB cold solder – as found

    A closer look:

    Glass Tile - WS2812 failure - PCB cold solder
    Glass Tile – WS2812 failure – PCB cold solder

    Shortly thereafter, the Nissan Fog Lamp developed an obvious beam problem:

    Nissan Fog Lamp - failed WS2812 effect
    Nissan Fog Lamp – failed WS2812 effect

    The WS2812 had the proper voltages / signals at all its pins and was still firmly stuck to the central “heatsink”:

    Nissan Fog Lamp - failed WS2812 detail
    Nissan Fog Lamp – failed WS2812 detail

    It also passed the Josh Sharpie Test:

    Glass Tile - WS2812 failure - tape - unknown
    Glass Tile – WS2812 failure – tape – unknown

    I’m particularly surprised by this one, because eleven of the twelve flex-PCB WS2812s in the Hard Drive Platter light have been running continuously for years with no additional failures.

    The alert reader will note the common factor: no matter what substrate the LED is (supposed to be) soldered to, no matter when I bought it, no matter what it’s wired into, a WS2812 will fail.

    They’re all back in operation:

    Glowing Algorithmic Art
    Glowing Algorithmic Art

    Although nobody knows for how long …

    Obviously, it’s time to refresh my programmable RGB LED stockpile!

  • Monthly Science: USB Current Testers vs. NP-BX1 Batteries

    Monthly Science: USB Current Testers vs. NP-BX1 Batteries

    Having some interest in my Sony HDR-AS30 helmet camera’s NP-BX1 battery runtime, I’ve been measuring and plotting recharge versus runtime after each ride:

    USB Testers - Charge vs Runtime
    USB Testers – Charge vs Runtime

    The vertical axis is the total charge in mA·h, the horizontal axis is the discharge time = recorded video duration. Because 1 A = 1 coulomb/s, 1 mA·h = 3.6 C.

    The data points fall neatly on two lines corresponding to a pair of cheap USB testers:

    USB Testers
    USB Testers

    When you have one tester, you know the USB current. When you have two testers, you’re … uncertain.

    The upper tester is completely anonymous, helpfully displaying USB Tester while starting up. The lower one is labeled “Keweisi” to distinguish it from the myriad others on eBay with identical hardware; its display doesn’t provide any identifying information.

    The back sides reveal the current sense resistors:

    USB Testers - sense resistors
    USB Testers – sense resistors

    Even the 25 mΩ resistor drops enough voltage that the charger’s blue LED dims appreciably during each current pulse. The 50 mΩ resistor seems somewhat worse in that regard, but eyeballs are notoriously uncalibrated optical sensors.

    The upper line (from the anonymous tester) has a slope of 11.8 mA·h/minute of discharge time, the lower (from the Keweisi tester) works out to 8.5 mA·h/minute. There’s no way to reconcile the difference, so at some point I should measure the actual current and compare it with their displays.

    Earlier testing suggested the camera uses 2.2 W = 600 mA at 3.7 V. Each minute of runtime consumes 10 mA·h of charge:

    10 mA·h = 600 mA × 60 s / (3600 s/hour)

    Which is in pretty good agreement with neither of the testers, but at least it’s in the right ballpark. If you boldly average the two slopes, it’s dead on at 10.1 mA·h/min; numerology can produce any answer you need if you try hard enough.

    Actually, I’d believe the anonymous meter’s results are closer to the truth, because recharging a lithium battery requires 10% to 20% more energy than the battery delivered to the device, so 11.8 mA·h/min sounds about right.

    Memo to Self: Trust, but verify.

  • Glass Tiles: 2×2 Matrix

    Glass Tiles: 2×2 Matrix

    Start with a single cell holding a glass tile over a WS2812 RGB LED:

    Glass Tile - 1x1 cell test - purple phase
    Glass Tile – 1×1 cell test – purple phase

    A bit of OpenSCAD tinkering produces a simple 2×2 array with square interiors as a test piece:

    Glass Tile - 2x2 - PETG strings
    Glass Tile – 2×2 – PETG strings

    The excessive stringing and the booger in the upper-left cell come from absurdly thin infill tucked into the too-thin walls; Slic3r doesn’t (seem to) have a “minimum infill width” setting and it’ll desperately try to fit infill between two nearly adjacent perimeter threads.

    The little support spiders under the LED PCB recesses snapped right out, though, so I got that part right:

    Glass Tile - 2x2 - support spiders
    Glass Tile – 2×2 – support spiders

    The perimeter threads around the LED aperture aren’t quite fused, because it was only one layer thick and that’s not enough.

    A quick test with two LEDs showed the white PETG let far too much light bleed between the cells, which was no surprise from the single cell test piece.

    Fortunately, it’s all parametric, so a bit more tinkering produced a slightly chunkier matrix with a base for an Arduino Nano and M3 threaded brass inserts for the screws holding it together:

    Glass Tile Frame - 2x2 - Arduino Nano base - solid model
    Glass Tile Frame – 2×2 – Arduino Nano base – solid model

    Those two parts require about three hours of printing, much faster than I could produce them by milling pockets into aluminum or black acrylic slabs, and came out with minimal stringing.

    A little cleanup, some epoxy work, and a few dabs of solder later:

    Glass Tile - 2x2 - Arduino wiring
    Glass Tile – 2×2 – Arduino wiring

    An initial lamp test showed the white-ish glass tiles aren’t all quite the same color:

    Glass Tile - 2x2 - white color variation
    Glass Tile – 2×2 – white color variation

    I thought it was an LED color variation, too, but the slightly blue tint in the lower left corner followed the tile.

    The blurred horizontal strip across the middle is adhesive tape holding the tiles in place; I was reluctant to glue them in before being sure this whole thing would work. A peek into the future, though, shows it’s got potential:

    Glass Tile - 2x2 - first two units
    Glass Tile – 2×2 – first two units

    They do give off a definite Windows logo vibe, don’t they?

    The OpenSCAD source code as a GitHub Gist:

    // Illuminated Tile Grid
    // Ed Nisley – KE4ZNU
    // 2020-05
    /* [Configuration] */
    Layout = "Build"; // [Cell,CellArray,MCU,Base,Show,Build]
    Shape = "Square"; // [Square, Pyramid, Cone]
    Cells = [2,2];
    CellDepth = 15.0;
    Support = true;
    Inserts = true;
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Tile = [25.0 + 0.1,25.0 + 0.1,4.0];
    WallThick = 3*ThreadWidth;
    Flange = [4*ThreadWidth,4*ThreadWidth,0]; // ridge supporting tile
    Separator = [3*ThreadWidth,3*ThreadWidth,Tile.z – 1]; // between tiles
    Screw = [3.0,6.0,3.5]; // M3 SHCS, OD=head, LENGTH=head
    Insert = [3.0,4.2,8.0]; // threaded brass insert
    PCB = [15.0,8.0,2.5];
    LED = [5.0 + 2*HoleWindage,5.0 + 2*HoleWindage,1.0];
    LEDOffset = [0.0,(PCB.y – LED.y)/2 – 0.5,0.0]; // slight offset from +Y PCB edge
    CellOAL = [Tile.x,Tile.y,0] + Separator + [0,0,CellDepth] + [0,0,WallThick] + [0,0,PCB.z];
    ArrayOAL = [Cells.x*CellOAL.x,Cells.y*CellOAL.y,CellOAL.z]; // just the LED cells
    BlockOAL = ArrayOAL + [2*WallThick,2*WallThick,0]; // LED cells + exterior wall
    echo(str("Block OAL: ",BlockOAL));
    InsertOC = ArrayOAL – [Insert[OD],Insert[OD],0] – [2*WallThick,2*WallThick,0];
    echo(str("Insert OC: ",InsertOC));
    TapeThick = 1.0;
    Arduino = [44.0,18.0,8.0 + TapeThick]; // Arduino Nano to top of USB Mini-B plug
    USBPlug = [15.0,11.0,8.5]; // USB Mini-B plug insulator
    USBOffset = [0,0,5.5]; // offset from PCB base
    WiringBay = [BlockOAL.x – 4*WallThick,38.0,3.0];
    PlateOAL = [BlockOAL.x,BlockOAL.y,WallThick + Arduino.z + WiringBay.z];
    echo(str("Base Plate: ",PlateOAL));
    //————————
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(d=(FixDia + HoleWindage),h=Height,$fn=Sides);
    }
    //———————–
    // Base and optics in single tile
    module LEDCone() {
    hull() {
    translate([0,0,CellDepth + Tile.z/2])
    cube(Tile – [Flange.x,Flange.y,0],center=true);
    if (Shape == "Square") {
    translate([0,0,LED.z/2])
    cube([Tile.x,Tile.y,LED.z] – [Flange.x,Flange.y,0],center=true);
    }
    else if (Shape == "Pyramid") {
    translate([0,0,LED.z/2])
    cube(LED,center=true);
    }
    else if (Shape == "Cone") {
    translate([0,0,LED.z/2])
    cylinder(d=1.5*LED.x,h=LED.z,center=true);
    }
    else {
    echo(str("Whoopsie! Invalid Shape: ",Shape));
    cube(5);
    }
    }
    }
    // One complete LED cell
    module LEDCell() {
    difference() {
    translate([0,0,CellOAL.z/2])
    cube(CellOAL,center=true);
    translate([0,0,CellOAL.z – Separator.z + Tile.z/2])
    cube(Tile,center=true);
    translate([0,0,PCB.z + WallThick])
    LEDCone();
    cube([LED.x,LED.y,CellOAL.z],center=true);
    translate(-LEDOffset + [0,0,PCB.z/2 – Protrusion/2])
    cube(PCB + [0,0,Protrusion],center=true);
    }
    if (Support)
    color("Yellow") render()
    translate(-LEDOffset) {
    // translate([0,0,ThreadThick/2])
    // cube([PCB.x – 2*ThreadWidth,PCB.y – 2*ThreadWidth,ThreadThick],center=true);
    intersection() {
    translate([0,0,(PCB.z – ThreadThick)/2])
    cube([PCB.x – 2*ThreadWidth,PCB.y – 2*ThreadWidth,PCB.z – ThreadThick],center=true);
    union() { for (a=[0:22.5:359])
    rotate(a)
    translate([PCB.x/2,0,PCB.z/2])
    cube([PCB.x,2*ThreadWidth,PCB.z],center=true); }
    }
    }
    }
    // The whole array of cells
    module CellArray() {
    difference() {
    union() {
    translate([CellOAL.x/2 – Cells.x*CellOAL.x/2,CellOAL.y/2 – Cells.y*CellOAL.y/2,0])
    for (i=[0:Cells.x – 1], j=[0:Cells.y – 1])
    translate([i*CellOAL.x,j*CellOAL.y,0])
    LEDCell();
    if (Inserts) // bosses
    for (i=[-1,1], j=[-1,1])
    translate([i*InsertOC.x/2,j*InsertOC.y/2,0])
    rotate(180/8)
    cylinder(d=Insert[OD] + 3*WallThick,h=Insert[LENGTH],$fn=8);
    }
    if (Inserts) // holes
    for (i=[-1,1], j=[-1,1])
    translate([i*InsertOC.x/2,j*InsertOC.y/2,-Protrusion])
    rotate(180/8)
    PolyCyl(Insert[OD],Insert[LENGTH] + WallThick + Protrusion,8);
    }
    difference() {
    translate([0,0,CellOAL.z/2])
    cube(BlockOAL,center=true);
    translate([0,0,CellOAL.z])
    cube(ArrayOAL + [0,0,2*CellOAL.z],center=true);
    }
    }
    // Arduino bounding box
    // Origin at center bottom of PCB
    module Controller() {
    union() {
    translate([0,0,Arduino.z/2])
    cube(Arduino,center=true);
    translate([Arduino.x/2 – Protrusion,-USBPlug.y/2,USBOffset.z + TapeThick – USBPlug.z/2])
    cube(USBPlug + [Protrusion,0,0],center=false);
    }
    }
    // Baseplate
    module BasePlate() {
    difference() {
    translate([0,0,PlateOAL.z/2])
    cube(PlateOAL,center=true);
    translate([0,0,WallThick])
    Controller();
    translate([0,0,WallThick + PlateOAL.z/2])
    cube([Arduino.x – 2*2.0,WiringBay.y,PlateOAL.z],center=true);
    translate([0,0,PlateOAL.z – WiringBay.z + WiringBay.z/2])
    cube(WiringBay + [0,0,2*Protrusion],center=true);
    for (i=[-1,1], j=[-1,1])
    translate([i*InsertOC.x/2,j*InsertOC.y/2,-Protrusion])
    rotate(180/8) {
    PolyCyl(Screw[ID],2*PlateOAL.z,8);
    PolyCyl(Screw[OD],Screw[LENGTH] + 4*ThreadThick + Protrusion,8);
    }
    translate([0,0,ThreadThick-Protrusion])
    cube([17.0,45,2*ThreadThick],center=true);
    }
    linear_extrude(height=2*ThreadWidth + Protrusion) {
    translate([1,0,-Protrusion])
    rotate(-90) mirror([1,0,0])
    text(text="Ed Nisley",size=6,font="Arial:style:Bold",halign="center");
    translate([-6.5,0,-Protrusion])
    rotate(-90) mirror([1,0,0])
    text(text="softsolder.com",size=4.5,font="Arial:style:Bold",halign="center");
    }
    if (Support)
    color("Yellow")
    for (i=[-1,1], j=[-1,1])
    translate([i*InsertOC.x/2,j*InsertOC.y/2,0])
    for (a=[0:45:135])
    rotate(a)
    translate([0,0,(Screw[LENGTH] – ThreadThick)/2])
    cube([Screw[OD] – 2*ThreadWidth,2*ThreadWidth,Screw[LENGTH] – ThreadThick],center=true);
    }
    //———————–
    // Build things
    if (Layout == "Cell")
    LEDCell();
    else if (Layout == "CellArray")
    CellArray();
    else if (Layout == "MCU")
    Controller();
    else if (Layout == "Base")
    BasePlate();
    else if (Layout == "Show") {
    translate([0,0,PlateOAL.z + 10])
    CellArray();
    BasePlate();
    }
    else if (Layout == "Build") {
    translate([0,0.6*BlockOAL.y,0])
    CellArray();
    translate([0,-0.6*BlockOAL.y,0])
    rotate(90)
    BasePlate();
    }

  • Glass Tiles: Single Test Cell

    Glass Tiles: Single Test Cell

    A single glass tile rests on the ridge around the pyramidal interior:

    Glass Tile Frame - pyramid cell
    Glass Tile Frame – pyramid cell

    The bottom has a cutout for the WS2812 PCB, with some in-the-model support for simplicity:

    Glass Tile Frame - pyramid cell - bottom
    Glass Tile Frame – pyramid cell – bottom

    Which becomes this in real life:

    Glass Tile - 1x1 cell test - pyramid PETG strings
    Glass Tile – 1×1 cell test – pyramid PETG strings

    There’s plenty of PETG hair inside the opening, which seems like a Bad Thing all around.

    Cleaning out the worst of the fur, taping a WS2812 LED into the opening, and dropping a white-ish tile in place:

    Glass Tile - 1x1 cell test - purple phase
    Glass Tile – 1×1 cell test – purple phase

    Obviously, JPG compression wasn’t built with a finely textured granular surface in mind:

    Glass Tile - 1x1 cell test - blue phase
    Glass Tile – 1×1 cell test – blue phase

    But it looks really nice in a dim room!

    With a physical object in hand, it’s obvious the pyramidal interior adds exactly zero value:

    • Direct rays in the beam from the WS2812 don’t hit the walls
    • Light outside the beam doesn’t contribute much after hitting those irregular walls

    So the next pass should be just a hollow box with tweaked tile & PCB measurement: rapid prototyping in full effect!

  • Glass Tiles: Proof of Concept

    Glass Tiles: Proof of Concept

    Extract some victims from a square foot of glass tiles:

    Glass Tiles - as sold
    Glass Tiles – as sold

    Wire an old WS2812 breakout board (the new ones are much larger) to an Arduino Nano running the Nissan Fog Lamp firmware:

    Glass Tile - backlight blue - setup
    Glass Tile – backlight blue – setup

    Aaaand it looks like this might actually work:

    Glass Tile - backlight blue
    Glass Tile – backlight blue

    The WS2812 “beam” illuminates the 25 mm square tile without too much vignetting at about 15 mm.

    The bottom tile is white-ish, the top is gray-ish, and they look different enough to justify using only one color in each array:

    Glass Tile - backlight neutral
    Glass Tile – backlight neutral

    Now, for some solid modeling …

  • Floor Lamp Height vs. Reach: Plumbing Fitting

    Floor Lamp Height vs. Reach: Plumbing Fitting

    Update: Welcome Adafruit! The reshaped elbow shown here eventually got threaded adapters for the tubing and an awful paint job.

    The floor lamp with the invisible / non-tactile controls moved to a different chair, where it didn’t have quite enough reach and too much height. Knowing what was about to happen, I spliced a JST-SM connector into the wire inside the tube:

    Floor Lamp - base wiring JST-SM connector
    Floor Lamp – base wiring JST-SM connector

    After trimming off all the extraneous bits, the larger half of the connector (male pins) fits through the tubing and the smaller half (female sockets) barely fits through the bottom bushings.

    It turns out half-inch copper pipe fittings (ID = 15.9 mm) almost exactly fit the tubing (OD = 15.7 mm):

    Floor Lamp - copper 45° elbow
    Floor Lamp – copper 45° elbow

    A quick test showed the 45° (actually, it’s 135°, but we’re deep into plumbing nomenclature) positioned the lamp head too high and with too much reach:

    Floor Lamp - gooseneck exercise
    Floor Lamp – gooseneck exercise

    So shorten the tube attached to the head and deburr the cut:

    Floor Lamp - tube deburring
    Floor Lamp – tube deburring

    The 45° fitting is too high and a 90° fitting is obviously too low, so cut a 20° slice out of a 90° fitting:

    Floor Lamp - copper 90° elbow - 20° cutout
    Floor Lamp – copper 90° elbow – 20° cutout

    Cut a snippet of brass tubing to fit, bash to fit, file to hide, buff everything to a high shine, silver-solder it in place, and buff everything again:

    Floor Lamp - copper 90° elbow - 20° fill strip
    Floor Lamp – copper 90° elbow – 20° fill strip

    The 5/8 inch aluminum rods serve to stiffen the fitting, smooth out the torch heating, and generally keep things under control.

    Wrap the obligatory Kapton tape around the butt ends of the tubes to fill the fitting’s oversize hole, put everything together, and it’s just about perfect:

    Floor Lamp - copper 70° elbow - installed
    Floor Lamp – copper 70° elbow – installed

    I immobilized the fitting with black Gorilla tape, but it really needs something a bit more permanent. One of these days, maybe, a pair of setscrews will happen.

    The additional reach required a little more counterweight on the far side for security, so I added the broken stub of a truck leaf spring. It should be secured firmly to the base plate, but no tool I own can put a dent in those three pounds of spring steel. Maybe it’ll merit a fancy enclosure wrapped around the base?