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

  • Vacuum Tube LEDs: Radome Prototype

    Definitely not a vacuum tube:

    Arduino Pro Mini - NP-BX1 cell - SK6812 - blue phase
    Arduino Pro Mini – NP-BX1 cell – SK6812 – blue phase

    It’s running the same firmware, though, with the Arduino Pro Mini and the LEDs drawing power from the (mostly) defunct lithium battery.

    The LED holder is identical to the Pirhana holder, with a 10 mm diameter recess punched into it for the SK6812 PCB:

    Astable Multivibrator Battery Holder - Neopixel PCB - Slic3r
    Astable Multivibrator Battery Holder – Neopixel PCB – Slic3r

    Those embossed legends sit in debossed rectangles for improved legibility. If I repeat it often enough, I’m sure I’ll remember which is which.

    The 3.6 V (and declining) power supply may not produce as much light from the SK6812 LEDs, but it’s entirely adequate for anything other than a well-lit room. The 28 AWG silicone wires require a bit of careful dressing to emerge from the holes in the radome holder:

    SK6812 LED PCB - Pirhana holder wiring
    SK6812 LED PCB – Pirhana holder wiring

    The firmware cycles through all the usual colors:

    Arduino Pro Mini - NP-BX1 cell - SK6812 - orange phase
    Arduino Pro Mini – NP-BX1 cell – SK6812 – orange phase

    A pair of tensilized 22 AWG copper wires support the Pro Mini between the rear struts. The whole affair looks a bit heavier than I expected, though, so I should reduce the spider to a single pair of legs with a third hole in the bottom of the LED recess for the data wire.

    The OpenSCAD source code needs some refactoring and tweaking, but the Pirhana LED solid model version of the battery holder should give you the general idea.

  • Vacuum Tube LEDs: Arduino Pro Mini vs. NP-BX1 Battery

    A year or so ago, a certain Young Engineer suggested my Vacuum Tube Lights really needed battery power and rebuffed my feeble objections concerning low LED intensity (3.6-ish V, not plug-in 5 V USB) and short run time (because three constantly lit LEDs draw too much current). Having a spare NP-BX1 holder lying about, here’s a feasibility study:

    Arduino Pro Mini - Neopixel - NP-BX1 battery
    Arduino Pro Mini – Neopixel – NP-BX1 battery

    Not much to it, eh?

    Hitching the DSO150 to a Tek current probe (which needs a 50 Ω load, thus the terminator on the BNC tee) seems a clear-cut case of a sow’s ear joining forces with a silk purse:

    DSO150 - Arduino Pro Mini - Neopixel current
    DSO150 – Arduino Pro Mini – Neopixel current

    It was just sitting there, so why not?

    Seen with a bit more detail on a better scope:

    Ard Mini - NP-BX1 - SK6812 - 10 mA-div
    Ard Mini – NP-BX1 – SK6812 – 10 mA-div

    Each vertical increment represents the current into a single LED (at 10 mA/div), with the PWM cycles ticking along at 1.3 kHz.

    The current steps aren’t the same height, because the LEDs have different forward voltages. The taller step (at the top) probably comes from the red LED, with the other two being blue and green. The maximum current is only 40 mA, not the 60 mA you’d expect with a 5 V supply.

    The PWM width, of course, determines the brightness of each LED. Eyeballometrically, the average current will be half of 40 mA for (just less than) half of each PWM cycle, so figuring each SK6812 module (there’s only one here) will draw 10 mA seems reasonable.

    The “base load” from the Arduino looks like 2 mA, so there’s not much point in removing its power and status LEDs.

    The NP-BX1 lithium cell has lost enough capacity to no longer power my Sony HDR-AS30V helmet camera for at least half of a typical ride. The camera draws around 1 A, so you can clearly see the defunct batteries:

    Sony NP-BX1 - 2018-04-24
    Sony NP-BX1 – 2018-04-24

    If the average voltage during discharge is 3.3. V, then a 10 mA load would be 33 mW and a defunct NP-BX1 battery with 2 W·h capacity (at 1 A) might provide 60 hours of continuous use. I’d expect more capacity at lower current, although it’s not clear the cells actually behave that way.

    So a battery-powered Vacuum Tube Light might make sense, perhaps as romantic illumination for techie snuggling:

    21HB5A - Guilloche platter
    21HB5A – Guilloche platter

    Ya never know …

  • DMM Probes

    After the Great DMM Probe Debacle, I picked up similar-but-different set of cheap probes and clip leads.

    The needle-tip probes carry a 20 A current rating:

    No-Name DMM probes - needle tip - 20 A
    No-Name DMM probes – needle tip – 20 A

    If you look out along the wire, though, you’ll find a 10 A rating:

    No-Name DMM probes - needle tip - 10 A wires
    No-Name DMM probes – needle tip – 10 A wires

    Now, even though 20 AWG wire in silicone may carry a 17 A spec, the corresponding 200 °C temperature seems excessive for a test probe. Limiting the current to 10 A would reduce the power dissipation by two thirds, which should limit the temperature rise. Whether the wire actually contains 20 AWG of actual copper strands remains an open question.

    The kit also had banana plug / test hooks with no particular rating, although the wire allegedly has 16 AWG conductors:

    DMM Clip Leads - 16 AWG
    DMM Clip Leads – 16 AWG

    The banana plug / alligator clip combo claims 30 A, also with 16 AWG conductors. Who knows? It could be true.

    For comparison, the Siglent SDM3045 DMM came with these probes:

    Siglent DMM probes - 10 A
    Siglent DMM probes – 10 A

    The probes carry a 10 A rating and, although the wires aren’t branded, I’ll assume they have good-enough QC to ensure the copper matches the claims. The production values seem a bit higher, too, even if they bear a striking resemblance to the cheap probes.

    And, for reference, the probes with the cold solder joint also claim 20 A:

    No-Name DMM probes - 20 A
    No-Name DMM probes – 20 A

    Wouldn’t trust any of ’em for more than a few amps, tops …

  • Sony HDR-AS30V Helmet Camera: MicroSD Card Spacer

    Sony tried, they really tried, to make their proprietary Memory Stick flash memory cards catch on, but the slot in their HDR-AS30V Action / Helmet camera accepts both Memory Stick Micro and MicroSD cards. The two cards have slightly different sizes, the AS30V’s dual-purpose slot allows MicroSD cards to sit misaligned with the contacts, and the camera frequently kvetches about having no card.

    The only solution seemed to be starting the camera while watching the display to ensure the card worked, but it would sometimes joggle out of position during a ride.

    I cut out a tiny polypropylene rectangle(-ish) spacer to fill the Memory Stick side of the slot, sized to fit between the spring fingers holding the MicroSD card against its contacts:

    Sony HDR-AS30V Camera - MicroSD card and spacer
    Sony HDR-AS30V Camera – MicroSD card and spacer

    Not the best cutting job I’ve ever done, but it was an iterative process and that’s where I stopped. If this works and I have need for another / better spacer, I promise to do better.

    The spacer’s somewhat mottled appearance comes from tapeless sticky (an adhesive layer on a peel-off backing: inverse tape!) applied to the top side, which will affix it to the slot. I’d rather glue the spacer to the MicroSD card, but then the card wouldn’t fit in the USB 3.0 adapter I use to transfer the files.

    The chips along the left edge of silkscreen come from my fingernail, because pressing exactly there seems to be the best way to force the damn thing into the proper alignment.

    So the slot + spacer looks like this:

    Sony HDR-AS30V Camera - dual-card slot with spacer
    Sony HDR-AS30V Camera – dual-card slot with spacer

    The MicroSD card fits in the far side of the slot, facing toward you with contacts downward, thusly:

    Sony HDR-AS30V Camera - MicroSD card with spacer
    Sony HDR-AS30V Camera – MicroSD card with spacer

    And then It Just Works™, at least on the very few rides we’ve gotten in during December and early January.

    Incidentally, the blue and exceedingly thin latch finger holding the battery in place will snap, should you drop the camera on its non-lens end from any height. Conversely, should you drop it on the lens end, you can kiss the optics goodbye. Your choice.

  • Tensilizing Copper Wire

    The “bus bars” on the battery holders are 14 AWG copper wire:

    Astable - NP-BX1 base - detail
    Astable – NP-BX1 base – detail

    Slightly stretching the wire straightens and work-hardens it, which I’d been doing by clamping one end in the bench vise, grabbing the other in a Vise-Grip, and whacking the Vise-Grip with a hammer. The results tended to be, mmm, hit-or-miss, with the wires often acquiring a slight bend due to an errant whack.

    I finally fished out the slide hammer Mary made when we took a BOCES adult-ed machine shop class many many years ago:

    Slide Hammer
    Slide Hammer

    The snout captured the head of a sheet metal screw you’d previously driven into a dented automobile fender. For my simple purposes, jamming the wire into the snout and tightening it firmly provides a Good Enough™ grip:

    Slide Hammer Snout
    Slide Hammer Snout

    Clamp the other end of the wire into the bench vise, pull gently on the hammer to take the slack out of the wire, and slap the weight until one end of the wire breaks.

    With a bit of attention to detail, the wires come out perfectly straight and ready to become Art:

    Straightened 14 AWG Copper Wires
    Straightened 14 AWG Copper Wires

    The wires start out at 1.60 mm diameter (14 AWG should be 1.628, but you know how this stuff goes) and break around 1.55 mm. In principle, when the diameter drops 3%, the area will decrease by 6% and the length should increase by 6%, but in reality the 150 mm length stretches by only 1 mm = 1%, not 3 mm. My measurement-fu seems weak.

    Highly recommended, particularly when your Favorite Wife made the tool.

    The Harbor Freight version comes with a bunch of snouts suitable for car repair and is utterly unromantic.

  • Astable Multivibrator: Monochrome Pirhana LED

    The LED parts box disgorged some single-color Pirhana-style LEDs:

    Astable - 2N7000 - Mono Pirhana LED
    Astable – 2N7000 – Mono Pirhana LED

    Didn’t quite catch the blink, but the Ping-Pong ball radome lights up just as you’d expect.

    The radome sits on a stripped-down RGB LED spider:

    Astable Multivibrator Battery Holder - mono LED Spider - fit view
    Astable Multivibrator Battery Holder – mono LED Spider – fit view

    The circuitry is the same as the First Light version, with a 1 MΩ resistor stabilizing the LED ballast resistor:

    Astable - 2N7000 - Mono Pirhana LED - detail
    Astable – 2N7000 – Mono Pirhana LED – detail

    Those are 1 µF ceramic caps in the astable section, so I’m no longer abusing electrolytics, and a stylin’ 100 nF film cap metering out the LED pulse up above.

    Just for pretty, I’ve been using yellow / black wires for the battery connections and matching the LED color with its cathode lead.

    The OpenSCAD source code as a GitHub Gist:

    // Holder for Li-Ion battery packs
    // Ed Nisley KE4ZNU January 2013
    // 2018-11-15 Adapted for 1.5 mm pogo pins, battery data table
    // 2018-12 RGB LED spider, general cleanups
    /* [Layout options] */
    BatteryName = "NP-BX1"; // [NP-BX1,NB-5L,NB-6L]
    RGBCircuit = false; // false = 1 strut pair, true = 2 pairs
    Layout = "Spider"; // [Build,Show,Fit,Case,Lid,Pins,RGBSpider,Spider]
    /* [Extrusion parameters] – must match reality! */
    // Print with +2 shells and 3 solid layers
    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
    /* [Hidden] */
    inch = 25.4;
    BuildOffset = 3.0; // clearance for build layout
    Gap = 2.0; // separation for Fit parts
    //- Basic dimensions
    WallThick = 4*ThreadWidth; // holder sidewalls
    BaseThick = 6*ThreadThick; // bottom of holder to bottom of battery
    TopThick = 6*ThreadThick; // top of battery to top of holder
    //- Battery dimensions – rationalized from several samples
    // Coordinate origin at battery corner with contacts, key openings downward
    T_NAME = 0; // Name must fit recess, so don't get loquacious
    T_SIZE = 1;
    T_CONTACTS = 2;
    T_KEYS = 3;
    BatteryData = [
    ["NP-BX1",[43.0,30.0,9.5],[[-0.75,6.0,6.2,"+"],[-0.75,16.0,6.2,"-"]],[[1.70,3.70,2.90],[1.70,3.60,2.90]]],
    ["NB-5L", [45.0,32.0,8.0],[[-0.82,4.5,3.5,"-"],[-0.82,11.0,3.5,"+"]],[[2.2,0.75,2.0],[2.2,2.8,2.0]]],
    ["NB-6L",[42.5,35.5,7.0],[[-0.85,5.50,3.05,"-"],[-0.85,11.90,3.05,"+"]],[[2.0,0.70,2.8],[2.0,2.00,2.8]]],
    ];
    echo(str("Battery: ",BatteryName));
    BatteryIndex = search([BatteryName],BatteryData,1,0)[0];
    echo(str(" Index: ",BatteryIndex));
    BatterySize = BatteryData[BatteryIndex][T_SIZE]; // X = length, Y = width, Z = thickness
    echo(str(" Size: ",BatterySize));
    Contacts = BatteryData[BatteryIndex][T_CONTACTS]; // relative to battery edge, front, and bottom
    echo(str(" Contacts: ",Contacts));
    ContactOC = Contacts[1].y – Contacts[0].y; // + and – terminals for pogo pin contacts
    ContactCenter = Contacts[0].y + ContactOC/2;
    KeyBlocks = BatteryData[BatteryIndex][T_KEYS]; // recesses in battery face set X position
    echo(str(" Keys: ",KeyBlocks));
    //- Pin dimensions
    ID = 0;
    OD = 1;
    LENGTH = 2;
    PinShank = [1.5,2.0,6.5]; // shank, flange, compressed length
    PinFlange = [1.5,2.0,0.5]; // flange, length included in PinShank
    PinTip = [0.9,0.9,2.5]; // extended spring-loaded tip
    WireOD = 1.7; // wiring from pins to circuitry
    PinChannel = WireOD; // cut behind flange for solder overflow
    PinRecess = 3.0; // recess behind pin flange end for epoxy fill
    echo(str("Contact tip dia: ",PinTip[OD]));
    echo(str(" .. shank dia: ",PinShank[ID]));
    OverTravel = 0.5; // space beyond battery face at X origin
    //- Holder dimensions
    GuideRadius = ThreadWidth; // friction fit ridges
    GuideOffset = 7; // from compartment corners
    LidOverhang = 2.0; // atop of battery for retention
    LidClearance = LidOverhang * (BatterySize.z/BatterySize.x); // … clearance above battery for tilting
    echo(str("Lid clearance: ",LidClearance));
    CaseSize = [BatterySize.x + PinShank[LENGTH] + OverTravel + PinRecess + GuideRadius + WallThick,
    BatterySize.y + 2*WallThick + 2*GuideRadius,
    BatterySize.z + BaseThick + TopThick + LidClearance];
    echo(str("Case size: ",CaseSize));
    CaseOffset = [-(PinShank[LENGTH] + OverTravel + PinRecess),-(WallThick + GuideRadius),0]; // position around battery
    ThumbRadius = 10.0; // thumb opening at end of battery
    CornerRadius = 3*ThreadThick; // nice corner rounding
    LidSize = [-CaseOffset.x + LidOverhang,CaseSize.y,TopThick];
    LidOffset = [0.0,CaseOffset.y,0];
    //- Wire struts
    StrutDia = 1.6; // AWG 14 = 1.6 mm
    StrutSides = 3*4;
    StrutBase = [StrutDia,StrutDia + 4*WallThick,CaseSize.z – TopThick]; // ID = wire, OD = buildable
    //StrutOC = [IntegerLessMultiple(BatterySize.x – StrutBase[OD],5.0), // set easy OC wire spacing
    // IntegerMultiple(CaseSize.y + StrutBase[OD],5.0)];
    StrutOC = [IntegerLessMultiple(CaseSize.x – 2*CornerRadius -2*StrutBase[OD],5.0),
    IntegerMultiple(CaseSize.y + StrutBase[OD],5.0)];
    StrutOffset = [CaseSize.x/2 + CaseOffset.x,BatterySize.y/2]; // from case centerlines
    StrutAngle = atan(StrutOC.y/StrutOC.x);
    echo(str("Strut OC: ",StrutOC));
    //- RGB / Pirhana / Neopixel-ish LEDs
    RGBBody = [8.0,8.0,5.0]; // Z = body height
    PixelPCB = [4.0,10.0,3.0]; // Neopixel-ish PCBs, ID = chip window
    RGBPin = 5.0; // pin length
    RGBPinsOC = [5.0,5.0]; // pin layout
    RGBRecess = RGBBody.z + RGBPin/2; // maximum LED recess depth
    BallOD = 40.0; // radome sphere
    BallSides = 4*StrutSides; // nice number of sides
    BallPillar = [norm([RGBBody.x,RGBBody.y]),
    norm([RGBBody.x,RGBBody.y]) + 4*WallThick,
    StrutBase[OD] + RGBBody.z];
    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);
    }
    //——————-
    //– Guides for tighter friction fit
    module Guides() {
    translate([GuideOffset,-GuideRadius,0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    translate([GuideOffset,(BatterySize.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    translate([(BatterySize.x – GuideOffset),-GuideRadius,0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    translate([(BatterySize.x – GuideOffset),(BatterySize.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    translate([(BatterySize.x + GuideRadius),GuideOffset/2,0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    translate([(BatterySize.x + GuideRadius),(BatterySize.y – GuideOffset/2),0])
    PolyCyl(2*GuideRadius,(BatterySize.z – Protrusion),4);
    }
    //– Contact pins
    // Rotated to put them in their natural oriention
    // Aligned to put tip base / end of shank at Overtravel limit
    module PinShape() {
    translate([-(PinShank[LENGTH] + OverTravel),0,0])
    rotate([0,90,0])
    rotate(180/6)
    union() {
    PolyCyl(PinTip[OD],PinShank[LENGTH] + PinTip[LENGTH],6);
    PolyCyl(PinShank[ID],PinShank[LENGTH] + Protrusion,6); // slight extension for clean cuts
    PolyCyl(PinFlange[OD],PinFlange[LENGTH],6);
    }
    }
    // Position pins to put end of shank at battery face
    // Does not include recess access into case
    module PinAssembly() {
    union() {
    for (p = Contacts)
    translate([0,p.y,p.z])
    PinShape();
    translate([-(PinShank[LENGTH] + OverTravel) + PinChannel/2, // solder space
    ContactCenter,
    Contacts[0].z])
    cube([PinChannel,
    (Contacts[1].y – Contacts[0].y + PinFlange[OD]),
    PinFlange[OD]],center=true);
    for (j=[-1,1]) // wire channels
    translate([-(PinShank[LENGTH] + OverTravel – PinChannel/2),
    j*ContactOC/4 + ContactCenter,
    Contacts[0].z – PinFlange[OD]/2])
    rotate(180/6)
    PolyCyl(WireOD,CaseSize.z,6);
    }
    }
    //– Case with origin at battery corner
    module Case() {
    difference() {
    union() {
    difference() {
    union() {
    translate([(CaseSize.x/2 + CaseOffset.x), // basic case shape
    (CaseSize.y/2 + CaseOffset.y),
    (CaseSize.z/2 – BaseThick)])
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(CaseSize.x/2 – CornerRadius),
    j*(CaseSize.y/2 – CornerRadius),
    k*(CaseSize.z/2 – CornerRadius)])
    sphere(r=CornerRadius/cos(180/8),$fn=8); // cos() fixes undersize spheres!
    for (i= RGBCircuit ? [-1,1] : -1) { // strut bases
    hull()
    for (j=[-1,1])
    translate([i*StrutOC.x/2 + StrutOffset.x,j*StrutOC.y/2 + StrutOffset.y,-BaseThick])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[LENGTH],$fn=StrutSides);
    translate([i*StrutOC.x/2 + StrutOffset.x,StrutOffset.y,StrutBase[LENGTH]/2 – BaseThick])
    cube([2*StrutBase[OD],StrutOC.y,StrutBase[LENGTH]],center=true); // blocks for fairing
    for (j=[-1,1]) // hemisphere caps
    translate([i*StrutOC.x/2 + StrutOffset.x,
    j*StrutOC.y/2 + StrutOffset.y,
    StrutBase[LENGTH] – BaseThick])
    rotate(180/StrutSides)
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    }
    }
    translate([-OverTravel,-GuideRadius,0])
    cube([(BatterySize.x + GuideRadius + OverTravel),
    (BatterySize.y + 2*GuideRadius),
    (BatterySize.z + LidClearance + Protrusion)]); // battery space
    translate([BatterySize.x/2,BatterySize.y/2,0]) // recess around battery name
    cube([0.8*BatterySize.x,8,2*ThreadThick],center=true);
    translate([CaseOffset.x + CaseSize.x/2,BatterySize.y/2,-BaseThick + ThreadThick – Protrusion]) // recess around battery name
    cube([0.75*CaseSize.x,8,2*ThreadThick],center=true);
    }
    Guides(); // improve friction fit
    translate([-OverTravel,-GuideRadius,0]) // battery keying blocks
    cube(KeyBlocks[0] + [OverTravel,GuideRadius,0],center=false);
    translate([-OverTravel,(BatterySize.y – KeyBlocks[1].y),0])
    cube(KeyBlocks[1] + [OverTravel,GuideRadius,0],center=false);
    translate([BatterySize.x/2,BatterySize.y/2,-ThreadThick])
    linear_extrude(height=2*ThreadThick,convexity=10)
    text(text=BatteryName,size=5,spacing=1.20,font="Arial:style:Bold",halign="center",valign="center");
    translate([CaseOffset.x + CaseSize.x/2,BatterySize.y/2,-BaseThick])
    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");
    }
    translate([2*CaseOffset.x, // battery top access
    (CaseOffset.y – Protrusion),
    BatterySize.z + LidClearance])
    cube([2*CaseSize.x,(CaseSize.y + 2*Protrusion),2*TopThick]);
    for (i2 = RGBCircuit ? [-1,1] : -1) { // strut wire holes and fairing
    for (j=[-1,1])
    translate([i2*StrutOC.x/2 + StrutOffset.x,j*StrutOC.y/2 + StrutOffset.y,0])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[ID],2*StrutBase[LENGTH],StrutSides);
    for (i=[-1,1], j=[-1,1])
    translate([i*StrutBase[OD] + (i2*StrutOC.x/2 + StrutOffset.x),
    j*StrutOC.y/2 + StrutOffset.y,
    -(BaseThick + Protrusion)])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[OD],StrutBase[LENGTH] + 2*Protrusion,StrutSides);
    }
    translate([(BatterySize.x – Protrusion), // remove thumb notch
    (CaseSize.y/2 + CaseOffset.y),
    (ThumbRadius)])
    rotate([90,0,0])
    rotate([0,90,0])
    cylinder(r=ThumbRadius,
    h=(WallThick + GuideRadius + 2*Protrusion),
    $fn=22);
    PinAssembly(); // pins and wiring
    translate([CaseOffset.x + PinRecess + Protrusion,(Contacts[1].y + Contacts[0].y)/2,Contacts[0].z])
    translate([-PinRecess,0,0])
    cube([2*PinRecess,
    (Contacts[1].y – Contacts[0].y + PinFlange[OD]/cos(180/6) + 2*HoleWindage),
    2*PinFlange[OD]],center=true); // pin insertion hole
    }
    }
    // Lid position offset to match case
    // The polarity indicator recesses are pure bodges
    module Lid() {
    union() {
    difference() {
    translate([-LidSize.x/2 + LidOffset.x + LidOverhang,LidSize.y/2 + LidOffset.y,0])
    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,$fn=8);
    translate([0,0,-LidSize.z/2]) // remove bottom
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),LidSize.z],center=true);
    translate([LidSize.x/8,0,0])
    cube([LidSize.x/4,0.75*LidSize.y,4*ThreadThick],center=true); // epoxy recess
    }
    translate([0,0,-(Contacts[0].z + PinFlange[OD])]) // punch wire holes
    PinAssembly();
    for (n=[0,1]) // polarity recesses
    translate([-LidOverhang/2 – 0.40,Contacts[n].y,LidSize.z – ThreadThick/2])
    cube([4,4.5,ThreadThick + Protrusion],center=true);
    }
    for (n=[0,1]) // polarity indicators
    translate([-LidOverhang/2,Contacts[n].y,LidSize.z – 1*ThreadThick]) // … proud of surface
    rotate(90)
    linear_extrude(height=2*ThreadThick,convexity=10)
    text(text=Contacts[n][3],size=5,font="Arial:style:Bold",halign="center",valign="center");
    }
    }
    // Spider for RGB LED + radome atop vertical struts
    module RGBSpider() {
    difference() {
    union() {
    for (i=[-1,1], j=[-1,1]) {
    translate([i*StrutOC.x/2,j*StrutOC.y/2,StrutBase[OD]/2])
    rotate(180/StrutSides) // doesn't quite match crosspieces; close enough
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    translate([i*StrutOC.x/2,j*StrutOC.y/2,0])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[OD]/2,$fn=StrutSides);
    }
    for (m=[-1,1]) // connecting bars
    rotate(m*StrutAngle)
    translate([0,0,StrutBase[OD]/4])
    cube([norm(StrutOC),StrutBase[OD],StrutBase[OD]/2],center=true);
    translate([0,0,0]) // pillar for RGB LED and ball
    cylinder(d=BallPillar[OD],h=BallPillar[LENGTH],$fn=BallSides);
    }
    for (i=[-1,1], j=[-1,1]) // strut wires
    translate([i*StrutOC.x/2,j*StrutOC.y/2,-Protrusion])
    rotate(0)
    PolyCyl(StrutBase[ID],StrutBase[OD]/2,6);
    for (m=[-1,1], n=[0,1]) // RGBA wires through bars
    rotate(m*StrutAngle + n*180)
    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,2*RGBBody.z + (BallPillar[LENGTH] – BallChordM) – RGBRecess]) // LED inset
    cube(RGBBody + [HoleWindage,HoleWindage,3*RGBBody.z],center=true); // XY clearance + huge height for E-Z cut
    translate([0,0,StrutBase[OD]/2]) // Neopixel recess
    PolyCyl(PixelPCB[OD],3*RGBBody.z,BallSides/2);
    for (m=[-1,1]) // RGBA wires through pillar
    rotate(m*StrutAngle)
    translate([0,0,StrutBase[OD]/2 + WireOD/2 + 0*Protrusion])
    cube([norm(StrutOC)/2,WireOD,WireOD],center=true);
    }
    }
    // Spider for single LED atop struts, with the ball
    // Aligned to struts at terminal end of battery on Y axis
    module Spider() {
    difference() {
    union() {
    for (j=[-1,1]) {
    translate([-StrutOC.x/2,j*StrutOC.y/2,StrutBase[OD]/2])
    rotate(180/StrutSides)
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    translate([-StrutOC.x/2,j*StrutOC.y/2,0])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[OD]/2,$fn=StrutSides);
    }
    translate([-StrutOC.x/2,0,StrutBase[OD]/4]) // connecting bars
    cube([StrutBase[OD]*cos(180/StrutSides),StrutOC.y,StrutBase[OD]/2],center=true);
    translate([-StrutOC.x/2,0,0]) // pillar for RGB LED and ball
    cylinder(d=BallPillar[OD],h=BallPillar[LENGTH],$fn=BallSides);
    }
    for (j=[-1,1]) // strut wires
    translate([-StrutOC.x/2,j*StrutOC.y/2,-Protrusion])
    rotate(0)
    PolyCyl(StrutBase[ID],StrutBase[OD]/2,6);
    translate([-StrutOC.x/2,0,0]) // wires through bars
    for (n=[-1,1])
    rotate(n*90)
    translate([StrutOC.x/3,0,-Protrusion])
    PolyCyl(StrutBase[ID],StrutBase[OD],6);
    translate([-StrutOC.x/2,0,-Protrusion]) // center hole for Neopixel
    rotate(180/6)
    PolyCyl(StrutBase[ID],StrutBase[OD],6);
    translate([-StrutOC.x/2,0,BallOD/2 + BallPillar[LENGTH] – BallChordM]) // ball inset
    sphere(d=BallOD);
    translate([-StrutOC.x/2,0,2*RGBBody.z + (BallPillar[LENGTH] – BallChordM) – RGBRecess]) // LED inset
    cube(RGBBody + [HoleWindage,HoleWindage,3*RGBBody.z],center=true); // XY clearance + huge height for E-Z cut
    translate([-StrutOC.x/2,0,StrutBase[OD]/2]) // Neopixel recess
    PolyCyl(PixelPCB[OD],3*RGBBody.z,BallSides/2);
    translate([-StrutOC.x/2,0,StrutBase[OD]/2 + WireOD/2 + 0*Protrusion]) // wire channels
    cube([WireOD,StrutOC.y/2,WireOD],center=true);
    }
    }
    //——————-
    // Build it!
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "RGBSpider") {
    RGBSpider();
    }
    if (Layout == "Spider") {
    Spider();
    }
    if (Layout == "Pins") {
    color("Silver",0.5)
    PinShape();
    PinAssembly();
    }
    if (Layout == "Fit") { // reveal pin assembly
    difference() {
    Case();
    translate([(CaseOffset.x – Protrusion),
    Contacts[1].y,
    Contacts[1].z])
    cube([(-CaseOffset.x + Protrusion),CaseSize.y,CaseSize.z]);
    translate([(CaseOffset.x – Protrusion),
    (CaseOffset.y – Protrusion),
    0])
    cube([(-CaseOffset.x + Protrusion),
    Contacts[0].y + Protrusion – CaseOffset.y,
    CaseSize.z]);
    }
    translate([0,0,BatterySize.z + Gap])
    Lid();
    color("Silver",0.15)
    PinAssembly();
    if (RGBCircuit) {
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z])
    difference() {
    RGBSpider();
    rotate(180-StrutAngle)
    translate([0,0,-Protrusion])
    cube([norm(StrutOC),StrutBase[OD],2*BallPillar.z],center=false);
    }
    color("Green",0.35)
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z + BallOD/2 + BallPillar[LENGTH] – BallChordM])
    sphere(d=BallOD);
    }
    else {
    difference() {
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z])
    Spider();
    translate([-BallPillar[OD],BatterySize.y/2,2*BatterySize.z – Protrusion])
    cube([BallPillar[OD],StrutOC.y,2*BallPillar.z],center=false);
    }
    color("Green",0.35)
    translate([0,BatterySize.y/2,2*BatterySize.z + BallOD/2 + BallPillar[LENGTH] – BallChordM])
    sphere(d=BallOD);
    }
    }
    if (Layout == "Build") {
    rotate(90) {
    translate([-BatterySize.x/2,-BatterySize.y/2,BaseThick])
    Case();
    translate([-CaseSize.x + LidSize.x,-(LidSize.y/2 + LidOffset.y),0])
    Lid();
    if (RGBCircuit)
    translate([StrutOC.x + BatterySize.x/2,0,0])
    RGBSpider();
    else
    translate([StrutOC.x + BatterySize.x/2,0,0])
    Spider();
    }
    }
    if (Layout == "Show") {
    Case();
    translate([0,0,(BatterySize.z + Gap)])
    Lid();
    color("Silver",0.25)
    PinAssembly();
    if (RGBCircuit) {
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z])
    RGBSpider();
    color("Green",0.35)
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z + BallOD/2 + BallPillar[LENGTH] – BallChordM])
    sphere(d=BallOD);
    }
    else {
    translate([StrutOC.x/2,BatterySize.y/2,2*BatterySize.z])
    Spider();
    color("Green",0.35)
    translate([0,BatterySize.y/2,2*BatterySize.z + BallOD/2 + BallPillar[LENGTH] – BallChordM])
    sphere(d=BallOD);
    }
    }

  • Minilathe MT3 Spindle Collet Fitting

    I’ve used the LMS set of inch-size MT3 spindle collets on occasion, but releasing them required an unseemly amount of drawbar battering. It recently occurred to me to check their fit in the spindle taper:

    Minilathe - MT3 collet - taper test
    Minilathe – MT3 collet – taper test

    Huh.

    The only place they touch the spindle is right around the base, so it’s no wonder they clamp poorly and release grudgingly. I tried several others with the same result.

    Cross-checking shows a much closer fit along the entire length of the dead center, so it’s not the spindle’s fault:

    Minilathe - Dead Center - MT3 taper check
    Minilathe – Dead Center – MT3 taper check

    Stipulated: we’re not talking toolroom precision here

    I set the collets on centers:

    Minilathe - MT3 collet - drive setup
    Minilathe – MT3 collet – drive setup

    And proceeded to file away the offending section to move the clamping force closer to the business end of the collet:

    Minilathe - MT3 collet - filed result
    Minilathe – MT3 collet – filed result

    I did the small collets, the ones I’m most likely to need, and left the big ones for another rainy day.

    They don’t have much clamping range and seem good only for exact-inch-size rods.

    I should lay in a stock of ER16 and maybe ER32 collets for small stuff.