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.

Month: July 2012

  • Sherline Leadscrew Nut Removal

    The Y axis on my Sherline CNC mill has developed about 8 mils of backlash, a bit more than seems reasonable. Some poking around shows that the anti-backlash nut is loose while in the middle of the leadscrew and snug while at either end, which suggests the leadscrew thread is also worn. That’s no surprise, as I didn’t figure out that having a bellows over the leadscrew was a Good Thing until, let us say, considerably later than I should.

    If I must replace the leadscrew, I may as well take the whole XY assembly apart, clean everything, and replace the consumables. So I ordered a sack o’ parts from Sherline; they’re all cheap and readily available. The overall index has the exploded diagrams and the parts list for my mill boiled down to:

    • 54161 Y axis leadscrew (9 inch)
    • 50140 Y axis anti-backlash nut
    • 50200 Y axis nut
    • 50171 X axis leadscrew
    • 50130 X axis anti-backlash nut
    • 40890 X axis nut
    • 50150 anti-backlash lock

    The only gotcha: nowhere (that I can find, anyway) is it written how to get the leadscrew nuts out of the stage. It turns out that the holes through the stage aren’t uniform: the X narrower on the right and the Y on the front, so you must drive the X axis nut out to the left and the Y axis nut out to the rear. The counterbore is visible just behind the anti-backlash nut if you know what to look for, so you’re driving the axis nut away from the backlash nut.

    On the X axis:

    X axis leadscrew hole counterbore
    X axis leadscrew hole counterbore

    On the Y axis:

    Y axis leadscrew hole counterbore
    Y axis leadscrew hole counterbore

    Trust me on this: you cannot drive a 5/16 inch nut through a 19/64 inch counterbore. If you have a 19/64 inch transfer punch, that’s a dandy way to get the nuts out.

    The easiest way to loosen the socket head cap screw holding the flex coupling to the leadscrew is to grab the coupling in a lathe chuck (with the leadscrew protruding into the headstock) and then apply the hex key:

    Loosening leadscrew bolt
    Loosening leadscrew bolt

    They used red (high-strength) Loctite on all the leadscrew bolts, as well as on the tapered joint between the leadscrew and the flex coupling, and on the bearing preload nut… so I will, too.

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

  • Another Dirac Delta: Moen Faucet Repair (?!)

    Can anyone explain how it is that, all of a sudden, Yahoo! Image Search generates 466 hits for my post on repairing our Moen bathroom sink faucet, a sleepy post in my Long Tail that’s been ticking along at one view per day since last December?

    The flash mob was here and gone within the span of an hour:

    View spike for Moen faucet post - 2012-04-05
    View spike for Moen faucet post – 2012-04-05

    Does Yahoo! Image Search have something like a featured image of the hour? I can’t find anything obvious, but I cannot imagine what else would cause that many views of a single page that lacks buck-naked celebrities, jackass stunts, or hideously embarrassing personal revelations.

    Given that all of their “trending image searches” show (typically female) human faces, I doubt that the grubby innards of a faucet would appear in that gallery.

    This has happened before from the same source…

  • Philips Sonicare Essence 5000: Battery Replacement

    Back when I got a Philips Sonicare (on the recommendation of my dental hygenist, after a particularly nasty bout of plaque removal), the battery gave nearly two weeks of service between charges. As shown in that graph, the runtime gradually faded away to two days, at which point I decided it was time to tear the thing apart and see about replacing the batteries.

    The instruction manual tells how to dismantle the case and extract the NiCd battery for recycling:

    Please note that this process is NOT reversible.

    Well, there’s a challenge if I ever read one, but Wouldn’t It Be Nice If you could take something apart, unplug its defunct battery, install a new one, and button it up again? Then you wouldn’t be forced to buy a new $70 toothbrush, which probably explains everything… and I suppose the replacement battery would cost $40, even if it were a pair of AA cells.

    For reference, the instructions (clicky for more dots):

    Disassembly Instructions - 1
    Disassembly Instructions – 1
    Disassembly Instructions - 2
    Disassembly Instructions – 2
    Disassembly Instructions - 3
    Disassembly Instructions – 3

    As predicted, suasion applied through a small screwdriver popped the top end of the case apart, but the remainder required concerted prying and muttering. The case halves mate with a tongue-and-groove joint that’s either sonic welded or adhesive bonded to form a watertight seal all the way around, to the extent that they suggested cleaning the thing in a dishwasher.

    Eventually, though, it came apart:

    Sonicare - case opened
    Sonicare – case opened

    The “motor” (actually, a solenoid that couples to the magnet on the brush stem) is firmly potted in place (on the right), as are the NiCd cells and the charging power pickup coil at the base on the left. The potting compound seems to be a clear epoxy, rather than a compliant rubber, and it doesn’t bond to the case at all. It is, however, a perfect fit and doesn’t pop loose without a struggle; their instructions will definitely break the PCB.

    Seen from the other direction, six connections join the PCB to those immovable objects. The four pins (on the far left) go to the solenoid and the pair (just to their right) to the battery:

    Sonicare PCB solder points
    Sonicare PCB solder points

    A few dabs of desoldering wick suffice to free the pins and release the PCB. Mercifully, the potting compound surrounding the charging coil slid out easily, as they (inexplicably) omitted a mechanical lock molded into the case:

    Sonicare - PCB removed
    Sonicare – PCB removed

    Removing the NiCd cells required considerable prying, as described in the instructions, that en passant damaged their cases. I think if you weren’t paying attention, you could easily rupture a cell case with the screwdriver and spatter the area with potassium hydroxide, perhaps shorting the cell in the process and producing rather more excitement than most folks expect.

    A closeup of one cell; the other bears similar damage:

    Sonicare - damaged NiCd cells
    Sonicare – damaged NiCd cells

    I snipped off the cell tabs and applied them to the new NiMH cells. A bit of closed-cell foam between the cells and the PCB cushions the assembly:

    Sonicare - new NiMH cells on PCB
    Sonicare – new NiMH cells on PCB

    Stacking more foam snippets under the cells filled the space left by the potting compound, then soldering the solenoid pins held everything together:

    Sonicare - new NiMH in place
    Sonicare – new NiMH in place

    A wrap of clear adhesive (rather than the obligatory Kapton) makes for a tidy joint that probably won’t last very long, but it looks much the way it did before the operation. The case is no longer waterproof and won’t withstand the dishwasher. In fact, I must now store it with the brush end downward to keep the last few drops out of the handle.

    There’s an interesting solder jumper on the PCB that I didn’t bridge, but the next time it’s opened up I’ll apply a dab:

    Sonicare - BLINKY jumper
    Sonicare – BLINKY jumper

    The alert reader will notice that I’ve replaced 2000 mA·h AA NiCd cells with 600 mA·h 2/3 AANiMH cells, without changing the charger. The power transfer through the inductive coupling drives a trickle charger at about one hour of recharge per brushing, so there’s not much danger of overcharging the cells.

    Now, to discover what runtime fresh cells deliver. This calls for another slip of geek scratch paper in the bathroom.

  • Sonicare E5000 Toothbrush: Battery Decline

    Being that type of guy, I noted each date when my Sonicare toothbrush needs recharging, at least after the battery had declined to about a week between charges, specifically so I could produce this graph:

    Sonicare Runtime
    Sonicare Runtime

    The peaks include trips where I didn’t use the toothbrush and I’ve certainly blundered a few dates, but you can eyeball a trendline: those cells are kaput!

    In round numbers, I bought the thing in early 2010, so the cells lasted maybe 2-½ years. I routinely run the toothbrush until the blinky light indicates that it needs charging, then fill it up overnight, to avoid having the cells fail like the ones in the beard trimmer.

    Somehow the notion of discarding the whole toothbrush seems wrong, even though the instruction manual describes how to remove the battery for recycling before you junk the carcass. Talk about planned obsolescence!

    Ah, but I know what to do about that

  • It’s a Small World

    This quarterly tabulation doesn’t include the hundred-odd of you who click in through the RSS feed from wherever you may beq, but it’s in the right ballpark:

    Country Views
    United States 37459
    United Kingdom 4366
    Canada 4243
    Germany 2501
    Australia 2342
    Netherlands 1759
    Finland 1723
    India 1584
    France 1491
    Spain 1343
    Italy 1304
    Brazil 1157
    Poland 959
    Belgium 815
    Russian Federation 748
    Argentina 717
    Indonesia 627
    Portugal 614
    Turkey 591
    Sweden 582
    Thailand 559
    Romania 538
    Republic of Korea 528
    Malaysia 521
    Mexico 510
    Czech Republic 505
    New Zealand 496
    Philippines 476
    Hungary 468
    Japan 458
    Denmark 452
    Singapore 443
    Switzerland 427
    Greece 415
    Viet Nam 401
    Taiwan 385
    Ukraine 345
    Ireland 314
    Israel 304
    Norway 304
    Bulgaria 300
    Austria 295
    South Africa 276
    Lithuania 245
    Croatia 240
    Slovakia 238
    Slovenia 232
    Hong Kong 225
    Pakistan 203
    Serbia 198
    Colombia 176
    Latvia 160
    Egypt 159
    Estonia 136
    Chile 129
    Moldova 114
    United Arab Emirates 108
    Saudi Arabia 103
    Peru 86
    Tunisia 67
    Venezuela 60
    Sri Lanka 60
    Iceland 59
    Bosnia and Herzegovina 55
    Costa Rica 54
    Belarus 53
    Malta 53
    Ecuador 51
    Macedonia 49
    Cyprus 46
    Morocco 46
    Syrian Arab Republic 38
    Bangladesh 37
    Algeria 35
    Panama 34
    Puerto Rico 34
    Lebanon 32
    Trinidad and Tobago 31
    Iraq 29
    Jamaica 28
    Kuwait 28
    Uruguay 26
    Armenia 26
    Kenya 26
    Bermuda 26
    El Salvador 24
    Jordan 23
    Georgia 23
    Qatar 22
    Luxembourg 21
    Guatemala 21
    Cambodia 21
    Brunei Darussalam 20
    Mongolia 18
    Albania 18
    Dominican Republic 17
    China 17
    Bahrain 16
    Mauritius 15
    Nepal 15
    Monaco 14
    Oman 13
    Ghana 13
    Bolivia 13
    Sudan 11
    Honduras 10
    Nigeria 10
    Guam 10
    Libyan Arab Jamahiriya 9
    Paraguay 9
    Namibia 7
    San Marino 7
    Montenegro 7
    Maldives 7
    Macao 7
    Yemen 7
    Malawi 6
    Barbados 6
    Ethiopia 5
    Vanuatu 5
    Fiji 5
    Togo 4
    Netherlands Antilles 4
    Bahamas 4
    Cameroon 4
    Mauritania 4
    Nicaragua 4
    Botswana 4
    Norfolk Island 3
    Myanmar 3
    Uganda 3
    Andorra 3
    Belize 3
    Côte d’Ivoire 2
    Lao People’s Democratic Republic 2
    Mozambique 2
    Cape Verde 2
    French Guiana 2
    Swaziland 2
    Liechtenstein 2
    Azerbaijan 2
    Jersey 2
    Cuba 2
    Haiti 1
    Liberia 1
    Guyana 1
    Niger 1
    New Caledonia 1
    Zimbabwe 1
    British Virgin Islands 1
    Guadeloupe 1
    Northern Mariana Islands 1
    United Republic of Tanzania 1
    Senegal 1
    Antigua and Barbuda 1
    Madagascar 1
    Cayman Islands 1
    Kyrgyzstan 1
    Afghanistan 1
    Saint Lucia 1
    Papua New Guinea 1

    Mad props to whoever clicked in from Afghanistan: I hope you found whatever you were looking for!