Archive for June, 2011

Monthly Aphorism: On Non-Economic Repairs

  • The skills we acquire fixing stuff that we don’t care about serve us well when we have to fix something that actually matters

Courtesy of John Rehwinkel.

A long time ago, I read this in E. E. “Doc” Smith’s The Skylark of Space:

He could study safeblowing fifteen minutes and be top man in the field

Even back then, I knew knowledge didn’t work that way. If your fingers haven’t done it, you don’t know how to do it. The more you do it, the better you get.

Go fix something!

4 Comments

Helmet Mirror Mount: Solid Model

Helmet mirror mount - 3D model - Fit layout

Helmet mirror mount - 3D model - Fit layout

After a bit of OpenSCAD twiddling, those doodles turned into a printable model. This view shows what it looks like all neatly assembled:

The tiny hole on the top of the Elevation Body accepts a 2-56 setscrew that grabs the arc protruding from the Elevation Plate and locks the up-and-down setting. The Azimuth Mount pivots on the 3-48 screw holding it to the Elevation Mount.

Both of those pivots must be loose enough to move when you bump the mirror and tight enough to stay put in normal use. It’s a delicate balance and I’m not convinced this will work for the long term, but it’s a brassboard.

The 2-56 stud on the end of the mirror shaft screws into a socket in the rear side of the Az Mount. Another 2-56 setscrew in the Az Mount (facing the El Body), grabs the side of the shaft and prevents it from rotating.

All the parts lay out on their backs for printing, with a grid to show how they fit on the build platform:

Helmet mirror mount - 3D model - Show layout

Helmet mirror mount - 3D model - Show layout

The mirror shaft shoulder on the Az Mount (front center) sticks out in mid air and requires a little bit of support.

The El Mount (left rear) builds surprisingly well with its curved top surface downward. If it’s rotated 90 degrees with the curve facing to the left, Skeinforge grumps about not being able to do something or another and generates totally bogus G-Code.

The Helmet Plate has a 3 mm deep depression that more-or-less corresponds to the helmet’s surface. It’s gouged out by a huge sphere sitting on the plate, with a radius calculated from the measured helmet curvature.

The OpenSCAD source code has two useful parameters near the top:

  • Layout selects the overall appearance: Fit, Show, or Build
  • Examine selects a single part for inspection & tweakage

You’ll need the MCAD and Visibone libraries to make this work. It’s the original code, without the tweaks to the grid mentioned in the comments there:

// Helmet mirror mount
// Ed Nisley KE4ZNU June 2011

include </home/ed/Thing-O-Matic/lib/MCAD/units.scad>
include </home/ed/Thing-O-Matic/lib/MCAD/boxes.scad>
include </home/ed/Thing-O-Matic/lib/visibone_colors.scad>

//-- Layout Control

Layout = "Show";					// Build Fit Show None

Examine = "None";				// AzMount ElMount ElBody ElPlate HelmetPlate None

//-- Extrusion parameters

ThreadThick = 0.33;
ThreadWT = 2.0;
ThreadWidth = ThreadThick * ThreadWT;

HoleWindage = 0;			// enlarge hole dia by this amount

//-- Useful sizes

Tap2_56 = 0.070 * inch;
Clear2_56 = 0.082 * inch;
Head2_56 = 0.156 * inch;
Head2_56Thick = 0.055 * inch;
Nut2_56Dia = 0.204 * inch;
Nut2_56Thick = 0.065 * inch;

Tap3_48 = 0.079 * inch;
Clear3_48 = 0.096 * inch;
Head3_48 = 0.184 * inch;
Head3_48Thick = 0.058 * inch;
Nut3_48Dia = 0.201 * inch;
Nut3_48Thick = 0.073 * inch;

Tap4_40 = 0.089 * inch;
Clear4_40 = 0.110 * inch;
Head4_40 = 0.211 * inch;
Head4_40Thick = 0.065 * inch;
Nut4_40Dia = 0.228 * inch;
Nut4_40Thick = 0.086 * inch;

//-- Azimuth Mount

AzMountDia = 12.0;
AzMountLength = 14.0;

AzFacets = 30;

echo(str("Azmuth mount dia: ",AzMountDia," length: ",AzMountLength));

//-- Mirror sizes

MirrorShaftDia = 3.60;
MirrorShaftOffset = -1.5;				// vertical offset from center of AzMountBody
MirrorShoulderLen = 3*MirrorShaftDia;
MirrorShoulderDia = min(AzMountDia,MirrorShaftDia + 6*ThreadWidth);

MirrorStudDia = Tap3_48;
MirrorStudLen = 2.0;

//-- Elevation Mount / Body / Plate

ElMountDia = AzMountDia;
ElMountLength = 2.0 + ElMountDia;

ElMountBase = 2.0;

ElMountRounding = 2.0;

ElMountFacets = AzFacets;

ElBodyWidth = ElMountDia;
ElBodyBlockLength = ElMountLength + AzMountLength/2 - MirrorShaftOffset;
ElBodyThick = 8.0;

echo(str("Elevation body overall: ",(ElBodyBlockLength + ElBodyWidth/2)," width: ",ElBodyWidth));

ElPlateTall = ElBodyBlockLength + 0.70*ElBodyWidth;
ElPlateWidth = 1.25 * ElPlateTall;
ElPlateThick = ceil(4.0 / ThreadThick) * ThreadThick;

ElPlatePlusX = ElPlateThick + (ElMountDia/2 + ElMountBase) + ElBodyThick;

echo(str("Elevation plate tall: ",ElPlateTall," width: ",ElPlateWidth));

ElArcRadius = (3/4) * ElBodyBlockLength;
ElArcThick = 4*ThreadWidth;
ElArcHeight = (1/2) * ElBodyThick;
ElArcAngle = 35;
ElArcFacets = 32;

ElPlateFacets = 52;

//-- Helmet Interface Plate

HelmetCX = 60.0;
HelmetMX = 4.0;
HelmetRX = (pow(HelmetMX,2) + pow(HelmetCX,2)/4)/(2*HelmetMX);

HelmetPlateC = max(ElPlateTall,ElPlateWidth);
HelmetPlateTheta = atan(HelmetPlateC/HelmetRX);
HelmetPlateM = 2*HelmetRX*pow(sin(HelmetPlateTheta/4),2);

HelmetPlateThick = ThreadThick*(ceil(HelmetPlateM/ThreadThick) + 1);

//-- Bearing Interfaces

BearingWidth = 3*ThreadWidth;

BearingOverlap = 3*ThreadThick;
BearingClearance = 1*ThreadThick;

BearingStudDia = min(AzMountDia,ElBodyWidth) - 2*BearingWidth;

//-- Convenience values

Protrusion = 0.1;		// make holes look good

PegSize = 1.0;

//----------------------
// Useful routines

module PolyCyl(Dia,Height) {			// based on nophead's polyholes

  Sides = ceil(Dia) + 2;
  FixDia = Dia / cos(180/Sides);

  cylinder(r=(FixDia + HoleWindage)/2,
           h=Height,
	   $fn=Sides);
}

module ShowPegGrid(Size) {
	for (x=[-5:5])
	  for (y=[-5:5])
		translate([x*10,y*10,Size/2])
		  cube(Size,center=true);

}

//----------------------
// Azimuth Mount

module AzMount() {

  difference() {
	union() {
	  cylinder(r=AzMountDia/2,h=AzMountLength,$fn=AzFacets);		// body
	  translate([0,0,AzMountLength/2 + MirrorShaftOffset])
		rotate([-90,0,0])
		  cylinder(r=MirrorShoulderDia/2,
				   h=MirrorShoulderLen,$fn=AzFacets);				// mirror shaft shoulder

	  if (Layout != "Fit")
		for (y=[0:1])												// shoulder support
		  translate([-AzMountDia/2,(4*y + AzMountDia/2 + ThreadWidth),0])
			difference() {
			  cube([AzMountDia,2*ThreadWidth,AzMountLength/6]);
			  translate([AzMountDia/2,-Protrusion,AzMountLength/2 + MirrorShaftOffset])
				rotate([-90,0,0])
				  cylinder(r=MirrorShoulderDia/2,h=ThreadWidth + 2*Protrusion);
			}
	}

    translate([0,-Head3_48/2,AzMountLength/2 + MirrorShaftOffset])
      rotate([-90,0,0])
		PolyCyl(MirrorShaftDia,(AzMountDia + MirrorShoulderLen));	// mirror shaft

    translate([0,-(Head3_48/2 - Protrusion),AzMountLength/2 + MirrorShaftOffset])
	  rotate([90,0,0])
		PolyCyl(MirrorStudDia,MirrorStudLen+Protrusion);			// mirror stud

    translate([0,0,
	       Head3_48Thick - (AzMountLength - MirrorShaftDia)/2 + MirrorShaftOffset - Protrusion])
      PolyCyl(Head3_48,AzMountLength + Protrusion);					// mounting screw head

    translate([0,0,-Protrusion])
      cylinder(r=(Clear3_48 + HoleWindage)/2,
	  h=(AzMountLength + 2*Protrusion),
	  $fn=ceil(Clear3_48)+2);										// mounting screw clearance

	translate([0,0,AzMountLength/2 + Head3_48Thick + MirrorShaftDia/2 + MirrorShaftOffset - Protrusion])
	  cylinder(r1=(Head3_48/cos(180/7) + HoleWindage)/2,
			   r2=Clear3_48/2,
			   h=(3*ThreadThick + Protrusion),
			   $fn=7);												// overhang support

    translate([0,0,AzMountLength/2 + MirrorShaftOffset])
      rotate([0,90,0])
		PolyCyl(Tap2_56,AzMountDia/2 + Protrusion);					// setscrew hole

    translate([0,0,AzMountLength - (BearingOverlap + BearingClearance)])
	  PolyCyl(BearingStudDia,
			  BearingOverlap + BearingClearance + Protrusion);		// bearing surface

  }

}

//----------------------
// Elevation Mount

module ElMount() {

  difference() {

	union() {

	  translate([(ElMountDia/4 + ElMountBase/2),0,(ElMountLength/2 + BearingOverlap)])
		rotate([0,90,0])
		  cube([ElMountLength,ElMountDia,(ElMountDia/2 + ElMountBase)],
			  center=true);											// mounting block

	  translate([0,0,BearingOverlap]) {
//		color([0.4,0.3,0.3,0.7])
		cylinder(r=ElMountDia/2,
				 h=ElMountLength - ElMountDia/2,
				 $fn=ElMountFacets);								// cylinder to Az

//		color([0.3,0.4,0.3,0.7])
		translate([0,0,ElMountLength - ElMountDia/2]) {				// curved interface
		  intersection() {
			cylinder(r=ElMountDia/2,h=ElMountDia/2,$fn=ElMountFacets);
			translate([0,ElMountDia/2,0])
			  rotate([90,0,0])
				cylinder(r=ElMountDia/2,h=ElMountDia,$fn=ElMountFacets);
		  }
		}

	  }

	  cylinder(r=(BearingStudDia - HoleWindage)/2,h=BearingOverlap);	// bearing stud
	}

	translate([0,0,-Protrusion])
	  PolyCyl(Tap3_48,(3/4)*ElMountLength + BearingOverlap + Protrusion);	// AzMount screw
  }
}

//----------------------
// Elevation Body

module ElBody() {

  difference() {
	union() {
	  translate([-ElBodyBlockLength,-ElBodyWidth/2,0])
		cube([ElBodyBlockLength,ElBodyWidth,ElBodyThick]);
	  translate([0,0,ElBodyThick])
		cylinder(r=(ElBodyWidth - 2*BearingWidth)/2,h=BearingOverlap);
	  cylinder(r=ElBodyWidth/2,h=ElBodyThick,$fn=ElMountFacets);
	}

	PolyCyl(Clear3_48,ElBodyThick + BearingOverlap + Protrusion);

	translate([0,0,-Protrusion])
	  PolyCyl(Head3_48,Head3_48Thick);

	translate([-ElArcRadius,0,ElBodyThick - ElArcHeight/2])
	  rotate([0,-90,0])
		PolyCyl(Tap2_56,ElBodyBlockLength - ElArcRadius + Protrusion);

	translate([0,0,ElBodyThick - (ElArcHeight + BearingClearance)])
	  difference() {
		cylinder(r=ElArcRadius + (ElArcThick/2 + BearingClearance),
				 h=ElArcHeight + BearingClearance + Protrusion,
				 $fn=ElArcFacets);
		cylinder(r=ElArcRadius - (ElArcThick/2 + BearingClearance),
				 h=ElArcHeight + BearingClearance + Protrusion,
				 $fn=ElArcFacets);
	  }

  }

}

//----------------------
// Elevation Plate

module ElPlate() {

  union() {
	difference() {
	  translate([ElBodyWidth/2 - ElPlateTall/2,0,0])
		scale([ElPlateTall,ElPlateWidth,1.0])
		  cylinder(r=0.5,h=ElPlateThick,$fn=ElPlateFacets);
	  translate([0,0,-Protrusion])
		PolyCyl(Tap3_48,ElPlateThick + 2*Protrusion);
	  translate([0,0,ElPlateThick - (BearingOverlap + BearingClearance)])
		PolyCyl(BearingStudDia,(BearingOverlap + BearingClearance) + Protrusion);
	  translate([0,0,-Protrusion])
		cylinder(r=Nut3_48Dia/2,h=(1.1*Nut3_48Thick + Protrusion),$fn=6);
	}

	translate([0,0,ElPlateThick])
	difference() {
	  cylinder(r=ElArcRadius + ElArcThick/2,
			   h=ElArcHeight,
			   $fn=ElArcFacets);
	  cylinder(r=ElArcRadius - ElArcThick/2,
			   h=ElArcHeight + Protrusion,
			   $fn=ElArcFacets);
	  rotate([0,0,90 - ElArcAngle])
	    translate([ElArcRadius + ElArcThick,0,ElArcHeight/2])
		  cube([2*ElArcRadius + ElArcThick,
				2*ElArcRadius + ElArcThick,
				ElArcHeight + Protrusion],
				center=true);
	  rotate([0,0,-(90 - ElArcAngle)])
	    translate([ElArcRadius + ElArcThick,0,ElArcHeight/2])
		  cube([2*ElArcRadius + ElArcThick,
				2*ElArcRadius + ElArcThick,
				ElArcHeight + Protrusion],
				center=true);
	}
  }
}

//----------------------
// Helmet Interface Plate

module HelmetPlate() {

  difference() {
	scale([ElPlateTall,ElPlateWidth,1.0])
	  cylinder(r=0.5,h=HelmetPlateThick,$fn=ElPlateFacets);

	translate([0,0,HelmetRX + HelmetPlateThick - HelmetPlateM])
	  sphere(r=HelmetRX,$fn=256,$fs=0.1);

  }
}

//----------------------
// Lash it together

if (Examine == "AzMount")
  AzMount();

if (Examine == "ElMount")
  ElMount();

if (Examine == "ElBody")
  ElBody();

if (Examine == "ElPlate")
  ElPlate();

if (Examine == "HelmetPlate")
  HelmetPlate();

if ((Layout == "Build" || Layout == "Show") && Examine == "None") {
  translate([-10,-20,0])
	rotate([0,0,90])					// mis-align top fill from ElMount
	  AzMount();

  translate([-10,20,ElMountLength + BearingOverlap])
	rotate([0,180,-90])
	  ElMount();

  translate([0,0,0])
	rotate([0,0,0])
	  ElBody();

  translate([10,15,0])
	rotate([0,0,215])					// mis-align top fill from ElBody
	  ElPlate();

  translate([20,-20,0])
	rotate([0,0,-45])
	  HelmetPlate();

  if (Layout == "Show")
	ShowPegGrid(PegSize);

}

if ((Layout == "Fit") && Examine == "None") {
  translate([0,0,-(AzMountLength/2 + MirrorShaftOffset)])
	color(MFG) AzMount();

  translate([0,0,AzMountLength/2 - MirrorShaftOffset - BearingOverlap])
	color(DHC) ElMount();
//	color([  0/255, 204/255, 204/255,0.5]) ElMount();

	translate([ElMountDia/2 + ElMountBase,0,0])
	  rotate([0,90,0])
		color(DFC) ElBody();

	translate([ElPlatePlusX,0,0])
	  rotate([180,90,0])
		color(LHC) ElPlate();

	translate([ElPlatePlusX,0,ElPlateTall/2 - ElBodyWidth/2])
	  rotate([0,90,0])
		color(LWM) HelmetPlate();
}

,

3 Comments

OpenSCAD Layout Grid

OpenSCAD Build Surface Grid

OpenSCAD Build Surface Grid

This OpenSCAD module spreads an array of cubes across the otherwise featureless preview window, so I know whether the gizmo I’m building or the parts I’m arranging actually fit on the Thing-O-Matic’s build platform. This doesn’t get out to the very edge, but if it looks close, then I should pay more attention anyway.

module ShowPegGrid(Size) {

 for (x=[-5:5])
  for (y=[-5:5])
   translate([x*10,y*10,Size/2])
    cube(Size,center=true);

}

ShowPegGrid(1.0);

You obviously don’t want to extrude these things, so put the ShowPegGrid() statement inside an if, so you can turn it off for the final build layout.

8 Comments

Stepper Motor Winding Current Rise Time

Here’s how the stepper drive voltage affects the current rise, using that kludge to sync the scope on one of those motors with L=2.6 mH and R=2.2 Ω. The peak winding current is 1 A, so the first step current-limits at 200 mA.

At 9 V:

Current Rise - 9 V 1A 3 RPS

Current Rise - 9 V 1A 3 RPS

At 18 V:

Current Rise - 18 V 1A 3 RPS

Current Rise - 18 V 1A 3 RPS

Knowing the rise time and current change, you can calculate the actual voltage across the inductor using:

VL = L di/dt

With 9 V drive the motor sees:

4.4 V = 2.6 mH x 220 mA / 130 us

With 18 V drive the motor sees:

14 V = 2.6 mH x 240 mA / 45 us

So, in round numbers, the driver MOSFETs, winding resistance, and all the crappy solderless breadboard connections soak up about 4 V of the available supply voltage. There’s some back EMF in there, too, but I haven’t measured that part of the puzzle yet.

The motor is turning at 3 rev/s in 1/8 microstepping mode, so each microstep is:

200 us = 1/(3 rev/s x 1600 step/rev)

7 Comments

Helmet Mirror: Mirror Modifications

Mirror shaft - 2-56 stud

Mirror shaft - 2-56 stud

This 2-56 stud will hold the mirror shaft into whatever helmet mount I eventually decide on. It’s a pan-head screw that miraculously fits snugly inside the cut-down shaft section, held in with a delicate epoxy dribble around the edge.

The head abuts the end of the smaller shaft section, so the two no longer slide. I think a length of heat-shrink tubing will stabilize them in rotation, although perhaps I should have just slobbered more epoxy into that joint.

After the epoxy cured, I sliced off all but 2 mm of the screw thread with an abrasive wheel and cleaned up the wreckage with a file. I actually remembered to spin on a nut before cutting, which ensured I finished the threads properly.

The business end of the mirror has far too many moving parts: two indented plates for the balls on the mirror and shaft, a screw, and a nut. That’s one too many ball joints, at least, and Wouldn’t It Be Nice If the mirror had a watertight seal around its perimeter?

Mirror ball joint clamp

Mirror ball joint clamp

For now, I just epoxied the nut in place after scuffing up the plate and nut with some sandpaper to give the epoxy something to grip:

Mirror ball joint - epoxied nut

Mirror ball joint - epoxied nut

You can’t see the new washer and rubber grommet under the screw head that provides a bit of compliance to hold the balls more securely, plus a dot of low-strength Loctite in the nut to discourage things from falling apart on the road.

8 Comments

Inspection Mirror Shaft Innards

Inspection mirror shaft friction springs

Inspection mirror shaft friction springs

With those doodles in mind, I applied an abrasive cutoff wheel to the shaft of an inspection mirror (from the usual eBay supplier) about 15 mm behind the second joint. That puts a short section of the third tube inside the yet-to-be-built helmet mirror mount.

The two copper-colored springs center the smaller tube inside the larger one and provide enough friction to make the whole thing work. The tubes seem to be chrome-plated brass and the springs  might be phosphor bronze. I suppose they’re Matryoshka-sized from one end to the other.

I’d never taken one of those shafts apart before; now we both know.

1 Comment

Better Bike Mirror Doodles

Mirror Mount - Unworkable Doodles

Mirror Mount - Unworkable Doodles

Having had many bike helmet mirrors disintegrate over the miles and years, I’ve had a background project bubbling along to build something more durable. Whether that’s feasible or not remains to be seen, but here’s another go at it.

A full-up ball joint seems to be more trouble than it’s worth and, in any event, requires far too much precision to be easily duplicated. That renders those doodles, mmm, inoperative.

These doodles aren’t workable, either, but they convert the ball joint into two orthogonal rotating joints that could be 3D printed with some attention to detail.

The general idea:

  • An ordinary inspection mirror has most of the tricky bits
  • An azimuth-elevation mount aligns the shaft relative to the helmet
  •  The mirror shaft extends to put the mirror forward of your eye
  • The existing mirror ball joint aligns the mirror relative to your eye

What’s not to like:

  • Exposed screw heads
  • Off-center, hard-to-grip adjustments
  • Probably not printable without support due to all the bearing surfaces and cutouts and suchlike
Mirror Mount - Doodles

Mirror Mount - Doodles

A few more days of doodling produced something that seems better. The az-el joint axes and the mirror shaft axis now meet at a common point, so the mirror shaft moves as the radius of a sphere. The elevation screw hides behind the azimuth mount, out of the way, which makes it awkward to adjust the tension.

The helmet mount plate must be concave to more-or-less match the helmet curvature. I’ve been securing mirrors using double-sided foam tape to good effect, but it requires a fairly large pad to provide enough adhesive force.

Two glue joints make everything buildable and should have basically the same strength as the parts themselves. The helmet plate builds concave face up. The az and el mounts build with the bearings upward, as do the mating surfaces on the other parts. Maybe the screws need actual nuts embedded in the mating parts, in which case there may be problems.

The setscrew holding the mirror shaft can crush the tube; I think they’re thin brass, at best. Putting a stud screw on the end will hold the shaft in place, leaving the setscrew to prevent rotation. Perhaps the stud can reinforce the tube.

What’s not to like:

  • Many parts (but all buildable at once)
  • It sticks out too far from the side of the helmet
  • Ugly on a stick

But maybe something will come of it.

10 Comments