Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
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
On the Y axis:
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
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.
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
And from the 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();
}
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
The previous version doesn’t look much different from what this one will become:
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 flash mob was here and gone within the span of an hour:
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.
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):
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
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
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
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
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
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
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
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.
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
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!
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!