The batteries (which are no longer available) and the control buttons have worn out on the SJCAM M20 camera on the back of my Tour Easy, so a replacement is in order:

It’s an SJCAM C100+ in its waterproof housing, screwed to a block descended from the M20 mount:

The C100+ has a non-replaceable lithium pouch battery that may not last for the hour or so we generally ride, but at least this is a starting point for seeing how the thing works.
The PrusaSlicer preview shows the support structure inside the seat rail arches:

That appears under the four central facets of each arch, where I “painted” the support enforcers, because the automagic supports fill the entire arch and are basically impossible to pry off.
The hole between the ears on the top holds an aluminum tab intended to diffuse the wobble from that tall camera. A laser-cut chipboard template simplified drilling & cutting the tab from an aluminum sheet:

The tab and the brass inserts are held in place with JB Weld Plastic Bonder, my new go-to adhesive for such things.
The camera includes WiFi and the inevitable app lets you download images without opening the case. Because I’ll be charging the camera after each ride, I may as well just haul the whole thing inside, plug it into a USB port, and proceed as before.
For future reference, the manual details the operating modes:

Because the camera powers up with WiFi enabled and I have no plans to communicate with it while riding, the startup sequence will be:
- Long-press to power on
- Double-click to disable WiFi
- Single-click to start recording
The OpenSCAD source code as a GitHub Gist:
// 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); | |
} | |
} |
2 thoughts on “Tour Easy: SJCAM C100+ Mount”