
// Tour Easy Fairing Flashlight Mount 

// Ed Nisley KE4ZNU  July 2017 



/* [Build Options] */ 



FlashName = "AnkerLC40"; // [AnkerLC40,AnkerLC90,J5TactV2,InnovaX5] 



Component = "Mount"; // [Ball, BallClamp, Mount, Plates, Bracket] 



Layout = "Show"; // [Build, Show] 



Support = false; 



MountSupport = true; 



/* [Extrusion] */ 



ThreadThick = 0.25; // [0.20, 0.25] 

ThreadWidth = 0.40; // [0.40] 



function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); 



Protrusion = 0.01; // [0.01, 0.1] 



HoleWindage = 0.2; 



/* [Fairing Mount] */ 



ToeIn = 0; // inward from ahead 

Tilt = 20; // upward from forward 

Roll = 0; // outward from top 



Shift = 5; // realign to plate center 



// Screws *c 



/* [Hidden] */ 



ID = 0; 

OD = 1; 

LENGTH = 2; 



/* [Screws and Inserts] */ 



BallInsert = [2.0,3.5,4.0]; 

BallScrew = [2.0,3.5,2.0]; 



ClampInsert = [3.0,4.2,8.0]; 

ClampScrew = [3.0,5.9,50.0]; // thread dia, head OD, screw length 

ClampScrewWasher = [3.0,6.75,0.5]; 

ClampScrewNut = [3.0,6.1,4.0]; // nyloc nut 



/* [Hidden] */ 



F_NAME = 0; 

F_GRIPOD = 1; 

F_GRIPLEN = 2; 



LightBodies = [ 

["AnkerLC90",26.6,48.0], 

["AnkerLC40",26.6,55.0], 

["J5TactV2",25.0,30.0], 

["InnovaX5",22.0,55.0] 

]; 



NumSides = 8*4; 



echo(str("Flashlight: ",FlashName)); 



FlashIndex = search([FlashName],LightBodies,1,0)[F_NAME]; 



BallThick = IntegerMultiple(5.0,ThreadWidth); // thickness of ball wall 

echo(str("Ball wall: ",BallThick)); 



BallOD = max(45,IntegerMultiple(LightBodies[FlashIndex][F_GRIPOD] + 2*(BallThick + BallInsert[OD]),2.0)); 

echo(str(" OD: ",BallOD)); 



BallScrewOC = BallOD  BallThick  BallInsert[OD]; // from OD to allow different body diameters 

echo(str(" screw OC: ",BallScrewOC)); 



BallLength = min(sqrt(pow(BallOD,2)  pow(LightBodies[FlashIndex][F_GRIPOD],2)), 

LightBodies[FlashIndex][F_GRIPLEN]); 

echo(str(" hole len: ",BallLength)); 



ClampThick = 2*ClampInsert[OD]; 

echo(str("Clamp wall: ",ClampThick)); 



ClampOD = BallOD + 2*ClampThick; 

echo(str(" OD: ",ClampOD)); 



ClampScrewOC = BallOD + 2*ClampInsert[OD]; 

echo(str(" screw OC: ",ClampScrewOC)); 



ClampLength = 0.70 * BallLength; 

echo(str(" length: ",ClampLength)); 



// Adjust hole diameter to make the size come out right 



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); 

} 



// Ball around flashlight 

// Must print two! 



module BodyBall() { 



difference() { 

intersection() { 

sphere(d=BallOD,$fn=2*NumSides); // basic ball 

cube([BallLength,2*BallOD,2*BallOD],center=true); // max of flashlight grip length 

} 

translate([LightBodies[FlashIndex][F_GRIPOD],0,0]) 

rotate([0,90,0]) rotate(180/NumSides) 

PolyCyl(LightBodies[FlashIndex][F_GRIPOD],2*BallOD,NumSides); // flashlight body 

for (j=[1,1]) 

translate([0,j*BallScrewOC/2,0]) // commmon screw offset 

translate([0,0,BallOD]) 

PolyCyl(BallInsert[ID],2*BallOD,6); // punch screw shaft through everything 

translate([0,BallScrewOC/2,Protrusion]) 

PolyCyl(BallInsert[OD],(BallInsert[LENGTH] + 3*ThreadThick + Protrusion),6); // threaded insert 

translate([0,BallScrewOC/2,BallThick]) 

PolyCyl(BallScrew[OD],BallOD,6); // screw head clearance 



translate([0,0,BallOD/2]) // remove bottom half 

cube(BallOD,center=true); 

translate([0,0,BallOD  BallThick/2]) // slice off top = bottom for EZ build 

cube(BallOD,center=true); 

} 



if (Support) { 

NumRibs = 24; 

RibHeight = (BallOD  LightBodies[FlashIndex][F_GRIPOD]/cos(180/NumSides)  BallThick) / 2; 

ChordC = 2*sqrt(BallThick*BallOD/2  pow(BallThick/2,2)); 

intersection() { 

cube([BallLength,2*BallOD,2*BallOD],center=true); // max of flashlight grip length 

translate([0,0,BallOD/2  BallThick/2]) 

for (i=[0:NumRibs  1]) 

rotate(i*360/NumRibs + 180/NumRibs) // avoid screw holes 

translate([ChordC/2 + BallOD/8,0,RibHeight/2]) 

cube([BallOD/4,2*ThreadWidth,RibHeight],center=true); 

} 

} 

} 



// Fairing Bracket 

// Magic numbers taken from the actual fairing mount 

// Centered on screw hole 



/* [Hidden] */ 



inch = 25.4; 



BracketHoleOD = 0.25 * inch; // 1/420 bolt holes 



BracketHoleOC = 1.0 * inch; // fairing hole spacing 

// usually 1 inch, but 15/16 on one fairing 



Bracket = [48.0,16.3,3.6  0.6]; // fairing bracket end plate overall size 

BracketHoleOffset = (3/8) * inch; // end to hole center 



BracketM = 3.0; // endcap arc height 

BracketR = (pow(BracketM,2) + pow(Bracket[1],2)/4) / (2*BracketM); // ... radius 



module Bracket() { 



linear_extrude(height=Bracket[2],convexity=2) 

difference() { 

translate([(Bracket[0]/2  BracketHoleOffset),0,0]) 

offset(delta=ThreadWidth) 

intersection() { 

square([Bracket[0],Bracket[1]],center=true); 

union() { 

for (i=[1,0,1]) // middle circle fills gap 

translate([i*(Bracket[0]/2  BracketR),0]) 

circle(r=BracketR); 

} 

} 

circle(d=BracketHoleOD/cos(180/8),$fn=8); // dead center at the origin 

} 



} 



// General plate shape 

// Centered on the hole for the fairing bracket 



Plate = [100.0,30.0,6*ThreadThick + Bracket[2]]; 

PlateRad = Plate[1]/4; 



echo(str("Base plate thick: ",Plate[2])); 



module PlateBlank() { 



difference() { 

translate([BracketHoleOC,0,0]) 

intersection() { 

translate([0,0,Plate[2]/2]) // select upper half of spheres 

cube(Plate,center=true); 

hull() 

for (i=[1,1], j=[1,1]) 

translate([i*(Plate[0]/2  PlateRad),j*(Plate[1]/2  PlateRad),0]) 

resize([2*PlateRad,2*PlateRad,2*Plate[2]]) 

sphere(r=PlateRad); // nice rounded corners! 

} 

translate([2*BracketHoleOC,0,Protrusion]) // screw holes 

PolyCyl(BracketHoleOD,2*Plate[2],8); 

translate([0,0,Protrusion]) 

PolyCyl(BracketHoleOD,2*Plate[2],8); 

} 

} 



// Inner plate 



module InnerPlate() { 



difference() { 

PlateBlank(); 

translate([0,0,Plate[2]  Bracket[2] + Protrusion]) // punch out fairing bracket 

Bracket(); 

} 

} 



// Clamp around flashlight ball 



module BallClamp() { 



BossLength = ClampScrew[LENGTH]  ClampScrewNut[LENGTH]  2*ClampScrewWasher[LENGTH]  4*ThreadThick; 



difference() { 

union() { 

intersection() { 

sphere(d=ClampOD,$fn=NumSides); // exterior ball blamp 

cube([ClampLength,2*ClampOD,2*ClampOD],center=true); // aiming allowance 

} 

for (i=[0]) 

hull() { 

for (j=[1,1]) 

translate([i*(ClampLength/2  ClampScrew[OD]),j*ClampScrewOC/2,BossLength/2]) 

rotate(180/8) 

cylinder(d=(ClampScrewWasher[OD] + 2*ThreadWidth),h=BossLength,$fn=8); 

} 

} 



sphere(d=(BallOD + 1*ThreadThick),$fn=NumSides); // interior ball 



for (i=[0] , j=[1,1]) { 

translate([i*(ClampLength/2  ClampScrew[OD]),j*ClampScrewOC/2,ClampOD]) // screw clearance 

rotate(180/8) 

PolyCyl(ClampScrew[ID],2*ClampOD,8); 

} 



} 





color("Yellow") 

if (Support) { // adhoc supports for top half 

NumRibs = 6; 

RibLength = 0.5 * BallOD; 

RibWidth = 1.9*ThreadWidth; 

SupportOC = ClampLength / NumRibs; 



cube([ClampLength,RibLength,4*ThreadThick],center=true); // base plate for adhesion 



intersection() { 

sphere(d=BallOD  0*ThreadWidth); // cut at inner sphere OD 

cube([ClampLength + 2*ThreadWidth,RibLength,BallOD],center=true); 

union() { // ribs for EZ build 

for (j=[1,0,1]) 

translate([0,j*SupportOC,0]) 

cube([ClampLength,RibWidth,1.0*BallOD],center=true); 

for (i=[0:NumRibs]) // allow +1 to fill the far end 

translate([i*SupportOC  ClampLength/2,0,0]) 

rotate([0,90,0]) 

cylinder(d=BallOD  2*ThreadThick, 

h=RibWidth,$fn=NumSides,center=true); 

} 

} 

} 

} 



// Mount between fairing plate and flashlight ball 



module Mount() { 



translate([BracketHoleOC,0,0]) 

PlateBlank(); 



translate([Shift,0,ClampOD/2]) 

rotate([Roll,ToeIn,Tilt]) 

intersection() { 

translate([0,0,ClampOD/2]) 

cube([2*ClampOD,2*ClampOD,ClampOD],center=true); 

BallClamp(); 

} 



if (MountSupport) { // anchor outer corners during worst overhang 

RibWidth = 1.9*ThreadWidth; 

SupportOC = 0.1 * ClampLength; 

difference() { 

rotate([0,0,Tilt]) 

translate([Shift + 0.3,0,0]) 

for (i=[4.5,2.5,0,2.0,4.5]) 

translate([i*SupportOC  0.0,0,(ClampThick + Plate[2])/2]) 

cube([RibWidth,0.8*ClampOD,(ClampThick + Plate[2])],center=true); 

# translate([Shift,0,ClampOD/2]) 

rotate([Roll,ToeIn,Tilt]) 

sphere(d=ClampOD  2*ThreadWidth,$fn=NumSides); 

} 

} 

} 



// Build things 



if (Component == "Ball") 

if (Layout == "Show") 

BodyBall(); 

else if (Layout == "Build") { 

translate([0,+1*(BallOD/2 + BallThick/2),0]) 

translate([0,0,BallOD/2  BallThick/2]) 

rotate([180,0,0]) 

BodyBall(); 

translate([0,1*(BallOD/2 + BallThick/2),0]) 

translate([0,0,BallOD/2  BallThick/2]) 

rotate([180,0,0]) 

BodyBall(); 

} 



if (Component == "BallClamp") 

if (Layout == "Show") 

BallClamp(); 

else if (Layout == "Build") { 

Both = false; 

difference() { 

union() { 

translate([Both ? ClampLength : 0,0,0]) 

BallClamp(); 

if (Both) 

translate([ClampLength,0,0]) 

rotate([180,0,0]) 

BallClamp(); 

} 

translate([0,0,ClampOD/2]) 

cube([2*ClampOD,2*ClampOD,ClampOD],center=true); 

} 

} 



if (Component == "Mount") 

Mount(); 



if (Component == "Plates") { 

translate([0,0.7*Plate[1],0]) 

InnerPlate(); 

translate([0,0.7*Plate[1],0]) 

PlateBlank(); 

} 



if (Component == "Bracket") 

Bracket(); 