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: Recumbent Bicycling

Cruisin’ the streets

  • Tour Easy: Front Tire Aneurysm

    Front tire aneurysm
    Front tire aneurysm

    Found this aneurysm on the front tire just before a grocery ride, so I stuffed a spare tire and tube into a pannier before rolling away. As expected, it didn’t blow out, but …

    I think this started with a gash in the Kevlar belt that didn’t quite penetrate the cords holding the tire together. As you’ve seen, our tires collect a remarkable number of cuts due to broken glass.

    The cords inside the tire seemed fine, although the weave was somewhat distorted. The inner rubber layer wasn’t punctured, despite what it looks like here.

    The tube also looked fine, despite riding on a tire liner for at least a year. The tube abrasion failures in the rear tire must be due to something other than just the combination of tube and liner; perhaps the tube flexes just enough to erode at the discontinuities.

    This tire went directly to the trash!

  • LED Forward Voltages

    Thinking about those batteries in the context of a really big LED tail light for a bike leads to wondering about the variation in LED forward voltages; it’s possible to drive LEDs in parallel if they’re well-matched for forward voltage. A quick-and-dirty test is in order to get some first-pass numbers… and I have bags of nominally identical red and amber LEDs.

    Applying a fixed voltage that produces 20 mA through 14 randomly chosen LEDs of each color, then measuring the voltage across each diode:

    LED Red V Amber V
    1 1.895 1.939
    2 1.893 1.921
    3 1.903 1.918
    4 1.895 1.921
    5 1.891 1.918
    6 1.935 1.906
    7 1.891 1.926
    8 1.904 1.930
    9 1.901 1.923
    10 1.894 1.927
    11 1.901 1.914
    12 1.894 1.939
    13 1.901 1.933
    14 1.903 1.925
    Minimum 1.891 1.906
    Average 1.900 1.924
    Maximum 1.935 1.939

    Pushing 20 mA through the five lowest voltage red LEDs requires 9.54 V. Applying that voltage to the five highest red LEDs produces 18.2 mA.

    Putting those two strings-of-five in parallel with 9.52 V produces 40 mA total: 16.6 mA in the low string and 19.9 mA in the high string, all measured with a fancy Tek Hall effect probe. No, those aren’t reversed and, yes, I did check twice: it makes no sense at all.

    Temperature matters a lot in such measurements and I wasn’t controlling for that, plus I didn’t have a constant-current supply. Better numbers await better instrumentation, but I think binning a couple bags of 100 LEDs based on forward current should be straightforward.

  • Wouxun KG-UV3D Battery Capacity

    After I get the next GPS+voice interface running on the (yet-to-be-bought) Wouxun KG-UV3D radio, a pair of reasonably new 1A17KG-3 7.4 V 1.7 A·h lithium battery packs will be floating around with nothing to do; the GPS interface connects an external battery to the radio, so there’s no need for the OEM battery.

    Before doing anything else, it’d be useful to know the actual capacity. The pack has flush terminals, so I snipped off two lengths of shield braid, jammed a wire into each one, and taped them in place:

    Battery pack - braid contacts
    Battery pack – braid contacts

    That obviously wasn’t going to last, so I added some closed-cell foam:

    Battery pack - foam compression
    Battery pack – foam compression

    And then, ever so gently, crunched a clamp around the whole mess:

    Battery Pack - clamped contacts
    Battery Pack – clamped contacts

    Crude, but workable, although the ragged start to the test showed I was too gentle. Another click of the clamp and everything settled down just fine:

    Wouxun Pack
    Wouxun Pack

    In round numbers, the pack delivers 1.6 A·h down to 7.0 V and then falls off very rapidly to the 6.0 V that ended the test.

    A string of three red / amber LEDs adds up to 3×1.9 = 5.7 V. A dumb DC blinky light running from 7.4 V has 77% efficiency, which isn’t all that bad, and 70% at the start. A current-regulating switcher might give 85% to 90% at the cost of considerable circuit complexity and wouldn’t be feasible for four independent blinky channels.

    The starting voltage, fresh from the charger, is just shy of 8.5 V, which is why I figured I could get away with 9 V from the external pack through the GPS interface. So far, so good.

    Obviously, if those packs are to be useful, I must conjure up a better battery holder. Having already designed a battery-shaped case for the GPS interface, it should be easy enough to build a radio-shaped mount for the pack.

  • Wouxun KG-UV3D GPS+Voice Interface: Improved Case

    This case has a few refinements beyond that one, but it’s recognizably a descendant. The main changes:

    • The HT cable port on the side has a nice polygonal roof to reduce overhang
    • The serial connector sits in a recess to allow a thicker top plate
    • Smaller opening for the LEDs; I’ll get a window in this one, fer shure, yeah
    • 4-40 screws hold the base plate on; setscrews may work and look better

    Looks like I’ll be using blue filament for this version, having just discovered the last of the weird colors in the bottom of the 5 gallon bucket serving as a storage bin.

    A view from the top:

    Solid Model - Oblique Exploded Top
    Solid Model – Oblique Exploded Top

    And from the base:

    Solid Model - Oblique Exploded Base
    Solid Model – Oblique Exploded Base

    The OpenSCAD source code:

    // Wouxun KB-UV3D Battery Pack Case
    // Ed Nisley KE4ZNU July 2012
    
    include </home/ed/Thing-O-Matic/lib/MCAD/units.scad>
    include </home/ed/Thing-O-Matic/Useful Sizes.scad>
    
    // Layout options
    
    Layout = "Show";
    					// Overall layout: Fit Show
    					// Printing plates: Build1 .. Buildn (see bottom!)
    					// Parts: TT3 Audio DSub Shell Base Top
    					// Shapes: RadioBase Contact
    					// Speaker-mic mount: PlugPlate
    
    ShowGap = 10;		// spacing between parts in Show layout
    
    //- Extrusion parameters must match reality!
    //  Print with +1 shells and 3 solid layers
    
    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
    
    //----------------------
    // Dimensions
    
    CaseOverallHeight = 31.5;				// from battery surface, must clear PCBs!
    CaseOverallWidth = 56;
    CaseOverallLength = 80.25;				// inside of base to end of compartment
    
    BatteryClearance = 1.5;					// contact seal height = air gap to compartment
    
    // Interface to radio battery contacts
    //	Length = shell length
    //		calculated after everything else, so as to fill the compartment
    
    ContactDia = 6.0;				// use rounded contact for simplicity
    ContactRecess = IntegerMultiple(0.75,ThreadThick);	// recess for contact plate
    ContactGapX = 10.5;				// X space between contacts
    Contact1Y = 52.5;				// offset from base to edge of contact
    Contact2Y = 56.5;
    ContactStudDia = Clear4_40;
    ContactStudHead = IntegerMultiple(Head4_40,ThreadWidth);
    ContactStudHeadThick = Head4_40Thick;
    
    PlateWidthMin = 53.0;
    PlateWidthMax = 54.5;
    PlateThick = IntegerMultiple(ContactRecess + ContactStudHeadThick,ThreadThick);
    PlateAngle = atan(PlateThick/(PlateWidthMax/2 - PlateWidthMin/2));
    
    echo("Battery plate thick: ",PlateThick);
    
    // Offsets from battery surface to PCB centerlines
    //	TT3 must be above HT back shell for DB9 clearance
    //	These must cooperate with the numbers in the case shell module
    
    TT3Offset = 17.5 + PlateThick;
    AudioOffset = 4.0 + PlateThick;
    
    // Plate interface to base alignment holes and notches
    
    BaseWidthInner = PlateWidthMin;
    BaseWidthOuter = CaseOverallWidth;
    BaseLength = CaseOverallHeight;					// perpendicular to battery surface
    BaseThick = IntegerMultiple(1.0,ThreadThick);	// minimum sheet thickness below teeth
    BaseWidthTaper = 5.0;							// ramp across entire width
    
    BaseOpeningMax = 43.0;
    BaseOpeningMin = 33.0;
    BaseOpeningY = 5.3;
    BaseOpeningDepth = IntegerMultiple(2.25,ThreadThick);
    
    BaseTotalThick = BaseThick + BaseOpeningDepth;
    echo("Base min thick: ",BaseThick," total: " ,BaseTotalThick);
    
    BaseTabWidth = 6.0;
    BaseTabThick = 2.0;
    BaseTabGap = 7.0;
    BaseTabOC = BaseTabWidth + BaseTabGap;
    BaseToothSection = 3*BaseTabWidth + 2*BaseTabGap;
    
    BaseToothBase = 5.8;
    BaseToothTip = 2.8;
    BaseToothThick = 2.0;
    BaseToothAngle = atan(BaseOpeningDepth/0.6);
    BaseToothOC = BaseTabOC;
    
    WedgeAngle = atan(BaseWidthTaper/((BaseWidthOuter - BaseWidthInner)/2));
    
    BaseEndLip = ThreadThick;			// should be 0.25 mm or so
    BaseEndWidth = (PlateWidthMin - 3*BaseToothBase - 2*BaseToothTip)/2;
    BaseEndAngle = atan((BaseOpeningDepth - BaseEndLip)/BaseOpeningY);
    
    SwitchBody = [8.6,3.7,3.3];			// mode switch
    
    // Plate interface to HT battery latch, cables, and connectors
    
    TopThick = IntegerMultiple(5.5,ThreadThick);	// plate thickness for stiffness behind latch bar
    echo("Top plate thick: ",TopThick);
    
    DB9Recess = TopThick - 4.0;			// recess to max TT3 PCB clearance behind DB9 plate
    
    TabEngageLength = 1.6;				// tab engaging surface length
    TabWidth = 3.0;						//  ... width
    TabEngageHeight = 4.5;				//  ... above battery compartment floor
    TabHeight = 7.5;					// tab ramp top above battery compartment floor
    TabOC = 40.0;
    
    LatchBarWidth = 3.4;				// sliding latch mechanism (brass L stock)
    LatchBarDepth = 3.4;
    LatchBarThick = 0.35;
    
    echo(" ... minimum: ",TopThick - LatchBarDepth);
    
    SplitOffset = TT3Offset - 3.5;
    
    TopBevel = 1.0;						// bevel at top of battery compartment
    TopBevelAngle = 45;
    
    PinOffsetWidth = 2.5;				// choose to center in sides of case shell
    PinOffsetHeight = 13.5;				// above baseplate bottom
    PinDepth = 7.0;						// into case shell
    PinDia = 1.2;
    
    ShellLength = CaseOverallLength - BaseThick - TopThick;
    
    echo("Shell length: ",ShellLength);
    
    // Speaker-mic plug plate
    
    PlugBaseThick = 2.5;				// recess depth
    PlugFillThick = 3.0;				// outer plate thickness
    
    //----------------------
    // 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);
    
    }
    
    //-------------------
    // Component parts
    
    //-----
    // TinyTrak3+ PCB and component envelope
    //	Some dimensions should feed into the case shell, but don't
    
    module TinyTrak3(Length = 1.0) {
    
    PCBThick = 1.6;
    PCBWide = 36.5;
    TopHigh = 9.5;
    TopWide = PCBWide - 1.5;
    BotHigh = 2.5;
    BotWide = 35.0;
    
    PCBx = PCBWide/2;
    PCBy = (PCBThick + HoleWindage)/2;
    URx = TopWide/2;
    URy = PCBy + TopHigh;
    LRx = BotWide/2;
    LRy = PCBy + BotHigh;
    
    linear_extrude(height=Length,center=false,convexity=2) {
    	polygon(points=[[URx,URy],[URx,PCBy],[PCBx,PCBy],[PCBx,-PCBy],[LRx,-PCBy],[LRx,-LRy],
    					[-LRx,-LRy],[-LRx,-PCBy],[-PCBx,-PCBy],[-PCBx,PCBy],[-URx,PCBy],[-URx,URy]
    				   ]);
    }
    }
    
    //-----
    // Interface PCB and component envelope
    //	Some dimensions should feed into the case shell, but don't
    
    module AudioInterface(Length = 1.0) {
    
    PCBThick = 2.0;
    PCBWide = 49.5;
    TopHigh = 9.0 + Protrusion;
    TopWide = 46.0;
    BotHigh = 3.0;
    BotWide = 44.0;
    
    PCBx = PCBWide/2;
    PCBy = (PCBThick + HoleWindage)/2;
    URx = TopWide/2;
    URy = PCBy + TopHigh;
    LRx = BotWide/2;
    LRy = PCBy + BotHigh;
    
    linear_extrude(height=Length,center=false,convexity=2) {
    	polygon(points=[[URx,URy],[URx,PCBy],[PCBx,PCBy],[PCBx,-PCBy],[LRx,-PCBy],[LRx,-LRy],
    					[-LRx,-LRy],[-LRx,-PCBy],[-PCBx,-PCBy],[-PCBx,PCBy],[-URx,PCBy],[-URx,URy]
    				   ]);
    }
    }
    
    //-----
    // DB-9 (DE-9) panel opening
    // http://www.interfacebus.com/Connector_D-Sub_Mechanical_Dimensions.html
    //  DB-9 shell mounts on outside surface of case
    // This is for the solder terminal side
    
    module DSubMin9(Length = 1.0) {
    
    Holex = 0.984/2 * inch;
    HoleDia = Tap4_40;
    
    URx = 0.769/2 * inch;
    URy = 0.432/2 * inch;
    
    	linear_extrude(height=Length,center=false,convexity=3) {
    	  polygon(points=[[URx,URy],[URx,-URy],[-URx,-URy],[-URx,URy]]);
    	  for (x = [-1,1]) {
    		translate([x*Holex,0,0])
    		  rotate(45) circle(r=(HoleDia + HoleWindage)/2,$fn=4);
    	  }
    	}
    
    }
    
    //-----
    // Central case shape
    //	This *should* depend directly on the circuit board sizes, but doesn't
    //	The "Offset" parameters attempt to bottle up all the board sizes
    //	Support in LED window must be hand-fit to work correctly... and isn't needed!
    
    module CaseShell(Length=(ShellLength),Holes="true") {
    
    // Polygon coordinates are in XY plane
    
    URx = 40.0/2;
    URy = CaseOverallHeight;
    
    MRx = CaseOverallWidth/2;
    MRy = 15.0;
    
    LRx = CaseOverallWidth/2;
    LRy = (LRx - PlateWidthMin/2)*tan(PlateAngle);
    
    BRx = PlateWidthMax/2;
    BRy = PlateThick - 0*Protrusion;
    
    PRx = PlateWidthMin/2;				// combined battery plate
    PRy = 0;
    
    ScrewOffset = 20.0;					// from top end of case
    
    LEDWindow = [26.0,5.0,6];			// with case aligned vertically
    LEDOffset = [15,URy,(Length + TopThick - 25.0)];
    
    TrimPot1 = [-14,TT3Offset,(Length + TopThick - 30)];
    TrimPot2 = [-14,TT3Offset,(Length + TopThick - 37.5)];
    
    HTCableDia = 3.5;
    HTCableOffset = AudioOffset + HTCableDia/2 + 1.0;
    
    rotate([90,0,180])
    	union() {
    	  difference() {
    
    		  linear_extrude(height=Length,center=false,convexity=5)
    			polygon(points=[[URx,URy],[MRx,MRy],[LRx,LRy],[BRx,BRy],[PRx,PRy],
    							[-PRx,PRy],[-BRx,BRy],[-LRx,LRy],[-MRx,MRy],[-URx,URy]]);
    
    		if (Holes) {
    		  translate([0,AudioOffset,-Protrusion])
    			AudioInterface(Length + 2*Protrusion);
    
    		  translate([0,TT3Offset,-Protrusion])
    			TinyTrak3(Length + 2*Protrusion);
    
    		  for (y = [TT3Offset,AudioOffset])
    			translate([-CaseOverallWidth,y,(Length - ScrewOffset)])
    			  rotate([0,90,0])
    				rotate(0)					// Z rotation puts point upward for printing
    				PolyCyl(Tap4_40,CaseOverallWidth);
    
    		  translate(LEDOffset)
    			rotate([90,90,0])
    			  translate([-LEDWindow[0]/2,-LEDWindow[1]/2,-Protrusion])
    			  cube(LEDWindow,center=false);
    
    		  for (p = [TrimPot1,TrimPot2])
    			translate(p)
    			  rotate([-90,90,0])				// Y rotation puts point upward for printing
    				PolyCyl(3.0,URy);
    
    		  for (x=[-1,1]) {
    			translate([x*(CaseOverallWidth/2 - PinOffsetWidth),
    					  PinOffsetHeight,
    					  (Length - PinDepth)])
    			  rotate(45)						// align hole sides with case sides
    				  PolyCyl(PinDia,2*TopThick);
    			translate([x*(CaseOverallWidth/2 - PinOffsetWidth),
    					  PinOffsetHeight,
    					  -PlateThick])
    			  rotate(45)						// align hole sides with case sides
    				  PolyCyl(PinDia,(PlateThick + PinDepth));
    		  }
    
    		  for (x=[-1,1])						// setscrews to secure base plate
    			translate([x*(CaseOverallWidth/2 - 3*Tap4_40),
    					  TT3Offset,-Protrusion])
    			  rotate(360/(5*4))
    				PolyCyl(Tap4_40,2*TopThick);
    
    		  translate([-(ContactGapX/2 + ContactDia/2),0,(Contact1Y + ContactDia/2)])
    			rotate([90,0,0])
    			  Contact();
    		  translate([+(ContactGapX/2 + ContactDia/2),0,(Contact2Y + ContactDia/2)])
    			rotate([90,0,0])
    			  Contact();
    
    		  translate([CaseOverallWidth/2,HTCableOffset,(Length - HTCableDia/4)])
    			rotate([0,90,0])
    			  cube([(HTCableDia/2 + Protrusion),HTCableDia,CaseOverallWidth],center=true);
    		  translate([0,HTCableOffset,(Length - HTCableDia/2)])
    			rotate([0,90,0])
    			  cylinder(r=(1/cos(30))*HTCableDia/2,h=CaseOverallWidth,$fn=6);
    		}
    	  }
    
    if (false)
    	  if (Holes)
    		translate(LEDOffset)						// support plug in LED window
    		  rotate([90,90,0])
    			translate([-0.95*LEDWindow[0]/2,-0.80*LEDWindow[1]/2,ThreadWidth/2])
    			  cube([0.95*LEDWindow[0],0.80*LEDWindow[1],2*ThreadWidth],center=false);
    
    	}
    
    }
    
    //-----
    // Battery contact recess
    //  This gets subtracted from the bottom plate in two places
    // 	Align points to print upward
    
    module Contact() {
    
    if (true)
    union() {													// vertical printing with case
    	translate([0,0,-(ContactRecess + Protrusion)/2])
    	  PolyCyl(ContactDia,(ContactRecess + Protrusion),8);
    	translate([0,0,-(PlateThick + Protrusion)])
    	  rotate(60/2)
    	  PolyCyl(ContactStudDia,PlateThick,6);
    	translate([0,0,-(ContactRecess + ContactStudHeadThick/3)])
    	  PolyCyl(ContactStudHead,ContactStudHeadThick,8);				// allow for solder blob
    }
    else
    union() {												// horizontal printing alone
    	translate([0,0,-(ContactRecess - Protrusion)/2])
    	  PolyCyl(ContactDia,(ContactRecess + Protrusion),8);
    	translate([0,0,-(PlateThick + Protrusion)])
    	  PolyCyl(ContactStudDia,(PlateThick + 2*Protrusion));
    	translate([0,0,-(ContactRecess + ContactStudHeadThick/3)])
    	  PolyCyl(ContactStudHead,ContactStudHeadThick,8);				// allow for solder blob
    }
    
    }
    
    //-----
    // Radio bottom locating feature
    //  This polygon gets subtracted from the battery pack base
    
    module RadioBase() {
    
    linear_extrude(height=(BaseOpeningDepth + Protrusion),center=false,convexity=5)
    polygon(points=[
    			[-BaseOpeningMax/2,-Protrusion],
    
    			[-BaseOpeningMin/2,BaseOpeningY],
    			[-(BaseToothOC/2 + BaseToothBase/2),BaseOpeningY],
    
    			[-(BaseToothOC/2 + BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    			[-(BaseToothOC/2 - BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    			[-(BaseToothOC/2 - BaseToothBase/2),BaseOpeningY],
    
    			[ (BaseToothOC/2 - BaseToothBase/2),BaseOpeningY],
    			[ (BaseToothOC/2 - BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    			[ (BaseToothOC/2 + BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    			[ (BaseToothOC/2 + BaseToothBase/2),BaseOpeningY],
    			[ BaseOpeningMin/2,BaseOpeningY],
    
    			[ BaseOpeningMax/2,-Protrusion],
    
    			[ (BaseTabOC + BaseTabWidth/2),-Protrusion],
    			[ (BaseTabOC + BaseTabWidth/2),BaseTabThick],
    			[ (BaseTabOC - BaseTabWidth/2),BaseTabThick],
    			[ (BaseTabOC - BaseTabWidth/2),-Protrusion],
    
    			[ BaseTabWidth/2,-Protrusion],
    			[ BaseTabWidth/2,BaseTabThick],
    			[-BaseTabWidth/2,BaseTabThick],
    			[-BaseTabWidth/2,-Protrusion],
    
    			[-(BaseTabOC + BaseTabWidth/2),-Protrusion],
    			[-(BaseTabOC + BaseTabWidth/2),BaseTabThick],
    			[-(BaseTabOC - BaseTabWidth/2),BaseTabThick],
    			[-(BaseTabOC - BaseTabWidth/2),-Protrusion],
    		  ],
    		  convexity=5
    );
    }
    
    //-----
    // Battery pack base
    
    module Base() {
    
    difference() {
    
    	rotate([-90,180,0])						// main case shape
    	  CaseShell(BaseTotalThick,false);
    
    	translate([0,0,BaseThick])				// radio base interface
    	  RadioBase();
    
    	translate([0,0,BaseThick])				// tooth bevel
    	  rotate([(-90 + BaseToothAngle),0,0])
    		translate([0,-0.5,0])
    		  cube([(BaseToothSection + 2*Protrusion),1.0,10],center=true);
    
    	translate([-BaseWidthOuter,				// surface slope
    			  -(BaseThick + BaseEndLip)/tan(BaseEndAngle),0])
    	  rotate([BaseEndAngle,0,0])
    		difference() {
    		  cube([2*BaseWidthOuter,3*BaseOpeningY,BaseOpeningDepth],center=false);
    		  translate([(BaseWidthOuter - (BaseToothSection + 2*Protrusion)/2),0,0])
    			cube([(BaseToothSection + 2*Protrusion),1.2*BaseOpeningY,BaseOpeningDepth],center=false);
    		}
    
    	for (x=[-1,1])							// alignment pin holes
    	  translate([x*(CaseOverallWidth/2 - PinOffsetWidth),PinOffsetHeight,-Protrusion])
    		rotate(45)							// align hole side with plate side
    		  PolyCyl(PinDia,2*TopThick);
    
    	for (x=[-1,1])							// mounting setscews
    	  translate([x*(CaseOverallWidth/2 - 3*Tap4_40),
    				TT3Offset,-Protrusion])
    		rotate(-360/(-5*4))
    		  PolyCyl(Tap4_40,2*TopThick);
    
    	translate([(-SwitchBody[0]/2),TT3Offset,-SwitchBody[2]/2])	// mode switch
    	  scale([1,1,2])
    		cube(SwitchBody);
    
    }
    }
    
    //-----
    // Top plate with latch
    //	Split around TinyTrak3 serial connector
    //	 ... which must be at the same height as in the shell!
    //	The cable hole sizes & locations are entirely ad-hoc
    
    module TopPlate() {
    
    Cable1Dia = 5.0;
    Cable2Dia = 5.0;
    CableHoleLength = TopThick + 2*Protrusion;
    CableHoleZ = -Protrusion;
    
    DB9Plate = [32.0,13.5,1.25];					// plate surrounding connector body
    
    difference() {
    
    	rotate([-90,180,180])
    	  CaseShell(TopThick,false);
    
    	translate([0,-TT3Offset,-Protrusion])
    	  DSubMin9(TopThick + 2*Protrusion);
    
    	translate([0,-TT3Offset,(TopThick - DB9Plate[2]/2)])
    	  cube([DB9Plate[0],DB9Plate[1],(DB9Plate[2] + Protrusion)],center=true);
    
    	translate([-CaseOverallWidth,-SplitOffset,-2*Protrusion])		// split the plate
    	  cube([2*CaseOverallWidth,4*Protrusion,(TopThick + 2*Protrusion)]);
    
    	translate([0,0,(TopThick - TopBevel)])
    	  rotate([-TopBevelAngle,0,0])
    		translate([-CaseOverallWidth,-TopThick,0])
    		  cube([2*CaseOverallWidth,2*TopThick,2*TopThick],center=false);
    
    	for (x=[-1,1])
    	  translate([(x*TabOC/2),
    				(-TabHeight/2 + Protrusion),
    				(TopThick - TabEngageLength/2 + Protrusion/2)])
    		rotate([90,0,0])
    		  cube([TabWidth,
    				(TabEngageLength + Protrusion),
    				(TabHeight + Protrusion)],center=true);
    
    	translate([-CaseOverallWidth,
    			  -(TabEngageHeight + LatchBarWidth - BatteryClearance),
    			  (TopThick - LatchBarDepth)])
    	  cube([2*CaseOverallWidth,(LatchBarWidth + LatchBarThick),(LatchBarDepth + Protrusion)]);
    
    	for (x=[-1,1])
    	  translate([(x*CaseOverallWidth/4),
    				-(TabEngageHeight + LatchBarWidth + Clear2_56/2 - BatteryClearance + Protrusion),
    				0]) {
    		translate([0,0,-Protrusion])
    		  rotate(45)						// align sides with slot
    			PolyCyl(Tap2_56,(TopThick + 2*Protrusion));
    		translate([0,0,(TopThick - LatchBarDepth)])
    		  rotate(60)						// align sides with slot
    			PolyCyl((Head2_56 + Protrusion),TopThick,6);		// extra extra clearance
    	  }
    
    	for (x=[-1,1])
    	  translate([x*(CaseOverallWidth/2 - PinOffsetWidth),-PinOffsetHeight,-Protrusion])
    		rotate(45)						// align hole side with plate side
    		  PolyCyl(PinDia,2*TopThick);
    
    	for (x=[-1,1])						// coincidentally line up with latch tabs
    	  translate([(x*TabOC/2),-(SplitOffset - 3.0),-Protrusion])
    		scale([1,1.7,1])
    		  PolyCyl(Cable1Dia,CableHoleLength,6);
    }
    
    }
    
    //-----
    // Speaker-Mic plug mounting plate
    
    module PlugPlate() {
    
    JackOC = 11.20;						// 14.25 OD - (3.58 + 2.58)/2
    
    JackScrewDia = 4.6;
    JackScrewOffsetX = 1.00;
    JackScrewOffsetY = 5.25;			//  mounting screw to edge of lower recess
    
    PlugBaseWidth = 9.25;				// lower section of plate
    PlugBaseLength = 22.0;
    PlugBaseRadius = 1.75;
    
    Plug3Offset = 5.25;					// edge of base recess to 3.5 mm jack
    
    Plug2BezelDia = 7.1;				// 2.5 mm plug
    Plug2BezelThick = 1.04;
    Plug2ScrewDia = 6.0;
    Plug3ScrewLength = 3.0;
    
    Plug3BezelDia = 8.13;				// 3.5 mm plug
    Plug3BezelThick = 1.6;
    Plug3ScrewDia = 7.95;
    Plug3ScrewLength = 4.0;
    
    PlugFillOffsetX = JackScrewOffsetX - 0.5;		// base recess CL to fill CL
    PlugFillOffsetY = -10.5;				//  ... to edge of fill plate
    PlugFillWidth = 11.0;
    PlugFillLength = 34.00;
    PlugFillRadius1 = 1.5;
    PlugFillRadius2 = 4.5;
    
    PlugFillOffsetYTotal = 0;
    
    BaseX = PlugBaseWidth/2 - PlugBaseRadius;
    BaseY = PlugBaseLength/2 - PlugBaseRadius;
    
    difference() {
    	union() {
    	  linear_extrude(height=PlugBaseThick,center=false,convexity=3)
    		hull() {
    		  translate([-BaseX,-BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([-BaseX, BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([ BaseX, BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([ BaseX,-BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		}
    
    	  translate([PlugFillOffsetX,
    				(PlugFillLength/2 - PlugBaseLength/2 + PlugFillOffsetY),
    				PlugBaseThick])
    		linear_extrude(height=PlugFillThick,center=false,convexity=5)
    		  hull() {
    			translate([0,-(PlugFillLength/2 - PlugFillRadius2),0])
    			  circle(r=PlugFillRadius2,$fn=10);
    			translate([-(PlugFillWidth/2 - PlugFillRadius1),-PlugBaseLength/2,0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([-(PlugFillWidth/2 - PlugFillRadius1),
    					  (PlugFillLength/2 - PlugFillRadius1),0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([(PlugFillWidth/2 - PlugFillRadius1),
    					  (PlugFillLength/2 - PlugFillRadius1),0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([(PlugFillWidth/2 - PlugFillRadius1),-PlugBaseLength/2,0])
    			  circle(r=PlugFillRadius1,$fn=8);
    		  }
    	}
    
    	translate([0,-JackOC/2,-Protrusion])
    	  rotate(360/16) {
    		PolyCyl(Plug3BezelDia,(Plug3BezelThick + Protrusion),8);
    		PolyCyl(Plug3ScrewDia,(PlugBaseThick + PlugFillThick + 2*Protrusion),8);
    	  }
    
    	translate([0,+JackOC/2,-Protrusion])
    	  rotate(360/16) {
    		PolyCyl(Plug2BezelDia,(Plug2BezelThick + Protrusion),8);
    		PolyCyl(Plug2ScrewDia,(PlugBaseThick + PlugFillThick + 2*Protrusion),8);
    	  }
    
    	translate([JackScrewOffsetX,-(PlugBaseLength/2 + JackScrewOffsetY),0])
    	  PolyCyl(JackScrewDia,(PlugBaseThick + PlugFillThick + Protrusion));
    }
    
    }
    
    //-------------------
    // Build things...
    
    ShowPegGrid();
    
    if (Layout == "TT3")
    TinyTrak3();
    
    if (Layout == "Audio")
    AudioInterface();
    
    if (Layout == "DSub")
    DSubMin9();
    
    if (Layout == "Shell")
    CaseShell(CaseOverallLength);
    
    if (Layout == "Top")
    TopPlate();
    
    if (Layout == "Base")
    Base();
    
    if (Layout == "RadioBase")
    RadioBase();
    
    if (Layout == "PlugPlate")
    PlugPlate();
    
    if (Layout == "Contact")
    rotate([180,0,0])
    	Contact();
    
    if (Layout == "Show" || Layout == "Fit") {
    
    translate([0,-ShellLength/2,0]) {
    
    	translate([0,(Layout == "Show")?-ShowGap:0,0])
    	  rotate([90,0,0])
    		color("SandyBrown") Base();
    
    	translate([0,0,0])
    	    color("LightGreen") render() CaseShell();
    
    	translate([-(CaseOverallWidth/2 + 10),50,CaseOverallHeight/2])
    	  rotate([0,-90,0])
    		color("Gold") PlugPlate();
    
    	translate([0,((Layout == "Show")?(ShellLength + ShowGap):ShellLength),0])
    	  rotate([-90,0,0])
    		color("BurlyWood") TopPlate();
    }
    }
    
    if (Layout == "Build1") {
    
    translate([5 + CaseOverallHeight,0,0])
    	rotate([0,0,90])
    	  Base();
    
    translate([-(5 + CaseOverallHeight),0,0])
    	rotate(90)
    	  TopPlate();
    
    }
    
    if (Layout == "Build2") {
    
    translate([0,-CaseOverallHeight/2,ShellLength])
    	rotate([-90,0,0])
    		CaseShell();
    
    }
    
    if (Layout == "Build3") {
    
    translate([0,0,(PlugBaseThick + PlugFillThick)])
    	rotate([180,0,0])
    	  PlugPlate();
    
    }
    
  • Wouxun KG-UV3D GPS+Voice Interface: Electronics

    The latest version of the GPS+Voice electronics for the Wouxun KG-UV3D, which I’m getting ready to build:

    Wouxun KG-UV3D GPS+Voice Schematic
    Wouxun KG-UV3D GPS+Voice Schematic

    A few changes:

    • It runs from those 9 V boosted packs, not 7.4 V direct from their lithium cells
    • U2, the MAX4544 data/voice mux, runs from the shunt-regulated +5 V, not the TT3+ regulator
    • Miscellaneous doc cleanup

    I’m mulling over a capacitor between the TT3+ data output and the earbud, so as to monitor transmissions, but I’m not convinced that’s worthwhile.

    The PCB layout, with wire jumpers on the two inner layers:

    Wouxun KG-UV3D GPS+Voice PCB
    Wouxun KG-UV3D GPS+Voice PCB

    The previous version doesn’t look much different from what this one will become:

    GPS-HT Wouxun interface - brassboard
    GPS-HT Wouxun interface – brassboard

    This will replace the ICOM Z-1A radio and GPS interface on Mary’s bike, which has been working fine for quite a while. That can’t last, so I’m trying to get ahead of the failure curve…

    The Wouxun HT GPS+Audio Interface schematic and PCB layout files, both tucked into a ZIP file with an ODT extension. That is not an OpenDocument file: rename it to remove the ODT extension, then unzip it.

  • Repairing Bike Tubes

    Now that I carry a spare tube on the bike to avoid on-the-road patching, a tangle of tubes has been accumulating in the Basement Laboratory. A protracted patching session shows why you can never have too many clamps:

    Repaired bike tubes with clamps
    Repaired bike tubes with clamps

    Yes, I know they’re supposed to be ready to ride immediately after slapping the patch in place. Clamping the patch overnight won’t hurt and might actually help eliminate slow leaks, soooo… after this, they’re rolled up and ready for another season of punctures.

    Repairing tubes goes much easier in the shop than by the side of the road, though. For what it’s worth, those silicone tape pads didn’t help much at all; the tube still eroded at the liner. Grumble…

  • External DVD Battery Capacity: Followup

    A bit over two years ago, those six 9 V 5.4 A·h lithium packs delivered around 4.5 A·h. They’ve been charged and discharged, run down until their undervoltage lockout tripped, severely jounced and bounced, and they still deliver about 4 A·h at 500 mA!

    External Li-Ion packs - 2012-05
    External Li-Ion packs – 2012-05

    That’s a Good Thing, because I haven’t seen anything like those packs since then…

    Never did get around to installing a cutoff switch, as we ride often enough that the penalty for not pulling the plug gets lost in normal use. The Wouxun KG-UV3D seems perfectly happy with 9 V delivered to its battery terminals, providing little motivation to hack into the battery case for a direct tap to the 7.4 V from the cells.