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.

Tag: Improvements

Making the world a better place, one piece at a time

  • Lathe-straightened Copper Wire

    Lathe-straightened Copper Wire

    I formerly straightened the copper wire into “bus bars” for the astable multivibrators by whacking it with a slide hammer, but someone whose name is lost in the mists of time told me the right way to do it:

    Lathe-straightening Wire - setup
    Lathe-straightening Wire – setup

    Yup, grab a piece of wire at both ends in Tiny Lathe and give it a few low-speed turns while pulling firmly on the tailstock.

    No muss, no fuss, no drama, just bar-straight and slightly work-hardened copper wires:

    Lathe-straightening Wire - results
    Lathe-straightening Wire – results

    I slide-hammered the top wire before remembering the clue. The bottom two wires have peppermint-stick swirls.

    Thank you, whoever you were!

  • PTC Fuses

    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.

  • 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.

  • Quilting Hexagon Template Generator: Knobless Half-Triangle

    Quilting Hexagon Template Generator: Knobless Half-Triangle

    Although I’d put the same knob on the half-triangle end piece template as on the equilateral triangle template for piecing hexagons into strips, Mary decided a flat chip would be easier to use:

    Quilting Hex Template - family - knobless half-triangle
    Quilting Hex Template – family – knobless half-triangle

    Bonus: you can now flip it over to cut the other half-triangles, if you haven’t already figured out how to cut two layers of fabric folded wrong sides together.

    While I was at it, the knob on the triangle became optional, too. Flipping that one doesn’t buy you much, though.

    The OpenSCAD source as a GitHub Gist has been ever so slightly tweaked:

    // Quilting – Hexagon Templates
    // Ed Nisley KE4ZNU – July 2020
    // Reverse-engineered to repair a not-quite-standard hexagon quilt
    // Useful geometry:
    // https://en.wikipedia.org/wiki/Hexagon
    /* [Layout Options] */
    Layout = "Build"; // [Build, HexBuild, HexPlate, TriBuild, TriPlate, EndBuild, EndPlate]
    //——-
    //- Extrusion parameters must match reality!
    // Print with 2 shells
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleFinagle = 0.2;
    HoleFudge = 1.00;
    function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle;
    Protrusion = 0.1; // make holes end cleanly
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    inch = 25.4;
    //——-
    // Dimensions
    /* [Layout Options] */
    FinishedWidthInch = 2.75;
    FinishedWidth = FinishedWidthInch * inch;
    SeamAllowanceInch = 0.25;
    SeamAllowance = SeamAllowanceInch * inch;
    TemplateThick = 3.0;
    TriKnob = true;
    EndKnob = false;
    /* [Hidden] */
    FinishedSideInch = FinishedWidthInch/sqrt(3);
    FinishedSide = FinishedSideInch * inch;
    echo(str("Finished side: ",FinishedSideInch," inch"));
    CutWidth = FinishedWidth + 2*SeamAllowance;
    CutSide = CutWidth/sqrt(3);
    echo(str("Cut side: ",CutSide / inch," inch"));
    // Make polygon-circles circumscribe the target widths
    TemplateID = FinishedWidth / cos(180/6);
    TemplateOD = CutWidth / cos(180/6);
    /* [Hidden] */
    TriRadius = FinishedSide/sqrt(3);
    TriPoints = [[TriRadius,0],
    [TriRadius*cos(120),TriRadius*sin(120)],
    [TriRadius*cos(240),TriRadius*sin(240)]
    ];
    echo(str("TriPoints: ",TriPoints));
    EndPoints = [[TriRadius,0],
    [TriRadius*cos(120),TriRadius*sin(120)],
    [TriRadius*cos(120),0]
    ];
    echo(str("EndPoints: ",EndPoints));
    TipCutRadius = 2*(TriRadius + SeamAllowance); // circumscribing radius of tip cutter
    TipPoints = [[TipCutRadius,0],
    [TipCutRadius*cos(120),TipCutRadius*sin(120)],
    [TipCutRadius*cos(240),TipCutRadius*sin(240)]
    ];
    HandleHeight = 1 * inch;
    HandleLength = (TemplateID + TemplateOD)/2;
    HandleThick = IntegerMultiple(3.0,ThreadWidth);
    HandleSides = 12*4;
    StringDia = 4.0;
    StringHeight = 0.6*HandleHeight;
    DentDepth = HandleThick/4;
    DentDia = 15 * DentDepth;
    DentSphereRadius = (pow(DentDepth,2) + pow(DentDia,2)/4)/(2*DentDepth);
    KnobOD = 15.0; // Triangle handle
    KnobHeight = 20.0;
    //——-
    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=HoleAdjust(FixDia)/2,h=Height,$fn=Sides);
    }
    //——-
    // Hex template
    module HexPlate() {
    difference() {
    cylinder(r=TemplateOD/2,h=TemplateThick,$fn=6);
    translate([0,0,-Protrusion])
    cylinder(r=TemplateID/2,h=(TemplateThick + 2*Protrusion),$fn=6);
    }
    for (i=[1:6/2])
    rotate(i*60)
    translate([0,0,TemplateThick/2])
    cube([HandleLength,HandleThick,TemplateThick],center=true);
    }
    module HexHandle() {
    difference() {
    rotate([90,0,0])
    scale([1,HandleHeight/(TemplateOD/2),1])
    rotate(180/HandleSides)
    cylinder(d=HandleLength,h=HandleThick,center=true,$fn=HandleSides);
    translate([0,0,-HandleHeight])
    cube([2*TemplateOD,2*TemplateOD,2*HandleHeight],center=true);
    translate([0,HandleThick,StringHeight])
    rotate([90,090,0])
    rotate(180/8)
    PolyCyl(StringDia,2*HandleThick,8);
    for (j=[-1,1]) {
    translate([0,j*(DentSphereRadius + HandleThick/2 – DentDepth),StringHeight])
    rotate(180/48)
    sphere(r=DentSphereRadius,$fn=48);
    }
    }
    }
    module HexTemplate() {
    HexPlate();
    HexHandle();
    }
    //——-
    // Triangle template
    module TriPlate() {
    linear_extrude(height=TemplateThick)
    intersection() {
    offset(delta=SeamAllowance) // basic cutting outline
    polygon(points=TriPoints);
    rotate(180)
    polygon(points=TipPoints);
    }
    }
    module TriTemplate() {
    union() {
    if (TriKnob)
    cylinder(d=KnobOD,h=KnobHeight,$fn=HandleSides);
    TriPlate();
    }
    }
    //——-
    // End piece template
    module EndPlate() {
    linear_extrude(height=TemplateThick)
    intersection() {
    offset(delta=SeamAllowance) // basic cutting outline
    polygon(points=EndPoints);
    rotate(180)
    polygon(points=TipPoints);
    }
    }
    module EndTemplate() {
    union() {
    if (EndKnob)
    translate([0,(TriRadius/2)*sin(30),0])
    cylinder(d=KnobOD,h=KnobHeight,$fn=HandleSides);
    EndPlate();
    }
    }
    //——-
    // Build it!
    if (Layout == "HexPlate")
    HexPlate();
    if (Layout == "HexBuild")
    HexTemplate();
    if (Layout == "TriPlate")
    TriPlate();
    if (Layout == "TriBuild")
    TriTemplate();
    if (Layout == "EndPlate")
    EndPlate();
    if (Layout == "EndBuild")
    EndTemplate();
    if (Layout == "Build") {
    translate([1.5*TriRadius,-TriRadius,0])
    rotate(180/6)
    TriTemplate();
    translate([-1.5*TriRadius,-TriRadius,0])
    rotate(180/6)
    EndTemplate();
    translate([0,TemplateOD/2,0])
    HexTemplate();
    }

  • Shuttles Game: Tapered Pegs

    Shuttles Game: Tapered Pegs

    As is all too common with 3D printed replacement parts done remotely, the first Shuttles game pegs didn’t quite fit into the game board’s holes. Fortunately, living in the future means rapid prototyping and quick turnaround:

    Shuttles Game pegs - tapered - solid model
    Shuttles Game pegs – tapered – solid model

    They’re slightly smaller, tapered toward the bottom, and take slightly less time to print.

    The OpenSCAD code in the GitHub Gist now has has the tweaks.

  • Finding & Copying Only The New Music Files

    Given a collection of music files in various subdirectories, find all the mp3 files that aren’t in the target directory and copy them. The only catch: don’t use rsync, because the target directory is on a Google Pixel phone filesystem which doesn’t support various attributes required by rsync.

    The solution goes like this:

    cd /mnt/music/Netlabel Mixes
    sudo jmtpfs /mnt/pixel -o allow_other,fsname="Pixel"
    find . -name \*mp3 -execdir test ! -e  /mnt/pixel/Internal\ shared\ storage/Music/Netlabel/\{\} \; -execdir cp -v -t /mnt/pixel/Internal\ shared\ storage/Music/Netlabel/ \{\} \;
    sudo umount /mnt/pixel

    The trick is remembering the second execdir operation in find happens only if the first succeeds, so the cp runs when the target file doesn’t exist.

    All the backslash escaping gets tedious, but it’s the least awful way to get the job done when the directories contain blanks, which is true for the default directory structure inside the Pixel.

    Your choice in music will surely be different …

  • Reinforced QD Propane Adapter Tool

    Reinforced QD Propane Adapter Tool

    Having just emptied a propane tank while making bacon, I couldn’t find any of the wrench adapters I made to remove the QD adapter from the tank’s POL fitting. With memory of the broken garden valve wrench still fresh, I tweaked the solid model to include a trio of 1 mm music wire reinforcements:

    Propane QD Adapter Tool - reinforced - Slic3r
    Propane QD Adapter Tool – reinforced – Slic3r

    Holes that small require clearing with a 1 mm drill, after which ramming the wires in place poses no problem:

    Reinforced QD Adapter Tool - inserting wire
    Reinforced QD Adapter Tool – inserting wire

    Except for the one that got away:

    Reinforced QD Adapter Tool - errant wire
    Reinforced QD Adapter Tool – errant wire

    The music wire came from a coil and each snippet required gentle straightening; perhaps that one wasn’t sufficiently bar-straight.

    Anyhow, I printed two tools for that very reason:

    Reinforced QD Adapter Tool - side view
    Reinforced QD Adapter Tool – side view

    They’re now where I can’t miss ’em the next time I need them, although that’s not where the previous ones reside.

    The OpenSCAD source code as a GitHub Gist:

    // Propane tank QD connector adapter tool
    // Ed Nisley KE4ZNU November 2012
    // 2018-04-08 toss MCAD includes overboard
    // 2020-07-27 add reinforcing rods
    //- Extrusion parameters must match reality!
    // Print with about half a dozen perimeter threads and 50% infill
    ThreadThick = 0.25;
    ThreadWidth = 2.0 * ThreadThick;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    //———————-
    // Dimensions
    WrenchSize = (5/8) * inch; // across the flats
    WrenchThick = 10;
    NoseDia = 8.6;
    NoseLength = 9.0;
    LockDia = 12.5;
    LockRingLength = 1.0;
    LockTaperLength = 1.5;
    TriDia = 15.1;
    TriWide = 12.2; // from OD across center to triangle side
    TriOffset = TriWide – TriDia/2; // from center to triangle side
    TriLength = 9.8;
    NeckDia = TriDia;
    NeckLength = 4.0;
    RebarOD = 1.0; // music wire pin 1 mm = 39 mil
    RebarLength = WrenchThick + NeckLength + TriLength;
    //———————-
    // 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);
    }
    //——————-
    // Build it…
    $fn = 4*6;
    union() {
    translate([0,0,(WrenchThick + NeckLength + TriLength – LockTaperLength – LockRingLength + Protrusion)])
    cylinder(r1=NoseDia/2,r2=LockDia/2,h=LockTaperLength);
    translate([0,0,(WrenchThick + NeckLength + TriLength – LockRingLength)])
    cylinder(r=LockDia/2,h=LockRingLength);
    difference() {
    union() {
    translate([0,0,WrenchThick/2])
    cube([WrenchSize,WrenchSize,WrenchThick],center=true);
    cylinder(r=TriDia/2,h=(WrenchThick + NeckLength +TriLength));
    cylinder(r=NoseDia/2,h=(WrenchThick + NeckLength + TriLength + NoseLength));
    }
    for (a=[-1:1]) {
    rotate(a*120)
    translate([(TriOffset + WrenchSize/2),0,(WrenchThick + NeckLength + TriLength/2 + Protrusion/2)])
    cube([WrenchSize,WrenchSize,(TriLength + Protrusion)],center=true);
    }
    for (a=[-1:1]) {
    rotate(a*120 + 60)
    translate([NoseDia/2,0,-Protrusion])
    PolyCyl(RebarOD,RebarLength,6);
    }
    }
    }