Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
My Useful Sizes.scad file has been accumulating the dimensions of nuts & bolts & a motor that don’t (seem to) appear elsewhere in the OpenSCAD universe:
The Logitech notebook webcam that peers into the Thing-O-Matic has terrible dynamic range compensation; turning on the LED ring light washes out the image something awful. An old Logitech ball camera seems better, but it sits atop a rubbery dingus adapted to grip huge old laptops. So I built an adapter with a standard 1/4-20 tripod screw thread in the bottom that ought to make it more useful.
The old & new mounts compared:
Logitech ball camera mounts
The color change comes from switching to yellow filament for an upcoming larger object.
The solid model shows those tiny little notches will require a bit of riffler file work:
Logitech camera tripod adapter – solid model
The bottom has a blind 1/4-20 tapped hole. Lacking a bottoming tap, not having any broken 1/4-20 taps, and being unwilling to grind the end off a perfectly good taper tap, I filed three notches along a bolt. Ran the taper tap in until it hit bottom, ran the bolt in likewise, and defined the result to be Good Enough:
Homebrew bottoming tap
On the other end, the most probable failure will leave that delicate little post jammed firmly inside the camera’s socket. There’s not enough post to allow printing a small guide hole, but there’s no real need for one; I drilled a #50 hole right down the middle, ran a 2-56 screw into it without tapping the hole, and filed the screw head flat:
Camera mount with filed screw
After cleaning up those notches, it snapped solidly into place:
Logitech ball camera with mount
And then the camera sits neatly atop a cheap Gorillapod knockoff:
Logitech ball camera on tripod
That tiny reddish dot in the middle of the imposing set of rings marks the actual lens, so it’s more of a pinhole camera than anything else. The fixed focus kicks in beyond a meter, but a bit of rummaging in the Box o’ Lenses produced a random meniscus lens that pulled the focus in to maybe 100 mm. Alas, that means the camera must float in mid-air about 15 mm inside the Thing-O-Matic’s box. If I can conjure up a mount that holds the ball inside the box, above-and-forward of the stage, that’d work great. VLC can allegedly rotate the image upside-down, so maybe I can mount it bottom-up.
Here’s everything I know about those two cameras, with the ball camera on top and the webcam on the bottom:
Logitech ball and notebook webcam data
Apparently it’s easier to put that information on a tag than provide a good old data plate on the camera body.
The OpenSCAD source code:
// Tripod mount for Logitech ball camera
// Ed Nisley KE4ZNU - Oct 2011
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 +0 shells and 3 solid layers
ThreadThick = 0.33;
ThreadWidth = 2.0 * ThreadThick;
HoleFinagle = 0.2;
HoleFudge = 1.02;
function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle;
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
Protrusion = 0.1; // make holes end cleanly
//-------
// Dimensions
BallDia = 60.0; // camera ball
BallRad = BallDia/2;
BaseDia = 16.0; // interface at tripod surface
BaseRad = BaseDia/2;
BaseLength = 10.0; // to base of ball
BoltDia = Tap025_20; // standard 1/4-20 thread
BoltLength = 7.0;
StemLength = 8.5;
StemDia = 4.7;
StemRad = StemDia/2;
FlangeWidth = 6.6;
FlangeThick = 2.6;
NotchSectionDia = 1.4; // toroid cross-section diameter
NotchSectionRad = NotchSectionDia/2;
NotchOffset = 2.3; // from top of stem
//-------
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);
}
//-------
//
ShowPegGrid();
translate([0,0,BaseLength])
union() {
difference() {
translate([0,0,-BaseLength])
cylinder(r=BaseRad,h=2*BaseLength);
translate([0,0,BallRad])
sphere(r=BallRad);
translate([0,0,-(BaseLength + Protrusion)])
PolyCyl(BoltDia,(BoltLength + Protrusion));
}
rotate(180/16)
cylinder(r=StemRad,h=StemLength,$fn=16);
difference() {
translate([0,0,StemLength/2])
cube([FlangeWidth,FlangeThick,StemLength],center=true);
translate([0,0,(StemLength - NotchOffset)])
rotate_extrude(convexity=3,$fn=64)
translate([FlangeWidth/2,0,0])
circle(r=NotchSectionRad,$fn=16);
translate([0,-FlangeWidth/2,StemLength + sqrt(FlangeWidth)])
rotate([0,45,0])
cube(FlangeWidth + 2*Protrusion);
translate([0,FlangeWidth/2,StemLength + sqrt(FlangeWidth)])
rotate([0,45,180])
cube(FlangeWidth + 2*Protrusion);
}
}
The squeeze handle that tightens the bar clamp cracked exactly where you’d expect: directly across the pivot hole where the miracle engineering plastic thins down to a precarious ridge. The end of the handle is still inside the clamp:
Bar clamp with broken handle
Nothing bonds that plastic, so, in the nature of a quick fix, I cut a steel strap to wrap around the perimeter of the broken section and epoxied the whole mess together:
Reinforced bar clamp handle
That lasted for exactly 2.5 squeezes and then pulled apart; the epoxy doesn’t really have anything to grab.
ABS isn’t a good substitute for engineering plastic, so this will require a bit of CNC work on the Sherline. I’ll probably carve the first one from polycarbonate, just because I have a sheet of the right thickness, but it really cries out for aluminum, doesn’t it?
Why CNC? Well, I’m going to make a handful of handles and get proactive on the other clamps.
My other bar clamps have much heavier sections in that area, so perhaps the folks supplying Harbor Freight could take a hint? Yeah, but the clamp was cheap, which always conflicts with good. On the other paw, I’ve seen exactly this same clamp priced at not cheap elsewhere.
We frequently host touring bicyclists who need a campsite in the Mid-Hudson Valley. The most recent couple has been riding for two years, starting eastward from Paris shortly after their wedding. Yeah, it’s a honeymoon trip.
After riding through Western and Eastern Europe, the Middle East, and several of the ‘Stans, JeanMarc’s handlebar mirror broke in Kazakhstan. Marie toted the carcass out of the ‘Stans, across India, through China, and then from Montreal to here. They’re biking to Houston, where they’ll fly to Peru, ride south and across the Andes, and work their way across the Atlantic on a cargo ship that eventually docks in Germany. Then, a year from now, they’ll just bike back to Paris.
Makes you feel like sludge, too, doesn’t it?
With that as prologue, JeanMarc wondered if I could fix the mirror mount. It started as a 10 mm plastic ball on a molded plastic fitting with an integral worm screw and strap; of course, the ball stem snapped off during a hard landing or some such event that comes naturally during long-distance riding. We kicked around some ideas, rummaged through the heap, and came up with a workable, albeit hideous solution.
I applied a Dremel slitting wheel to a pair of Zerk grease fittings, sliced off the inlet valve, extracted the valve spring, and cleaned up the residue to leave a somewhat misshapen 9.3 mm (really a scant 3/8 inch) ball-like end. A bit of lathe work converted a chunk of PVC pipe into a sleeve grooved for a metal hose clamp. I drilled two #3 holes, tapped them 1/4-28 (which, believe it or not, is the correct thread for a Zerk), bandsawed the pipe in half, introduced the pieces to Mr Belt Sander to round the edges, screwed Zerks into holes, and wound up with a pair of these:
Handlebar Mirror Mount – detail
Which looks awful on the handlebars, but we’re pretty sure it won’t break and he has a spare if the mirror on Marie’s bike snaps off:
Handlebar Mirror Mount – fixed
The Zerk fitting could unscrew, but the threads aren’t exactly in pristine condition after all that fussing and seem to be jammed firmly in place. If we had more time, I’d have heated the PVC and molded it around the handlebars, but we decided that wasn’t really necessary.
They rode off into the distance this morning… may you have smooth roads and a tailwind, JeanMarc and Marie!
So I picked up a cheap digital scale at Harbor Freight because it can count parts based on weight. After all the dust settled, it was on sale for about $8, which tells you just about all you need to know, and the “5 Year Warranty” looked generous on the box:
Harbor Freight 1 kg Scale
Alas, the fine print taketh away (clicky for more dots):
Warranty
Ah, well, all this stuff is disposable anyway, right? Nobody’d ever try to fix it…
The instructions for the Count function omit a step. In order to invoke the Count function, do this dance:
Turn it on
Count exactly 10 pieces on the scale, wait for stabilization
Press-and-hold PCS until the display shows 10
Release PCS
Press PCS briefly; the pcs annunciator turns on (they omitted that)
The display will still show 10, which is the number of pieces
Now you can weigh stuff and read off their counts
The scale resolution is 0.1 gram, so SMD resistors just aren’t going to count properly at all. It’s best if you add the entire group at one time, rather than trickle parts into the pan.
Scaling the cubes to about 15 mm on a side puts a 6×6 array neatly on the build plate. Takes nigh onto four hours to print all 36 of them at 30 mm/s print and 100 mm/s move… a bit over 6 minutes each.
The print quality is Good Enough. The bottom surface of the front cubes faces forward and reflects the scale markings:
When confronted with a zombie horde, though, nothing exceeds like excess:
Finned CO2 Cartridge Array
In real life, they’re 12 gram CO2 capsules, of the type used in tire inflators and air pistols. I knew I’d find something to do with the box of empties I’d been accumulating: they became (somewhat threatening) tchotchkes. This was inspired by that thing, although that STL file doesn’t render into anything and, as with many interesting Thingiverse things, there’s no source code.
These fins were an exercise in thin-wall printing: the outer square is one thread thick, the diagonal struts are two threads, and the ring around the nozzle has just a touch of fill inside, with a one-thread-thick base below the cartridge nozzle:
Fin Array on build platform
The solid model looks about like you’d expect:
Fin Assembly- solid model
The teeny little quarter-cylinders in the corners encourage Skeinforge to do the right thing: build each quadrant in one pass, leaving the corners unfinished. The diagonals must be exactly two threads wide to make that possible: each strut thread connects to the corresponding single-thread outer edge.
It turns out that my box has several different types of CO2 cartridges and the nozzle ends are all different. To get it right, there’s a template for matching the curves:
Cartridge nozzle template
That end of the cartridge consists of a cylinder for the body, a sphere mated to a tangential conic section, another conic fillet, and then the cylindrical nozzle. Basically, you twiddle with the parameters until the template comes pretty close to fitting, then fire off a few trial fins until it comes out right.
CO2 Capsule Nozzle – solid model detail
They were a big hit at the Long Island Linux Users Group meeting…
The OpenSCAD source code:
// CO2 capsule tail fins
// Ed Nisley KE4ZNU - Oct 2011
Layout = "Show"; // Show Build FinBlock Cartridge Fit
include
//-------
//- Extrusion parameters must match reality!
// Print with +0 shells and 3 solid layers
ThreadThick = 0.33;
ThreadWidth = 2.0 * ThreadThick;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
//-------
// Capsule dimensions
BodyDia = 18.70;
BodyRad = BodyDia/2;
BodyLength = 53.0; // between hemispherical endcap centers
BodyBaseLength = 21; // tip to endcap center
TipDia = 7.40;
TipRad = TipDia/2;
TipLength = IntegerMultiple(4.0,ThreadThick);
FilletLength = 5.0; // fillet between tip and cone
FilletTop = TipLength + FilletLength;
FilletBaseDia = 8.60;
FilletBaseRad= FilletBaseDia/2;
FilletTopDia = 9.5;
FilletTopRad = FilletTopDia/2;
ConeTop = 16.0; // tip to tangent with endcap
ConeLength = ConeTop - FilletTop;
echo(str("Cone Length: ",ConeLength));
IntersectZ = ConeTop; // coordinates of intersect tangent
IntersectX = sqrt(pow(BodyRad,2) - pow(BodyBaseLength - ConeTop,2));
echo(str("IntersectZ: ",IntersectZ));
echo(str("IntersectX: ",IntersectX," dia: ",2*IntersectX));
//-------
// Fin dimensions
FinThick = 1*ThreadWidth; // outer square
StrutThick = 2*FinThick; // diagonal struts
FinSquare = 24.0;
FinTaperLength = sqrt(2)*FinSquare/2 - sqrt(2)*FinThick - ThreadWidth;
FinBaseLength = 2*TipLength;
//-------
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);
}
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);
}
//-------
// CO2 cartridge outline
module Cartridge() {
$fn = 48;
union() {
translate([0,0,BodyBaseLength]) {
cylinder(r=BodyDia/2,h=BodyLength);
translate([0,0,BodyLength])
sphere(r=BodyRad);
}
intersection() {
translate([0,0,BodyBaseLength])
sphere(r=BodyRad);
union() {
translate([0,0,(TipLength + FilletLength+ConeLength)])
cylinder(r=BodyRad,h=(BodyBaseLength - ConeLength));
translate([0,0,(TipLength + FilletLength)])
cylinder(r1=FilletTopRad,r2=IntersectX,h=(ConeLength + Protrusion));
translate([0,0,TipLength])
cylinder(r1=FilletBaseRad,r2=FilletTopRad,h=(FilletLength + Protrusion));
}
}
translate([0,0,FilletTop])
cylinder(r1=FilletTopRad,r2=IntersectX,h=ConeLength);
translate([0,0,TipLength])
cylinder(r1=FilletBaseRad,r2=FilletTopRad,h=(FilletLength + Protrusion));
translate([0,0,-Protrusion])
PolyCyl(TipDia,(TipLength + 2*Protrusion));
}
}
//-------
// Diagonal fin strut
module FinStrut() {
rotate([90,0,45])
translate([0,0,-StrutThick/2])
linear_extrude(height=StrutThick)
polygon(points=[
[0,0],
[FinTaperLength,0],
[FinTaperLength,FinBaseLength],
[0,(FinBaseLength + FinTaperLength)]
]);
}
//-------
// Fin outline
module FinBlock() {
union() {
translate([0,0,FinBaseLength/2])
difference() {
cube([FinSquare,FinSquare,FinBaseLength],center=true);
difference() {
cube([(FinSquare - 2*FinThick),
(FinSquare - 2*FinThick),
(FinBaseLength + 2*Protrusion)],center=true);
for (Index = [0:3])
rotate(Index*90)
translate([(FinSquare/2 - FinThick),(FinSquare/2 - FinThick),0])
cylinder(r=StrutThick,h=(FinBaseLength + 2*Protrusion),center=true,$fn=16);
}
}
for (Index = [0:3])
rotate(Index*90)
FinStrut();
cylinder(r=IntegerMultiple((FilletBaseRad + StrutThick),ThreadWidth),h=TipLength);
}
}
//-------
// Fins
module FinAssembly() {
difference() {
FinBlock();
translate([0,0,ThreadThick]) // add one layer to close base cylinder
Cartridge();
}
}
module FinFit() {
translate([0,0.75*BodyBaseLength,2*ThreadThick])
rotate([90,0,0])
difference() {
translate([-FinSquare/2,-2*ThreadThick,0])
cube([IntegerMultiple(FinSquare,ThreadWidth),
4*ThreadThick,
1.5*BodyBaseLength]);
translate([0,0,5*ThreadWidth])
Cartridge();
}
}
//-------
// Build it!
ShowPegGrid();
if (Layout == "FinBlock")
FinBlock();
if (Layout == "Cartridge")
Cartridge();
if (Layout == "Show") {
FinAssembly();
color(LG) Cartridge();
}
if (Layout == "Fit")
FinFit();
if (Layout == "Build")
FinAssembly();