Tube Turning Adapters

Finishing the PVC tubes reinforcing the vacuum cleaner adapters required fixtures on each end:

Dirt Devil adapter - pipe turning
Dirt Devil adapter – pipe turning

Because the tubes get epoxied into the adapters, there’s no particular need for a smooth surface finish and, in fact, some surface roughness makes for a good epoxy bond. The interior of a 3D printed adapter is nothing if not rough; the epoxy in between will be perfectly happy.

Turning the tubes started by just grabbing the conduit in the chuck and peeling the end that stuck out down to the finished diameter, because the conduit was thick-walled enough to let that work.

The remaining wall was so thin that the chuck would crunch it into a three-lobed shape, so the white ring in the chuck is a scrap of PVC pipe turned to fit the tube ID and provide enough reinforcement to keep the tube round.

The conduit ID isn’t a controlled dimension and was, in point of fact, not particularly round. It was, however, smooth, which counts for more than anything inside a tube carrying airborne fuzzy debris; polishing the interior of a lathe-bored pipe simply wasn’t going to happen.

The fixture on the other end started as a scrap of polycarbonate bandsawed into a disk with a hole center-drilled in the middle:

Pipe end lathe fixture - center drilling
Pipe end lathe fixture – center drilling

Stick it onto a disk turning fixture and sissy-cut the OD down a little smaller than the eventual tube OD:

Pipe end lathe fixture - turning OD
Pipe end lathe fixture – turning OD

Turn the end down to fit the tube ID, flip it around to center-drill the other side, stick it into the tube, and finally finish the job:

Dirt Devil adapter - pipe fixture
Dirt Devil adapter – pipe fixture

The nice layering effect along the tube probably comes from molding the conduit from recycled PVC with no particular concern for color matching.

A family portrait of the fixtures with a finished adapter:

Dirt Devil adapter - fixtures
Dirt Devil adapter – fixtures

A fine chunk of Quality Shop Time: solid modeling, 3D printing, mini-lathe turning, and even some coordinate drilling on the Sherline.

Dirt Devil Vacuum Tool Adapters

Being the domain expert for adapters between a new vacuum cleaner and old tools, this made sense (even though it’s not our vacuum):

Dirt Devil Nozzle Bushing - solid model
Dirt Devil Nozzle Bushing – solid model

The notch snaps into a Dirt Devil Power Stick vacuum cleaner and the tapered end fits a variety of old tools for other vacuum cleaners:

Dirt Devil Nozzle Bushing top view - solid model
Dirt Devil Nozzle Bushing top view – solid model

Having some experience breaking thin-walled adapters, these have reinforcement from a PVC tube:

Dirt Devil adapter - parts
Dirt Devil adapter – parts

A smear of epoxy around the interior holds the tube in place:

Dirt Devil adapters - assembled
Dirt Devil adapters – assembled

Building the critical dimensions with a 3D printed part simplified the project, because I could (and did!) tweak the OpenSCAD code to match the tapers to the tools. Turning four of those tubes from a chunk of PVC conduit, however, makes a story for another day.

The OpenSCAD source code as a GitHub Gist:

// Dirt Devil nozzle adapter
// Ed Nisley KE4ZNU 2021-10
// Tool taper shift
Finesse = -0.1; // [-0.5:0.1:0.5]
// PVC pipe liner
PipeOD = 28.5;
/* [Hidden] */
//- Extrusion parameters
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
Protrusion = 0.1; // make holes end cleanly
//----------------------
// Dimensions
TAPER_MIN = 0;
TAPER_MAX = 1;
TAPER_LENGTH = 2;
Socket = [36.0,37.0,40.0];
LockringDia = 33.5;
LockringWidth = 4.5;
LockringOffset = 2.5;
Tool = [Finesse,Finesse,0] + [30.0,31.1,30.0];
AdapterOAL = Socket[TAPER_LENGTH] + Tool[TAPER_LENGTH];
NumSides = 36;
$fn = NumSides;
//----------------------
// 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);
}
//-------------------
// Define it!
module Adapter() {
difference() {
union() {
difference() {
cylinder(d1=Socket[TAPER_MIN],d2=Socket[TAPER_MAX],h=Socket[TAPER_LENGTH]);
translate([0,0,LockringOffset])
cylinder(d=2*Socket[TAPER_MAX],h=LockringWidth);
}
cylinder(d=LockringDia,h=Socket[TAPER_LENGTH]);
translate([0,0,LockringOffset + 0.75*LockringWidth])
cylinder(d1=LockringDia,d2=Socket[TAPER_MIN],h=0.25*LockringWidth);
translate([0,0,Socket[TAPER_LENGTH]])
cylinder(d1=Tool[TAPER_MAX],d2=Tool[TAPER_MIN],h=Tool[TAPER_LENGTH]);
}
translate([0,0,-Protrusion])
PolyCyl(PipeOD,AdapterOAL + 2*Protrusion,NumSides);
}
}
//----------------------
// Build it!
Adapter();

The taper in the code almost certainly won’t fit whatever tool you have: measure thrice, print twice, and maybe fit once …

Tour Easy Rear Running Light: Circuit Support Plate

Building the circuit support plate for the amber front running light was entirely too fiddly:

1 W LED Running Light - baseplate dry assembly
1 W LED Running Light – baseplate dry assembly

This was definitely easier:

Running Light Circuit Plate - solid model
Running Light Circuit Plate – solid model

Two pins fit in the small holes to align it with the LED heatsink, with an M3 stud and brass insert holding it in place:

Tour Easy Rear Running Light - circuit plate attachment
Tour Easy Rear Running Light – circuit plate attachment

The rectangular hole around the insert let me glop urethane adhesive over it to lock it into the plate, with more goop on the screw and pins to unify heatsink and plate.

The LED wires now emerge from the heatsink on the same side of the plate, simplifying the connections to the MP1584 regulator and current-sense resistor:

Tour Easy Rear Running Light - regulator wiring
Tour Easy Rear Running Light – regulator wiring

The paralleled 5.1 Ω and 3.3 Ω resistors form a 2.0 Ω resistor setting the LED current to 400 mA = 1 W at 2.6 V forward drop. They’re 1 W resistors dissipating a total of 320 mW and get barely warm.

The resistors and wires are stuck in place with clear adhesive, so things shouldn’t rattle around too much.

The OpenSCAD source code as a GitHub Gist:

// Circuit plate for Tour Easy running lights
// Ed Nisley - KE4ZNU - 2021-09
/* [Hidden] */
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
ID = 0;
OD = 1;
LENGTH = 2;
inch = 25.4;
//----------------------
// Dimensions
// Light case along X axis
LightID = 23.0;
WallThick = 2.0;
Screw = [3.0,6.8,4.0]; // M3 OD=washer, length=nut + washers
Insert = [3.0,4.2,8.0]; // splined brass insert, minus splines
InsertOffset = 10.0; // insert from heatsink end
PinOD = 1.6; // alignment pins
PinOC = 14.0;
PinDepth = 5.0;
Plate = [50.0,LightID,Insert[OD] + 4*ThreadThick]; // overall plate size
WirePort = [10.0,3.0,2*Plate.z];
NumSides = 2*3*4;
//----------------------
// 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);
}
// Circuit plate
module Plate() {
difference() {
intersection() {
cube(Plate,center=true);
rotate([0,90,0])
cylinder(d=LightID,h=2*Plate.x,$fn=NumSides,center=true);
}
rotate([0,90,0]) rotate(180/6)
translate([0,0,-Plate.x])
PolyCyl(Screw[ID],2*Plate.x,6);
rotate([0,90,0]) rotate(180/6)
translate([0,0,-Plate.x/2 - Protrusion])
PolyCyl(Insert[OD],Insert[LENGTH] + InsertOffset + Protrusion,6);
translate([-Plate.x/2 + InsertOffset + Insert[LENGTH]/2,0,Plate.z/2])
cube([Insert[LENGTH],Insert[OD],Plate.z],center=true);
for (j=[-1,1])
translate([-Plate.x/2,j*PinOC/2,0])
rotate([0,90,0]) rotate(180/6)
translate([0,0,-PinDepth])
PolyCyl(PinOD,2*PinDepth,6);
for (j=[-1,1])
translate([0,j*(Plate.y/2 - WirePort.y/2),0])
cube(WirePort,center=true);
}
}
//- Build it
Plate();

Rear Running Light: Tour Easy Seat Clamp

With the amber front running light blinking away, it’s time to replace the decade-old Planet Bike Superflash behind the seat:

Superflash on Tour Easy
Superflash on Tour Easy

The new mount descends directly from the clamps holding the fairing strut on the handlebars and various hose clamps:

Rear Running Light Seat Clamp - solid model
Rear Running Light Seat Clamp – solid model

The central block has two quartets of brass inserts epoxied inside:

Rear Running Light Seat Clamp - sectioned - solid model
Rear Running Light Seat Clamp – sectioned – solid model

That means I can install the light, then mount the whole affair on the bike, without holding everything together while fiddling with overly long screws.

A trial fit with the not-yet-cut-to-length 25.3 (-ish) PVC pipe body tube:

Rear Running Light - Tour Easy seat clamp trial fit
Rear Running Light – Tour Easy seat clamp trial fit

The aluminum plates have the standard used-car finish: nice polish over deep scratches.

Although I’ve been thinking of mounting the light below the seat rail, as shown, it can also sit above the rail.

Mary hauls seedlings and suchlike to the garden in a plastic drawer bungied to the rack, with the SuperFlash serving as an anchor point; this light may need fine tuning for that purpose.

The OpenSCAD source code as a GitHub Gist:

// Rear running light clamp for Tour Easy seat strut
// Ed Nisley - KE4ZNU - 2021-09
Layout = "Show"; // [Show,Build,Block]
Section = true;
/* [Hidden] */
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
ID = 0;
OD = 1;
LENGTH = 2;
inch = 25.4;
//----------------------
// Dimensions
// Light case along X axis, seat strut along Y, Z=0 at strut centerline
LightOD = 25.4 + HoleWindage;
StrutOD = 5/8 * inch + HoleWindage;
PlateThick = 1/16 * inch;
WallThick = 2.0;
Kerf = ThreadThick;
Screw = [3.0,6.8,4.0]; // M3 OD=washer, length=nut + washers
Insert = [3.0,5.4,8.0 + 1.0]; // splined brass insert
RoundRadius = IntegerMultiple(Screw[OD]/2,0.5); // corner rounding
ScrewOC = [IntegerMultiple(StrutOD + 2*WallThick + Screw[ID],1.0),
IntegerMultiple(LightOD + 2*WallThick + Screw[ID],1.0)];
echo(str("Screw OC: ",ScrewOC));
BlockSize = [ScrewOC.x + Insert[OD] + 2*WallThick,
ScrewOC.y + Insert[OD] + 2*WallThick,
LightOD + StrutOD + 3*WallThick];
echo(str("Block: ",BlockSize));
BaseOffset = -(WallThick + LightOD/2); // block bottom to centerline
StrutOffset = LightOD/2 + WallThick + StrutOD/2; // light centerline to strut centerline
echo(str("Strut screw min: ",IntegerMultiple(PlateThick + WallThick + StrutOD/2 + Insert[LENGTH]/2,1.0)));
echo(str("Light screw min: ",IntegerMultiple(PlateThick + WallThick + LightOD/2 + Insert[LENGTH]/2,1.0)));
NumSides = 2*3*4;
//----------------------
// 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);
}
// Block with light along X axis
module Block() {
difference() {
hull()
for (i=[-1,1], j=[-1,1])
translate([i*(BlockSize.x/2 - RoundRadius),j*(BlockSize.y/2 - RoundRadius),BaseOffset])
cylinder(r=RoundRadius,h=BlockSize.z,$fn=NumSides);
for (i=[-1,1], j=[-1,1])
translate([i*ScrewOC.x/2,j*ScrewOC.y/2,BaseOffset - Protrusion])
rotate(180/8)
PolyCyl(Screw[ID],BlockSize.z + 2*Protrusion,8);
for (i=[-1,1], j=[-1,1])
translate([i*ScrewOC.x/2,j*ScrewOC.y/2,0]) {
translate([0,0,-Protrusion])
rotate(180/8)
PolyCyl(Insert[OD],Insert[LENGTH] + 1*Protrusion,8);
translate([0,0,(StrutOffset - Insert[LENGTH] - Kerf/2 + Protrusion)])
rotate(180/8)
PolyCyl(Insert[OD],Insert[LENGTH] + 1*Protrusion,8);
}
translate([-BlockSize.x,0,0])
rotate([0,90,0])
cylinder(d=LightOD,h=2*BlockSize.x,$fn=NumSides);
translate([0,BlockSize.y,StrutOffset])
rotate([90,0,0])
cylinder(d=StrutOD,h=2*BlockSize.y,$fn=NumSides);
translate([0,0,StrutOffset])
cube([2*BlockSize.x,2*BlockSize.y,Kerf],center=true);
cube([2*BlockSize.x,2*BlockSize.y,Kerf],center=true);
}
}
//- Build it
if (Layout == "Block")
if (Section)
difference() {
Block();
rotate(atan(ScrewOC.y/ScrewOC.x))
translate([0,BlockSize.y,0])
cube(2*BlockSize,center=true);
}
else
Block();
if (Layout == "Show") {
Block();
color("Green",0.25)
translate([-BlockSize.x,0,0])
rotate([0,90,0])
cylinder(d=LightOD,h=2*BlockSize.x,$fn=NumSides);
color("Green",0.25)
translate([0,BlockSize.y,StrutOffset])
rotate([90,0,0])
cylinder(d=StrutOD,h=2*BlockSize.y,$fn=NumSides);
}
if (Layout == "Build") {
translate([-1.2*BlockSize.x,0,-BaseOffset])
difference() {
Block();
translate([0,0,BlockSize.z])
cube(2*BlockSize,center=true);
}
translate([1.2*BlockSize.x,0,StrutOD/2 + WallThick])
difference() {
rotate([180,0,0])
translate([0,0,-StrutOffset])
Block();
translate([0,0,BlockSize.z])
cube(2*BlockSize,center=true);
}
translate([0,0,StrutOffset - Kerf/2])
rotate([180,0,0])
intersection() {
Block();
translate([0,0,StrutOffset/2])
cube([2*BlockSize.x,2*BlockSize.y,StrutOffset],center=true);
}
}

Tour Easy 1 W Amber Running Light: Firmware

Rather than conjure a domain specific language to blink an LED, it’s easier to use Morse code:

Herewith, Arduino source code using Mark Fickett’s Morse library to blink an amber running light:

// Tour Easy Running Light
// Ed Nisley - KE4ZNU
// September 2021

#include <morse.h>

#define PIN_OUTPUT	13

LEDMorseSender Morser(PIN_OUTPUT,(float)10.0);

void setup()
{
	Morser.setup();

    Morser.setMessage(String("qst de ke4znu "));
    Morser.sendBlocking();

//    Morser.setWPM((float)3.0);
    Morser.setSpeed(50);
	Morser.setMessage(String("s   "));
}

void loop()
{
	if (!Morser.continueSending())
		Morser.startSending();

}

Bonus: a trivially easy ID string.

A dit time of 50 ms produces a brief flash that’s probably about as fast as it can be, given that the regulator must ramp the LED current up from zero after its Enable input goes high. In round numbers, a 50ms dit corresponds to 24 WPM Morse.

Each of the three blanks after the “s” produces a seven element word space to keep the blinks from running together.

Sending “b ” (two blanks) with a 75 ms dit time may be more noticeable. You should tune for maximum conspicuity on your rides.

1 W Amber Running Light - installed front
1 W Amber Running Light – installed front

On our first ride, Mary got a friendly wave from a motorcyclist, an approving toot from a driver, and several “you go first” gestures at intersections.

Works for us …

Vacuum Tube Lights: Urethane Coated Plate Cap

With a generous dollop of JB Plastic Bonder left over from a set of Bafang brake sensor magnets, I tried coating the ersatz plate cap of a triode tube:

Triode - urethane coated plate cap
Triode – urethane coated plate cap

That’s the result after leaving it hanging upside-down while it cured to push all the drips to the top.

For comparison, the uncoated cap back in the day:

Triode - plate cap plug
Triode – plate cap plug

Seeing as how the urethane is an adhesive, not a coating, I’d say it looks about as bad as expected.

As with all 3D printed things, one must embrace imperfections and striations, rather than endlessly strive for perfection.

Now, if I had a resin printer …