|
// SJCAM C100+ Camera Mount for Tour Easy seat back rail |
|
// Ed Nisley – KE4ZNU |
|
// 2023-04 |
|
|
|
/* [Layout Options] */ |
|
|
|
LookAngle = -20; // camera angle, looking backwards = 0° |
|
|
|
Layout = "Show"; // [Show,Build] |
|
|
|
|
|
/* [Hidden] */ |
|
|
|
ThreadWidth = 0.40; |
|
ThreadThick = 0.25; |
|
|
|
HoleWindage = 0.2; |
|
|
|
Protrusion = 0.1; |
|
|
|
ID = 0; |
|
OD = 1; |
|
LENGTH = 2; |
|
|
|
//—– |
|
// Dimensions |
|
|
|
ClampScrew = [5.0,10.0,40.0]; // ID=thread OD=washer LENGTH=total |
|
ClampInsert = [5.0,7.5,10.5]; // brass insert |
|
|
|
MountScrew = [5.0,10.0,23.0]; // ID=thread OD=washer LENGTH=under nut |
|
MountInsert = [5.0,7.5,10.5]; // ID=screw OD, OD=knurl dia |
|
|
|
EmbossDepth = 2*ThreadThick + Protrusion; // recess depth + Protrusion beyond surface |
|
|
|
DebossHeight = EmbossDepth; // text height + Protrusion into part |
|
|
|
RailOD = 20.0; // slightly elliptical in bent section |
|
RailSides = 2*3*4; |
|
|
|
ClampOA = [60.0,40.0,ClampScrew[LENGTH]]; // set clamp size to avoid weird screw spacing |
|
echo(ClampOA = ClampOA); |
|
|
|
ClampScrewOC = IntegerMultiple(ClampOA.x – ClampScrew[OD] – 10*ThreadWidth,1.0); |
|
echo(ClampScrewOC = ClampScrewOC); |
|
|
|
ClampOffset = 5.0; // in case we need more room on top |
|
|
|
ClampRadius = 3.0; |
|
ClampSides = 8; |
|
|
|
Kerf = 1.0; // slice through the middle |
|
|
|
// center mount blade, Z = depth into block |
|
MountBlade = [15.0 + 2*HoleWindage, |
|
3.0 + 2*HoleWindage, |
|
(ClampOA.z – RailOD + ClampOffset)/2 – 4*ThreadThick + Protrusion]; |
|
echo(MountBlade = MountBlade); |
|
|
|
MountRadius = MountBlade.x / 2; |
|
|
|
MountGap = 9.5; // camera mount gap around center blade |
|
|
|
MountOffset = [0,0,7.0]; // mount hole offset from block surface |
|
|
|
FadeColor = "Green"; |
|
FadeAlpha = 0.25; |
|
|
|
//—– |
|
// Useful routines |
|
|
|
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); |
|
|
|
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); |
|
} |
|
|
|
|
|
//—– |
|
// Clamp |
|
// Grips seat frame rail |
|
// Origin at middle of seat rail, X rearward, Y parallel to seat frame rail |
|
// Block offset raises whole thing |
|
|
|
module Clamp() { |
|
|
|
difference() { |
|
translate([0,0,ClampOffset]) { |
|
difference() { |
|
union() { |
|
hull() // the main block |
|
for (i=[-1,1], j=[-1,1], k=[-1,1]) |
|
translate([i*(ClampOA.x – 2*ClampRadius)/2, |
|
j*(ClampOA.y – 2*ClampRadius)/2, |
|
k*(ClampOA.z – 2*ClampRadius)/2]) |
|
sphere(r=ClampRadius/cos(180/ClampSides),$fn=ClampSides); |
|
|
|
hull() // camera mount boss |
|
for (k=[0,1]) |
|
translate([0,0,k*(MountOffset.z) + ClampOA.z/2]) |
|
rotate([0,90,LookAngle + 90]) rotate(180/12) |
|
cylinder(r=MountRadius,h=MountScrew[LENGTH],center=true,$fn=12); |
|
|
|
} |
|
|
|
for (i=[-1,1]) // clamp inserts |
|
translate([i*ClampScrewOC/2,0,-(ClampOA.z/2 + Protrusion)]) |
|
rotate(180/6) |
|
PolyCyl(ClampInsert[OD],ClampInsert[LENGTH],6); |
|
|
|
for (i=[-1,1]) // clamp screw holes |
|
translate([i*ClampScrewOC/2,0,-ClampOA.z]) |
|
rotate(180/6) |
|
PolyCyl(ClampScrew[ID],2*ClampOA.z,6); |
|
|
|
translate([0,0,ClampOA.z/2 – (MountBlade.z/2 – Protrusion/2)]) // camera center blade |
|
rotate(LookAngle) |
|
cube(MountBlade,center=true); |
|
|
|
rotate(LookAngle + 90) // camera mount boss slot |
|
translate([0,0,ClampOA.z/2 + 2*MountRadius]) |
|
cube([MountGap,4*MountRadius,4*MountRadius],center=true); |
|
|
|
translate([0,0,ClampOA.z/2 + MountOffset.z]) // camera mount boss hole |
|
rotate([90,0,LookAngle]) |
|
cylinder(d=MountScrew[ID],h=4*MountGap,center=true,$fn=6); |
|
|
|
translate([0.3*ClampOA.x, // recess for LookAngle legend |
|
-(ClampOA.y/2 – (EmbossDepth – Protrusion)/2), |
|
ClampOA.z/4]) |
|
cube([15,EmbossDepth,8],center=true); |
|
|
|
translate([0,0,-ClampOA.z/2 + (EmbossDepth – Protrusion)/2]) // recess for ID legend |
|
cube([35,10,EmbossDepth],center=true); |
|
} |
|
|
|
translate([0.3*ClampOA.x, // LookAngle legend |
|
-ClampOA.y/2 + DebossHeight + Protrusion/2, |
|
ClampOA.z/4]) |
|
rotate([90,0,00]) |
|
linear_extrude(height=DebossHeight,convexity=20) |
|
text(text=str(LookAngle),size=6,spacing=1.20, |
|
font="Arial:style:Bold",halign="center",valign="center"); |
|
|
|
translate([0,0,-ClampOA.z/2]) // ID legend |
|
linear_extrude(height=DebossHeight,convexity=20) |
|
mirror([0,1,0]) |
|
text(text="KE4ZNU",size=5,spacing=1.20, |
|
font="Arial:style:Bold",halign="center",valign="center"); |
|
} |
|
|
|
cube([2*ClampOA.x,2*ClampOA.y,Kerf],center=true); // split across rail |
|
|
|
rotate([90,0,0]) // seat rail |
|
cylinder(d=RailOD,h=2*ClampOA.y,$fn=RailSides,center=true); |
|
|
|
} |
|
} |
|
|
|
//—– |
|
// Build things |
|
|
|
// Layouts for design & tweaking |
|
|
|
if (Layout == "Show") { |
|
Clamp(); |
|
color(FadeColor,FadeAlpha) |
|
rotate([90,0,0]) |
|
cylinder(d=RailOD,h=2*ClampOA.y,$fn=RailSides,center=true); |
|
} |
|
|
|
|
|
// Build layout |
|
|
|
if (Layout == "Build") { |
|
translate([0,0.7*ClampOA.y,0]) |
|
difference() { |
|
translate([0,0,-Kerf/2]) |
|
Clamp(); |
|
translate([0,0,-ClampOA.z]) |
|
cube(2*ClampOA,center=true); |
|
} |
|
translate([0,-0.7*ClampOA.y,-0]) |
|
difference() { |
|
translate([0,0,-Kerf/2]) |
|
rotate([0,180,0]) |
|
Clamp(); |
|
translate([0,0,-ClampOA.z]) |
|
cube(2*ClampOA,center=true); |
|
} |
|
} |