Having not yet gotten around to building better taillights for our bikes, we picked up some Planet Bike Superflash lights on sale. I don’t like single-LED lights, because the optics produce a concentrated beam (which is how they get such high lumen ratings) that’s essentially invisible anywhere off-axis; a taillight that requires careful alignment for maximum effect is a Bad Thing. But, eh, they were on sale…
The graceful OEM seatpost mount, done in engineering plastic with smooth curves and something of a reputation for fragility, doesn’t work on a recumbent, so I build a butt-ugly mount that should last forever. It clamps firmly around a length of grippy silicone tape on the top seat frame rail:

The reviews also complain that normal road vibrations transmitted through the somewhat whippy OEM mount pop the case apart, depositing the lens and electronics on the road behind you. Hence the black tape across the case joint.
Here’s the whole affair on the bench:

The weird color line comes from white plastic left in the extruder that covers the bottom layer or two of each part. I’m not fussy about the first pass of any new gadget, because I know I’ll build at least one more to get everything right.
This is the first build arrangement; note the huge white teardrop blob at the start of the Skirt outline on the left. Obviously I didn’t have the initial retraction under control:

The screw recesses built over the plate and got cute little support spiders to keep their interiors from sagging:

After doing it that way, I flipped the top piece over so it builds with the screw head recesses upward to get a better finish on those nice curves. That means the arch needs support, which almost worked, although some of the fins fell over:

The solid model now adds a two-layer-thick flat plate joining the fins that should hold them firmly to the build plate.

I also added an option to build the flash mounting shoe separately:

That gives better control over the flange thickness, which turns out to be critical parameter requiring a bit of adjustment with a file in the first version. Of course, the shoe needs an alignment pin and another assembly step to glue it in place:

A 4-40 setscrew jams into the latch recess in the Superflash case, thus preventing it from walking off the shoe. You don’t need any particular pressure here, just enough protrusion to engage the case:

The first pass at hex nut recesses were exactly cos(30) too large, as I forgot my Useful Sizes file has the across-the-points diameter, so I added a dab of epoxy to each recess before gluing the halves together with solvent:

And then it’s all good.
The OpenSCAD source code:
// Planet Bike Superflash mount for Tour Easy seatback // Ed Nisley KE4ZNU - Dec 2011 Layout = "Show"; // Assembly: Show // Parts: Clamp Base Shoe Mount // Build Plate: Build SeparateShoe = true; // true = print mounting shoe separately // false = join shoe to Mount block Support = true; // true = include support Gap = 8; // between "Show" objects include </home/ed/Thing-O-Matic/lib/MCAD/units.scad> include </home/ed/Thing-O-Matic/Useful Sizes.scad> include </home/ed/Thing-O-Matic/lib/visibone_colors.scad> //------- //- Extrusion parameters must match reality! // Print with +1 shells, 3 solid layers, 0.2 infill ThreadThick = 0.25; ThreadWidth = 2.0 * ThreadThick; HoleFinagle = 0.1; HoleFudge = 1.00; function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle; Protrusion = 0.1; // make holes end cleanly function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); function IntegerMultipleMin(Size,Unit) = Unit * floor(Size / Unit); //------- // Dimensions BarDia = (5/8) * inch; // seat back rail diameter BarRad = BarDia/2; TapeThick = 0.3; // grippy tape around bar HoleDia = BarDia + 2*TapeThick; // total hole dia HoleRad = HoleDia/2; HoleSides = 4*5; echo("Bar hole dia: ",HoleDia); TightSpace = 1.0; // space for tightening screws PlateWidth = 20.0; // mounting plate across flanges PlateLength = 20.0; // ... parallel to flanges PlateThick = IntegerMultipleMin(1.96,ThreadThick); // ... thickness FlangeThick = IntegerMultiple(1.40,ThreadThick); // lamp flange thickness FlangeWidth = 2.0; // ... width ShoeThick = PlateThick + FlangeThick; // dingus protruding from main block ShoeOffset = 1.0; // offset due to end wall echo("Shoe thickness: ",ShoeThick," = ",PlateThick," + ",FlangeThick); LockOffset = -5.0; // offset of locking setscrew TopRoundRad = 1.5*Head10_32/2; // tidy rounding on top edge of clamp echo("Top rounding radius: ",TopRoundRad); NutDia = Nut10_32Dia*cos(30); // adjust from across-points to across-flats dia NutPart = IntegerMultiple(0.5*Nut10_32Thick,ThreadThick); // part of nut in each half BoltOffset = HoleRad + max(Head10_32,NutDia); BoltClear = Clear10_32; BoltHeadDia = Head10_32; BoltHeadThick = Head10_32Thick; MountWidth = PlateLength + ShoeOffset; // side-to-side MountLength = HoleDia + 3.5*max(BoltHeadDia,NutDia); ClampHeight = TopRoundRad + HoleRad; // includes gap/2 for simplicity BaseHeight = NutPart + HoleRad; // ... likewise MountHeight = PlateWidth; echo("Mount width: ",MountWidth," length: ",MountLength); echo("Height of clamp: ",ClampHeight," base: ",BaseHeight," mount: ",MountHeight); echo(" total: ",ClampHeight+BaseHeight+MountHeight); AlignPegDia = 2.9; // shoe alignment peg AlignPegLength = ShoeThick; echo("Alignment peg length: ",AlignPegLength); //------- 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=HoleAdjust(FixDia)/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); } //------- // Upper clamp half module Clamp() { difference() { translate([0,0,-TightSpace/2]) { difference() { translate([0,0,ClampHeight/2]) { intersection() { translate([0,0,-TopRoundRad]) minkowski() { cube([(MountLength - 2*TopRoundRad), (MountWidth - 2*Protrusion), ClampHeight],center=true); rotate([90,0,0]) cylinder(r=TopRoundRad,h=Protrusion,$fn=4*8); } cube([MountLength,MountWidth,ClampHeight],center=true); } } translate([0,(MountWidth/2 + Protrusion)]) rotate([90,0,0]) PolyCyl(HoleDia,(MountWidth + 2*Protrusion),HoleSides); for (Index=[-1,1]) translate([(Index*BoltOffset),0,0]) { translate([0,0,-Protrusion]) PolyCyl(BoltClear,(ClampHeight + Protrusion)); translate([0,0,(ClampHeight - BoltHeadThick)]) PolyCyl(BoltHeadDia,(BoltHeadThick + Protrusion)); } } } translate([0,0,-TightSpace/2]) cube([(MountLength + 2*Protrusion), (MountWidth + 2*Protrusion), TightSpace],center=true); } if (Support) // choose support to suit printing orientation union() { translate([0,0,1.5*ThreadThick]) cube([0.75*HoleDia,(MountWidth + 2*ThreadWidth),3*ThreadThick],center=true); intersection() { for (Index=[-3:3]) translate([0,Index*(MountWidth/6),-TightSpace/2]) rotate([90,0,0]) cylinder(r=(HoleRad - 0.25*ThreadThick), h=2*ThreadWidth,center=true,$fn=HoleSides); translate([-HoleRad,-MountWidth,0]) cube([HoleDia,2*MountWidth,HoleRad]); } } } //------- // Lower clamp half = base module Base() { difference() { translate([0,0,-TightSpace/2]) difference() { translate([0,0,BaseHeight/2]) cube([MountLength,MountWidth,BaseHeight],center=true); translate([0,(MountWidth/2 + Protrusion)]) rotate([90,0,0]) PolyCyl(HoleDia,(MountWidth + 2*Protrusion),HoleSides); for (Index=[-1,1]) translate([(Index*BoltOffset),0,0]) { translate([0,0,-Protrusion]) PolyCyl(BoltClear,(BaseHeight + Protrusion)); translate([0,0,(BaseHeight - NutPart)]) rotate(30) PolyCyl(NutDia,(NutPart + Protrusion),6); // cylinder(r=NutDia/2,h=(NutPart + Protrusion),$fn=6); } } translate([0,0,-TightSpace/2]) cube([(MountLength + 2*Protrusion), (MountWidth + 2*Protrusion), TightSpace],center=true); } if (Support) for (Index=[-1,1]) // support inside nut openings translate([(Index*BoltOffset), 0, (BaseHeight - (NutPart - ThreadThick) - TightSpace/2)]) { translate([0,0,0]) for (Seg=[0:5]) { rotate(30 + 360*Seg/6) cube([NutDia/2,2*ThreadWidth,NutPart - ThreadThick],center=false); } } } //------- // Superflash mounting shoe // Offset by -ShoeOffset/2 in Y to align on Mount (half of total offset on each side) module Shoe() { difference() { translate([-ShoeThick/2,-ShoeOffset/2,PlateWidth/2]) if (SeparateShoe) cube([ShoeThick,PlateLength,PlateWidth],center=true); else cube([(ShoeThick + Protrusion),PlateLength,PlateWidth],center=true); translate([-(FlangeThick - Protrusion), -(PlateLength/2 + ShoeOffset/2 + Protrusion), (MountHeight - FlangeWidth)]) cube([FlangeThick,(PlateLength + 2*Protrusion),(FlangeWidth + Protrusion)]); translate([-(FlangeThick - Protrusion), -(PlateLength/2 + ShoeOffset/2 + Protrusion), -Protrusion]) cube([FlangeThick,(PlateLength + 2*Protrusion),(FlangeWidth + Protrusion)]); translate([-(ShoeThick + Protrusion),LockOffset,MountHeight/2]) rotate([0,90,0]) rotate(0) // align to match Mount hole orientation PolyCyl(Tap4_40,(ShoeThick + 2*Protrusion)); if (SeparateShoe) translate([-(ShoeThick - AlignPegLength/2),0,MountHeight/2]) rotate([0,90,0]) PolyCyl(AlignPegDia,AlignPegLength); } } //------- // Bottom block for Superflash mount module Mount() { translate([0,0,MountHeight/2]) union() { difference() { union() { translate([-MountLength/4,0,0]) cube([MountLength/2,MountWidth,MountHeight],center=true); translate([((MountLength/2 - MountHeight)/2 + Protrusion),0,0]) cube([(MountLength/2 - MountHeight + 2*Protrusion), MountWidth, MountHeight],center=true); translate([(MountLength/2 - MountHeight),0,0]) intersection() { translate([MountLength/4,0,0]) cube([MountLength/2,MountWidth,MountHeight],center=true); translate([0,0,MountHeight/2]) rotate([90,0,0]) cylinder(r=MountHeight,h=MountWidth,center=true,$fn=4*16); } } translate([-(MountLength/2 + Protrusion),LockOffset,0]) rotate([0,90,0]) rotate(0) // align through hole sides with point upward PolyCyl(Clear4_40,(MountLength + 2*Protrusion)); for (Index=[-1,1]) translate([(Index*BoltOffset),0,0]) { translate([0,0,BaseHeight/2]) PolyCyl(BoltClear,(BaseHeight/2 + Protrusion)); translate([0,0,(BaseHeight - NutPart)]) rotate(30) PolyCyl(NutDia,(NutPart + Protrusion),6); } if (SeparateShoe) translate([-(MountLength/2 + AlignPegLength/2),0,0]) rotate([0,90,0]) PolyCyl(AlignPegDia,AlignPegLength); } if (Support) for (Index=[-1,1]) // support inside nut openings translate([(Index*BoltOffset),0,(MountHeight/2 - (NutPart - ThreadThick))]) { translate([0,0,0]) for (Seg=[0:5]) { rotate(30 + 360*Seg/6) cube([NutDia/2, 2*ThreadWidth, (NutPart - ThreadThick)],center=false); } } if (!SeparateShoe) translate([-MountLength/2,0,-MountHeight/2]) Shoe(); } } //------- ShowPegGrid(); if (Layout == "Clamp") Clamp(); if (Layout == "Base") Base(); if (Layout == "Shoe") Shoe(); if (Layout == "Mount") Mount(); if (Layout == "Show") { translate([0,0,(BaseHeight + MountHeight + Gap)]) { translate([0,0,TightSpace/2 + Gap]) color(MFG) Clamp(); translate([0,0,-TightSpace/2]) rotate([180,0,0]) color(DHC) Base(); } translate([0,0,0]) color(LDM) render(convexity=3) Mount(); if (SeparateShoe) translate([-(MountLength/2 + Gap),0,0]) color(DDM) Shoe(); } if (Layout == "Build") { translate([-15,30,(BaseHeight - TightSpace/2)]) rotate([180,0,0]) Base(); translate([-15,00,0]) rotate([0,0,0]) Clamp(); if (SeparateShoe) translate([20,30,ShoeThick]) rotate([0,-90,180]) Shoe(); if (SeparateShoe) translate([-15,-30,MountHeight]) rotate([180,0,180]) Mount(); else translate([-15,-40,MountWidth/2]) rotate([90,0,180]) Mount(); }
The original doodles, done on a retina-burning yellow scratchpad:
