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

  • Wouxun KG-UV3D Battery Contact Locations

    Having gone to great pains to put the center of the contact studs on the GPS+voice case exactly at the center of the screws on the back of the radio:

    HT-GPS Case - Wouxun KG-UV3D rear view
    HT-GPS Case – Wouxun KG-UV3D rear view

    I now discover why Wouxun used 7 mm square pads on the batteries: the springy contacts hit the pack so far off-center from the studs that they very nearly miss the heads on the 4-40 brass screws I’m using as contacts. This family portrait shows the radio, the battery pack, and the GPS+voice case:

    Wouxun KG-UV3D - battery contact locations - GPS case
    Wouxun KG-UV3D – battery contact locations – GPS case

    The lines on the masking tape highlight where the spring contacts touch the case and barely kiss the screw heads:

    KG-UV3D contact marks on GPS case
    KG-UV3D contact marks on GPS case

    Squinting at the marks on the battery case contacts (you can’t see it in the pictures), the contact line is maybe 2.5 mm beyond the centerline of the square pads. How this worked on the first case I built, I have no clue. For this version, I deliberately filed the heads a bit less and recessed them into the case a bit more; obviously, that was the wrong thing to do, as the connection was intermittent at best.

    For the purposes of getting things working, I wrapped snippets of copper mesh tape (from NASA, according to the surplus blurb, with conductive adhesive) around thin chunks of conductive foam, then put them over the studs. The scars in the plastic came from an abortive attempt to get the springs far enough into the case surface to kiss the very edge of the studs:

    Copper mesh on GPS case contacts
    Copper mesh on GPS case contacts

    There’s no point in having a contact patch on the near side of the radio springs, because nothing ever touches there. So the right thing to do is simply move the contact studs to the far side by 3 mm, centering them around the actual contact point. That means changing the PCB layout by the same amount. That’s easy enough to do, but … drat!

    When I took the case apart to boost the mic gain, I replaced those neatly filed studs with unfiled pan head 4-40 brass screws from the same parts stock. The heads were tall enough to touch the radio spring contacts closer to their centers and make perfect contact. Not elegant, but better than that copper braid tape.

    The one thing I do not like about the Wouxun battery packs: the radio contact pads are flush with the pack surface, so there’s absolutely no protection against casual shorts when the pack isn’t on the radio. The packs also sport four bare round contacts on their outer surface that mate with the charger, two of which make direct contact with the battery; those sit inside a shallow molded recess that helps prevent inadvertent shorts.

    assume there’s a protective circuit inside the pack that turns off the current on a dead short, but I am most assuredly not going to test that assumption. When the packs aren’t on the radio (which they never will be, effective immediately), they sport a strip of tape across those radio contact pads.

  • PL605060 Cells: First Charge

    Five PL605060 lithium cells (prismatic, 3.7 V, 2 A·h) for the longboard project arrived and underwent incoming inspection. The lower curves show the as-received charge state (about halfway) and the upper curves show the first charge:

    PL605060 Li-Ion Cell
    PL605060 Li-Ion Cell

    The crinkly sections come from me nudging the wires (held on by the most tenuous of alligator clip connections) and don’t really affect the results. The five cells charged to within a few percent of each other, which is Good Enough.

    I hotwired each cell to the hacked battery case for the SX230HS camera and used the Canon charger:

    PL605060 cell - charging adapter
    PL605060 cell – charging adapter

    As is turns out, that charger is rated for 700 mA current, exactly the same as the 4-cell charger we’ll use for the longboard battery, so the overall charge rate should be about the same. The Canon cells run around 1.1 A·h and the PL605060 is 2.0 A·h; I figured 2.0 was approximately 1.1 for small values of 2.0. The charge cycle required only a little more time than usual, which isn’t surprising because I stopped the discharge at 3 V rather than 2.8 or whatever.

    Cell C has the highest discharge voltage of the litter, so I’ll conjure it into becoming an external battery for the camera (which prefers higher voltages over higher capacity). I’m not sure how to mount it; a case that screws into the tripod socket is obvious, but it would interfere with the macro lens adapter. Of course, I could just print another macro lens adapter with an integral battery case …

  • Don’t Leave Your Food Unprotected

    This is the season for orb-weaving spiders, one of which laid a great web between a pole and the grass in the front yard. It worked wonderfully well to capture a flying katydid, but wasps got to the victim first:

    Webbed katydid with wasps
    Webbed katydid with wasps

    Maybe a bird took out the spider? We’ll never know, but that katydid won’t go to waste.

    This is a dot-for-dot crop from a handheld shot with the Canon SX230HS, macro setting, plus a dash of unsharp mask and gentle contrast stretching to knock the background down. It’s surprisingly hard to get perfect focus on a wind-blown object; this is the least awful of the group.

  • Spider: Wrong Place, Wrong Time

    I opened the grill cover before lighting it and this critter ran out of the depths:

    Spider in propane grill
    Spider in propane grill

    It eventually crawled up to the gap at the lid, from whence it could bail out over the edge. I fired the barby and, alas, it scuttled in exactly the wrong direction: down its web and directly into the burners.

    Made me feel like crap; spiders are good to have around.

  • Longboard Lighting Case: Final Edition

    After our Larval Engineer allowed as how OpenSCAD’s learning curve was rather too steep, I punched a few holes in the solid model of the case for the Longboard Ground Effect Lighting controller:

    Longboard Case Solid Model - with holes
    Longboard Case Solid Model – with holes

    Those rounded corners sucked the Kapton tape right off the build platform as the massive shape shrank. The top layer was the worst offender, with 1.4 mm of clearance (shown with that tapered scale) under one corner:

    Longboard case - warped corner
    Longboard case – warped corner

    The warping doesn’t matter much, because the case will be compression-loaded by screws and wave washers in the corners. We may need to fill or level the warp to keep the polycarbonate cover flat, though.

    I thought about putting a support structure in the rectangular power switch opening, then decided to just try it and see what happens. It turned out fine; this view looks up toward the as-printed top of the opening (the camera’s barrel distortion makes the curve on the bottom surface look worse than it is):

    Longboard case - switch hole overhang
    Longboard case – switch hole overhang

    Four stacked lithium cells produce upwards of 14.8 V, considerably more than those poor 12 V LED strips prefer to see, so I had her take some current vs. voltage data. She figured out how to convert 10-bit ADC values into battery voltage, after which she could, if she wanted to, beat her Arduino sketch into limiting the maximum PWM duty cycle to hold the LED power dissipation down to a reasonable number. Right now, it’s set to a fixed 25% and is way bright.

    Longboard with variable RGB LED Ground Effect lighting
    Longboard with variable RGB LED Ground Effect lighting

    A truly crappy First Light video taken in the driveway is there. She’s been doing the Happy Dance all day… and promises to document the whole project in gruesome detail.

    The OpenSCAD source code:

    // Longboard Ground Effect Lighting Controller Case
    // Ed Nisley KE4ZNU
    // Karen Nisley KC2SYU
    // August 2012
    
    // Layout options
    
    Layout = "Build3";
    					// 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 = 15.0;				// for connections
    
    CellHoldWidth = 6.0;					// edge to tabs
    CellHoldLength = 4*ThreadWidth;
    
    CellCount = 4;						// cells in the battery
    
    BatteryHeight = CellCount*CellThick + 2*CellClearance;
    BatteryLength = CellLength + 2*CellClearance;
    BatteryWidth = CellWidth + 2*CellClearance;
    
    PCMWidth = 16.0;						// Battery protection module
    PCMLength = 51.0;
    PCMThick = 4.0;							// at terminal end of cells
    
    PillarOD = Washer10_32OD + 2*1.0;		// screw pillar diameter
    PillarOffset = (PillarOD/2) / sqrt(2.0);	// distance to case inside corner
    
    WallThick = 7.5;						// case wall thickness
    
    PinOD = 1.4;							// alignment pin size
    
    CaseInsideLength = BatteryLength + CellTabClearance + PCMThick;
    CaseOALength = CaseInsideLength + 2*WallThick;
    echo("Box Length outside: ",CaseOALength);
    echo("            inside: ",CaseInsideLength);
    
    WiringLength = CaseInsideLength - CellLength - CellHoldLength - PCMThick;	// wiring space at PCM
    echo("Wiring length: ",WiringLength);
    
    CaseInsideWidth = BatteryWidth;
    CaseOAWidth = CaseInsideWidth + 2*WallThick;
    echo("Box Width outside: ",CaseOAWidth);
    echo("           inside: ",CaseInsideWidth);
    echo("Screw OC length: ",CaseInsideLength + 2*PillarOffset);
    echo("          width: ",CaseInsideWidth + 2*PillarOffset);
    
    PCBThick = 2.0;							// PCB thickness
    PCBMargin = 3.0;						// clamping margin around PCB edge
    PartHeight = 17.0;						// height of components above PCB (mind the switch!)
    WiringThick = 5.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);
    
    ChargePlugOD = 11.5;					// battery charger plug
    ChargeJackHeightOC = 6.5;				// coaxial jack center pin height from PCB
    
    SwitchLength = 20.0;					// master power switch
    SwitchWidth = 13.0;
    
    WheelCableOD = 3.0;						// 3-conductor from wheel rotation sensor
    
    LEDCableWidth = 10.0;					// 6 conductor loose wires to LED strips
    LEDCableThick = 2.0;
    
    //----------------------
    // 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]) {
    	  union() {
    		translate([-(CaseInsideLength/2 - BatteryLength/2),0,0])
    		  cube([BatteryLength,
    			   BatteryWidth,
    			   BatteryHeight + 2*Protrusion],
    			   center=true);
    		cube([CaseInsideLength,
    			 (BatteryWidth - 2*CellHoldWidth),
    			 BatteryHeight + 2*Protrusion],
    			 center = true);
    		translate([(CaseInsideLength/2 - WiringLength/2),0,0])
    		  cube([WiringLength,
    				max(BatteryWidth,PCMLength),
    				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();
    	translate([25,0,(PCBThick/2 + ChargeJackHeightOC)])
    	  rotate([90,0,0])
    		PolyCyl(ChargePlugOD,CaseOAWidth);
    	translate([25,CaseOAWidth/2,PCBLayer2Thick/2])
    	  rotate([90,0,0])
    		cube([SwitchLength,SwitchWidth,CaseOAWidth],center=true);
    	translate([-CaseOALength/2,0,PCBThick/2])
    	  rotate([0,-90,0])
    		cube([2*WheelCableOD,WheelCableOD,CaseOALength],center=true);
    	translate([CaseOALength/2,0,PCBThick/2])
    	  rotate([90,0,90])
    		cube([LEDCableWidth,2*LEDCableThick,CaseOALength],center=true);
      }
    
    }
    
    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(something here!!!);
    
    if ("BatteryLayer" == Layout)
      BatteryLayer();
    
    if ("PCBEnvelope" == Layout)
      PCBEnvelope();
    
    if ("PCBLayer1" == Layout)
      PCBLayer1();
    
    if ("PCBLayer2" == Layout)
      PCBLayer2();
    
    if ("Fit" == Layout) {
      color("LightBlue") BatteryLayer();
      translate([0,0,BatteryHeight + PlateThick])
    	color("Green") PCBLayer1();
      translate([0,0,BatteryHeight + PlateThick + PCBLayer1Thick])
    	color("Cyan") PCBLayer2();
    }
    
    if ("Show" == Layout) {
      color("LightBlue") 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();
    
    
  • TinyTrak3+ Trimpots: Not All Are Created Equal

    I designed the GPS+Audio case around the TinyTrak3+ board in my radio, which has two square, blue-plastic trimpots. The case worked fine for that board. Then I printed the case for the next bike and that TT3+ didn’t slide neatly into place:

    TinyTrak3+ trimpot overhang
    TinyTrak3+ trimpot overhang

    Turns out that one of the three TT3+ boards uses plastic trimpots and the other two have metal trimpots bent to fit the existing holes (so they’re not a drop-in replacement), with a very slight overhang beyond the edge of the PCB.

    So I attacked the case with some riffler files and carved a notch above the PCB slot. No pictures of that, lest you think I’m a butcher of lovely 3D printed objects. Next time: build the notch into the case’s solid model.

    Most likely, this is the only instance of those pots causing anyone a problem…

  • Wouxun KG-UV3D Audio Levels

    Wouxun KG-UV3D with GPS-audio interface
    Wouxun KG-UV3D with GPS-audio interface

    The Wouxun KG-UV3D is advertised as a “dual band” radio, but it has only one hardware receiver: in TDR mode (there is no explanation of what TDR means, so there may not be an English equivalent; I suspect it’s not Time Domain Reflectometry) with two frequencies / channels displayed, the first to receive a transmission produces audio output until that signal stops, regardless of what happens on the other frequency / channel. In contrast, the ICOM Z1A and W32A radios we were using had two hardware receivers and the audio output was the sum of the two signals, with independent volume controls.

    That wouldn’t matter, except that I monitor the E911 dispatch channel while riding, so that I know when an emergency vehicle will be coming along my route: distracted drivers are bad enough, but a distracted driver dodging an ambulance is really bad. The E911 transmitters have punchy audio compared to anything else, so it’d be nice to turn down the dispatcher’s level compared to the relatively quiet voice + APRS signals on the other channel.

    No can do.

    The KG-UV3D also requires much higher audio on the mic input than the Z1A for the equivalent output. Contrary to that schematic, I’m now running the op amp gain at about 4.5 (13 dB) instead of 1.6 (4 dB): it’s a 100 kΩ feedback resistor. That puts it on a par with the E911 audio, but it’s still somewhat quiet.

    The TinyTrak3+ board produces audio tones through a 4-bit binary resistor network that feeds into a 220 kΩ resistor in series with the 10 kΩ trimpot that sets its output level. Cranking that pot all the way up produces roughly the same volume as the +13 dB helmet mic audio. If I increase the mic gain any further, however, I should also increase the TT3+ audio output, which means reducing the 220 kΩ resistor on the TT3+ board. The TT3+ doc advises:

    Some mobile radios require more audio drive than TinyTrak3 puts out. If audio levels are too low, even with the R6 pot set to maximum, consider replacing the 220K R5 with a 100K resistor or shorting jumper. This should allow for about double the audio range.

    Dunno if that means another 3 or 6 dB or what, but it might come in handy.

    However, increasing the mic gain has the disadvantage of causing more wind noise: it’s always there and high mic gain makes it much worse. The foam balls over the mics work well, but the voice volume drops off dramatically as the mouth-to-mic distance increase; about half an inch is a good distance. So there’s an upper limit on mic gain.

    I’ve also increased the earphone attenuation, with a 150 Ω resistor in series with the earbud, to give the receiver volume control more useful range.

    It’s workable as it stands and the many APRS receivers have no trouble decoding the packets, so all this is in the nature of fine tuning. I do miss the dual audio outputs, though…