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.

Author: Ed

  • Longboard Ground Effect Lighting Case

    Our Larval Engineer has been diligently procrastinating on her summer project to add ground effect lighting to her longboard. I’m hereby depriving her of the opportunity to learn enough OpenSCAD to build a case from scratch:

    Longboard Ground Effect Lighting Case - exploded view
    Longboard Ground Effect Lighting Case – exploded view

    This is upside-down from its in-use position, but she’ll have it in this orientation on the bench. Four 10-32 screws clamp the whole affair together and hold it to a bottom aluminum plate with threads to suit; that plate also gets bolted between the longboard and the rear truck.

    The general idea is that four 2 A·h lithium prismatic cells live in the bottom slice with their protection circuit, sandwiched between two aluminum plates that should protect them from all but catastrophic impact. The circuit board (which ought to be a PCB, but we’ll go with hand wiring for the first iteration) gets clamped in the recess between the two upper slices, above the upper aluminum plate. A polycarbonate sheet on top provides visibility for the Arduino blinky LED inside and shows off the circuitry to one and all.

    I think a ridge on each long wall should suffice to hold the cells against the end wall; we don’t have the cells in hand to figure that out yet. She gets to add internal partitions, cable cutouts, and suchlike.

    Oh. “Ground effect lighting” means ten RGB LED strips glued under the longboard deck. Her innovation is to make the LED color depend on the speed, which can range upward to scary-fast. It’s a simple matter of software, using a Hall effect sensor for input. This will look much better after dark, but she’s pretty much nocturnal anyway.

    The OpenSCAD source code:

    // Longboard Ground Effect Lighting Controller Case
    // Ed Nisley KE4ZNU
    // Karen Nisley KC2SYU
    // July 2012
    
    // Layout options
    
    Layout = "Show";
     // Overall layout: Fit Show
     // Printing plates: Build1 .. Buildn (see bottom!)
     // Parts: BatteryLayer PCBLayer1 PCBLayer2
     // Shapes: CaseShell PCBEnvelope
    
    ShowGap = 5; // spacing between parts in Show layout
    
    //-----
    // Extrusion parameters must match reality!
    
    ThreadThick = 0.25;
    ThreadWidth = 2.0 * ThreadThick;
    
    HoleWindage = 0.2;
    
    //-- Handy stuff
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1; // make holes end cleanly
    
    inch = 25.4;
    
    Tap10_32 = 0.159 * inch;
    Clear10_32 = 0.190 * inch;
    Head10_32 = 0.373 * inch;
    Head10_32Thick = 0.110 * inch;
    Nut10_32Dia = 0.433 * inch;
    Nut10_32Thick = 0.130 * inch;
    Washer10_32OD = 0.381 * inch;
    Washer10_32ID = 0.204 * inch;
    
    //----------------------
    // Dimensions
    
    CellWidth = 50.0; // Lithium-ion cell dimensions
    CellLength = 60.0;
    CellThick = 6.0;
    CellClearance = 1.5; // on all sides
    CellTabClearance = 10.0; // for connections
    
    BatteryCount = 4; // cells in the battery
    BatteryHeight = BatteryCount*CellThick + 2*CellClearance;
    
    PCMWidth = 16.0; // Battery protection board
    PCMLength = 50.0;
    PCMThick = 4.0; // at terminal end of cells
    
    PillarOD = Washer10_32OD + 2*0.5; // screw pillar diameter
    PillarOffset = (PillarOD/2) / sqrt(2.0); // distance to case inside corner
    
    WallThick = 6.0; // case wall thickness
    
    PinOD = 1.4; // alignment pin size
    
    CaseOALength = CellLength + CellClearance + CellTabClearance + PCMThick + 2*WallThick;
    CaseInsideLength = CaseOALength - 2*WallThick;
    echo("Box Length outside: ",CaseOALength);
    echo(" inside: ",CaseInsideLength);
    
    CaseOAWidth = CellWidth + 2*CellClearance + 2*WallThick;
    CaseInsideWidth = CaseOAWidth - 2*WallThick;
    echo("Box Width outside: ",CaseOAWidth);
    echo(" inside: ",CaseInsideWidth);
    
    CaseOAHeight = BatteryCount * CellThick + CellClearance;
    
    PCBThick = 1.0; // PCB thickness
    PCBMargin = 3.0; // clamping margin around PCB edge
    PartHeight = 10.0; // height of components above PCB
    WiringThick = 4.0; // wiring below PCB
    
    echo("PCB thickness:",PCBThick);
    echo(" clamp margin: ",PCBMargin);
    echo(" wiring: ",WiringThick);
    echo(" components: ",PartHeight);
    
    PCBLayer1Thick = IntegerMultiple(WiringThick + PCBThick/2,ThreadThick);
    PCBLayer2Thick = IntegerMultiple(PartHeight + PCBThick/2,ThreadThick);
    
    echo("Battery compartment height: ",BatteryHeight);
    echo("PCB Layer 1 height: ",PCBLayer1Thick);
    echo("PCB Layer 2 height: ",PCBLayer2Thick);
    
    PlateThick = 1/16 * inch; // aluminum mount / armor plates
    
    echo("Total height: ",2*PlateThick + BatteryHeight + PCBLayer1Thick + PCBLayer2Thick);
    
    //----------------------
    // 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) {
    
    Range = floor(50 / Space);
    
    for (x=[-Range:Range])
     for (y=[-Range:Range])
     translate([x*Space,y*Space,Size/2])
     %cube(Size,center=true);
    
    }
    //-------------------
    // Shapes
    
    module CaseShell(h=1.0) {
    
    difference() {
     union() {
     translate([0,0,h/2])
     cube([CaseOALength,CaseOAWidth,h],center=true);
    
    for (x=[-1,1])
     for (y=[-1,1])
     translate([x*(PillarOffset + CaseInsideLength/2),
     y*(PillarOffset + CaseInsideWidth/2),
     h/2])
     cylinder(r=PillarOD/2,h,center=true,$fn=4*6);
     }
    
    for (x=[-1,1]) // screw holes on corners
     for (y=[-1,1])
     translate([x*(PillarOffset + CaseInsideLength/2),
     y*(PillarOffset + CaseInsideWidth/2),
     -Protrusion])
     PolyCyl(Clear10_32,(h + 2*Protrusion),8);
    
    for (x=[-1,1]) // alignment pins in width walls
     translate([x*(CaseOALength - WallThick)/2,0,-Protrusion])
     rotate(45)
     PolyCyl(PinOD,(h + 2*Protrusion));
     for (y=[-1,1]) // alignment pins in length walls
     translate([0,y*(CaseOAWidth - WallThick)/2,-Protrusion])
     rotate(45)
     PolyCyl(PinOD,(h + 2*Protrusion));
    
    }
    }
    
    module BatteryLayer() {
    
    difference() {
     CaseShell(BatteryHeight);
    
    translate([0,0,BatteryHeight/2])
     cube([CaseOALength - 2*WallThick,
     CaseOAWidth - 2*WallThick,
     BatteryHeight + 2*Protrusion],
     center=true);
     }
    }
    
    module PCBEnvelope() {
    
    union() {
     translate([0,0,WiringThick + PCBThick + PartHeight/2])
     cube([CaseInsideLength - 2*PCBMargin,
     CaseInsideWidth - 2*PCBMargin,
     PartHeight + 2*Protrusion],
     center=true);
    
    translate([0,0,WiringThick + PCBThick/2])
     cube([CaseInsideLength,CaseInsideWidth,PCBThick],center=true);
    
    translate([0,0,WiringThick/2])
     cube([CaseInsideLength - 2*PCBMargin,
     CaseInsideWidth - 2*PCBMargin,
     WiringThick + 2*Protrusion],
     center=true);
     }
    }
    
    module PCBLayer1() {
    
    difference() {
     CaseShell(PCBLayer1Thick);
     PCBEnvelope();
     }
    
    }
    
    module PCBLayer2() {
    
    difference() {
     CaseShell(PCBLayer2Thick);
     translate([0,0,-(WiringThick + PCBThick/2)])
     PCBEnvelope();
     }
    
    }
    
    module Aluminum() {
     translate([0,0,PlateThick/2])
     cube([1.1*CaseOALength,1.1*CaseOAWidth,PlateThick - Protrusion],center=true);
    }
    
    //-------------------
    // Build things...
    
    ShowPegGrid();
    
    if ("Battery" == Layout)
     Battery();
    
    if ("CaseShell" == Layout)
     CaseShell(CaseOAHeight);
    
    if ("BatteryLayer" == Layout)
     BatteryLayer();
    
    if ("PCBEnvelope" == Layout)
     PCBEnvelope();
    
    if ("PCBLayer1" == Layout)
     PCBLayer1();
    
    if ("PCBLayer2" == Layout)
     PCBLayer2();
    
    if ("Fit" == Layout) {
     BatteryLayer();
     translate([0,0,BatteryHeight + PlateThick])
     color("Green") PCBLayer1();
     translate([0,0,BatteryHeight + PlateThick + PCBLayer1Thick])
     color("Cyan") PCBLayer2();
    }
    
    if ("Show" == Layout) {
     BatteryLayer();
     translate([0,0,BatteryHeight + PlateThick + ShowGap])
     color("Green") PCBLayer1();
     translate([0,0,BatteryHeight + PlateThick + PCBLayer1Thick + 2*ShowGap])
     color("Cyan") PCBLayer2();
    }
    
    if ("Build1" == Layout)
     rotate(90) BatteryLayer();
    
    if ("Build2" == Layout)
     rotate(90) PCBLayer1();
    
    if ("Build3" == Layout)
     translate([0,0,PCBLayer2Thick])
     rotate([0,180,90])
     PCBLayer2();
    
  • Wouxun HT GPS+Voice Interface: Circuitry!

    After a few sessions of soldering-and-checking, it looks good:

    HT-GPS PCB - cabled in place
    HT-GPS PCB – cabled in place

    The yellow wires on the far right are temporary power connections; battery power enters through the contact studs in those large holes that press against the radio’s battery terminals. The cable in the lower right is the mis-color-coded USB cable that carries audio to & from the earbud & mic on the helmet. Not all the pads have components; I didn’t use all the parallel bypass cap locations because I wasn’t up for protracted self-resonance measurements.

    The TinyTrak3+ cable solders into the empty DB9 footprint over on the left. I must cannibalize that from the ICOM IC-Z1A interface in Mary’s bike after the next Wouxun KG-UV3D arrives; with any luck, there’ll be a rainy day or two for that work.

    The as-built schematic (clicky for more dots), which is pretty close to the original intent:

    Schematic - Wouxun HT GPS+Voice Interface - August 2012
    Schematic – Wouxun HT GPS+Voice Interface – August 2012
  • Wouxun KG-UV3D Plug Plate

    Based on those measurements that suggest spacing the plugs at 11.5 mm on center, I tweaked that parameter in the source code there and printed another one, just like the other one. Actually, I printed four of the fool things this time:

    Wouxun plug plates - 11.5 mm fixture
    Wouxun plug plates – 11.5 mm fixture

    With the plugs in the gluing fixture and the fixture in the vise, a ring of epoxy around the threaded sides holds them in place:

    Wouxun plug plate - wired
    Wouxun plug plate – wired

    A trial fit in the Wouxun KG-UV3D shows that the jacks prefer the 11.2 mm spacing I measured on the Wouxun headset, but they’ll accept plugs on 11.5 mm centers. I don’t know if that’s a real specification difference, a manufacturing tolerance, or what.

    FWIW, I’ve been using snippets of that cable forever, because it’s perfect for this application: two unshielded conductors and three more inside a braid, supple as a snake. It’s surplus, of course, with a gorgeous push-lock plug (and the jack!) on one end that must have cost a fortune… and which I’ll never to use for anything. Got two of them, just in case.

    Mushing an epoxy putty turd on the top anchors everything in place and protects the wires:

    Wouxun plug plate - epoxy cap
    Wouxun plug plate – epoxy cap

    In point of fact, the cable insulation isn’t anchored inside the blob and a minor tug could pull it loose. There will be a bit of slack at the case to allow for unlatching it from the radio, but the lashup will spend its entire life inside a snug pouch, so it shouldn’t come to any harm. We shall see.

  • Aphids on Milkweed

    We have a fine patch of milkweed in the back yard that attracts & nourishes the Monarch butterfly fleet. One of the plants also attracted a dense aphid population:

    Aphids on milkweed
    Aphids on milkweed

    They’re pretty much featureless orange blobs, although the one on the edge of the leaf at the upper right does show off its legs & antennae:

    Aphids on milkweed - detail
    Aphids on milkweed – detail

    Where are the ladybugs when you need them?

  • Kenwood / Wouxun Headset Jack Spacing

    Wouxun plug mounting plate - overview
    Wouxun plug mounting plate – overview

    Try as I might, I cannot uncover a definitive answer to this simple question: What’s the center-to-center spacing of the mic and earphone jacks on the side of Kenwood and Wouxun HTs?

    The usual searches produce answers like 11 and 12 mm, both of which are obviously wrong, as can be determined eyeballometrically just by holding a scale against the plugs.

    Based on measurements I made on a Wouxun headset, the yellow plug mounting plate put the plugs on 11.2 mm centers and they fit into the KG-UV3D radio; it’s been working fine ever since.

    However, having just measured a speaker/mic and a headset, both from Kenwood, I come up with 11.5 mm. Frankly, I trust the Kenwood hardware a bit more: the plugs seem more rugged and the overall production values are higher.

    The calculation is simple: measure the pin diameters, then subtract half their sum from the outside distance across the pins. Cross-check by adding half the sum to the inside distance between the pins, which should give the same answer. It helps if the pins are actually round.

    The jacks in the Kenwood and Wouxun radios have enough compliance to accept either a Wouxun or a Kenwood headset plug without complaint. Maybe it doesn’t matter?

    Despite that, I made another gluing fixture with 11.5 mm spacing:

    Plug alignment plate - 11.5 mm spacing
    Plug alignment plate – 11.5 mm spacing

    Those are 0.1 inch grids; it’s a little bitty block of smoke-gray polycarbonate from the scrap heap. The plugs are nominally 3.5 mm (which is not 1/8 inch in this universe) and 2.5 mm, with clearance drills #28 and #39.

    Then I tried poking those 11.2 mm spaced plugs, now firmly epoxied in place in the yellow plate, and guess what: they don’t fit, no how no way. That’s not surprising, because there’s no compliance on either side of the joint and the plugs aren’t on the right centers for the fixture. Makes for a good No-Go gauge, I suppose.

    However, I think I’ll tweak the solid model spacing to 11.5 mm and run off another plug mounting plate for the next radio.

    FWIW, our ICOM IC-Z1A HTs use a sensible 10.0 mm spacing and that old fixture worked fine.

  • HT GPS+Voice: Battery Contacts

    For this version of the contacts (the old version is there) that make the GPS interface look like a standard Wouxun lithium battery, I left a bit more of the slot on the brass screw heads and increased the recess depth to compensate:

    HT-GPS Case - Battery contacts
    HT-GPS Case – Battery contacts

    The nuts all have fancy nickel plating, with washers & ring lugs silver-soldered in place:

    HT-GPS PCB - battery contact parts
    HT-GPS PCB – battery contact parts

    The trial fit looks OK:

    HT-GPS Case - PCB and battery contacts - end view
    HT-GPS Case – PCB and battery contacts – end view

    I even found the cutest little flat 1/4 inch wrench that fits 4-40 nuts, so I can do a better job of crunching the PCB between the nuts. That excess screw length has got to go, too…

  • Monthly Picture: Hornet in Coreopsis

    Another picture from the Quaker Hill trip, where good light made all the difference:

    Hornet in Coreopsis
    Hornet in Coreopsis

    The flower is a Coreopsis and the insect is not a honeybee. The metallic highlights make it look artificial; if I wasn’t there in person, I’d think it was CGI, too.

    It’s underexposed by about one stop to prevent those mirrored body panels from burning out and to saturate yellow petals in direct sunlight. Hand-held with the Canon SX230HS in macro mode, then cropped to 1600×1200 without any resizing at all; it’s now the background for the landscape monitor.