The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Tag: M2

Using and tweaking a Makergear M2 3D printer

  • Browning Hi-Power Magazine Capacity Reduction Block: Steel Version

    The Shapeways stainless steel process produces nice results:

    Browning HP Mag Blocks - stainless and plastic - side
    Browning HP Mag Blocks – stainless and plastic – side

    It’s actually bronze-infused stainless steel powder, so it’s not exactly solid steel. The parts spend a day rattling around in a vibratory polisher that slightly rounds off their edges and smooths the surface, but (as with all 3D printed objects) you must learn to love the results; it’s certainly more photogenic than the black plastic version from my M2.

    The bottom view shows the hole I added to reduce the metallic volume; they charge a bit under $0.01/mm3, which encourages airy design:

    Browning HP Mag Blocks - stainless and plastic - bottom
    Browning HP Mag Blocks – stainless and plastic – bottom

    A cross-section view of the solid model shows the interior structure:

    Browning Hi-Power Magazine Block - steel - solid model - section
    Browning Hi-Power Magazine Block – steel – solid model – section

    The vent pipes are somewhat larger than in the plastic version and, obviously, I didn’t include the yellow support structures in the model I sent to Shapeways.

    Their specs give a minimum wall thickness of 3.0 mm, which I’m definitely pushing on some of the internal features. The pipes came out perfectly, as nearly as I can tell, although some polishing media did get wedged in the smaller hole. Air passes freely across the top, which is the important part.

    Although the specs list a ±2 mm (!) tolerance, a comment in a Shapeways forum said that applies to larger objects, with 0.2 mm being typical for smaller objects. The steel and plastic parts match within 0.2 mm of the nominal model dimensions, so that lower tolerance seems about right; I have no idea how consistent it is.

    Another comment recommended carbide tools for secondary operations and that’s definitely true; I wrecked a perfectly good HSS tap trying to thread the central hole. Fortunately, I made the block slightly smaller outside and slightly larger inside, specifically to avoid having a deep thread; I intend to ram a standard M3x0.5 SHCS into that hole and epoxy it in place without worrying about thread damage.

    A trial fit shows it captures the spring tab just like the plastic version did:

    Browning Hi-Power magazine - steel block trial fit
    Browning Hi-Power magazine – steel block trial fit

    I must contact my legislators again, as I’m pretty sure they’re not going to contact me.

    The OpenSCAD source code:

    // Browning Hi-Power Magazine Plug
    // Ed Nisley KE4ZNU December 2013
    
    Layout = "Show";			// Show Whole Split
    							//  Show = section view for demo, not for building
    							//  Whole = upright for steel or plastic
    							//  Split = laid flat for plastic show-n-tell assembly
    
    AlignPins = (Layout == "Split");					// pins only for plastic show-n-tell
    
    Support = true && (Layout != "Split");				// no support for split, optional otherwise
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    ThreadThick = 0.15;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    Angle = 12.5;				// from vertical
    
    SpringID = 10.3;			// magazine spring curvature (measure with drill shank)
    SpringRadius = SpringID / 2;
    
    Length = 23.0;				// front-to-back perpendicular to magazine shaft
    Height = 18.0;				// bottom-to-top, parallel to magazine shaft
    							//  18 = 10 round max capacity
    
    RectLength = Length - SpringID;	// block length between end radii
    
    HornBaseOD = 8.0;			// fits between follower pegs to prevent shortening
    HornTipOD = 5.0;
    HornAddTip = (HornTipOD/2)*tan(Angle);
    HornAddBase = (HornBaseOD/2)*tan(Angle);
    HornAddLength = HornAddTip + HornAddBase + 2*Protrusion;
    HornLength = 12.0;			// should recompute ODs, but *eh*
    
    ScrewOD = 3.0 - 0.25;		// screw hole dia - minimal thread engagement
    ScrewLength = 13.0;
    ScrewOffset = -1.5;			//   ... from centerline
    							//  OEM = 0.0
    							//  generic A = -1.5
    
    NutOD = 5.6;				// hex nut dia across flats
    NutThick = 2.4;				//  ... generous allowance for nut
    NutTrapLength = 1.5*NutThick;		// allow for epoxy buildup
    NutOffset = 6.0;			//  ... base height from floor
    
    TrimHeight = 2.5;			// vertical clearance for spring clip on base plate
    							//   OEM = 2.5
    							//   generic A = 2.5
    
    TrimOffset = -9.5 + ScrewOffset;	// ... horizontal from centerline
    							//	 OEM = 0.0
    							//   generic A = 1.5
    
    SupportLength = 4.0;		// length of support struts under Trim
    SupportWidth = SpringID;	// ... width
    
    VentDia = 2.5;				// air vent from back of screw recess
    VentOffset = ScrewOffset - 6.0;
    
    RecessDia = 5.0;			// recess to reduce weight
    RecessLength = 0.66*Length;	//  ... internal length
    RecessOffset = 8.5;	//  ... offset from centerline
    
    PinOD = 1.72;				// alignment pins
    PinLength = 6.0;
    PinInset = 0.6*SpringRadius;	// from outside edges
    echo(str("Alignment pin length: ",PinLength));
    
    NumSides = 8*4;				// default cylinder sides
    
    Offset = 5.0/2;				// from centerline for build layout
    
    //----------------------
    // Useful routines
    
    function Delta(a,l) = l*tan(a);				// incremental length due to angle
    
    // Locating pin hole with glue recess
    //  Default length is two pin diameters on each side of the split
    
    module LocatingPin(Dia=PinOD,Len=0.0) {
    
    	PinLen = (Len != 0.0) ? Len : (4*Dia);
    
    	translate([0,0,-ThreadThick])
    		PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4);
    
    	translate([0,0,-2*ThreadThick])
    		PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4);
    
    	translate([0,0,-(Len/2 + ThreadThick)])
    		PolyCyl(Dia,(Len + 2*ThreadThick),4);
    
    }
    
    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);
    
    }
    
    //----------------------
    // The magazine block
    
    module Block(SectionSelect = 0) {
    
    CropHeight = Height*cos(Angle);				// block height perpendicular to base
    echo(str("Perpendicular height: ",CropHeight));
    
    	difference() {
    		union() {
    			intersection() {
    				rotate([Angle,0,0])
    					hull() {
    						for (i=[-1,1])
    							translate([0,i*RectLength/2,-((Length/2)*sin(Angle) + Protrusion)])						cylinder(r=SpringRadius,
    										h=(Height + 2*(Length/2)*sin(Angle) + 2*Protrusion),
    										$fn=NumSides);
    					}
    				translate([0,0,CropHeight/2])
    					cube([2*SpringID,3*Length,CropHeight],center=true);
    			}
    			translate([0,-Height*sin(Angle),Height*cos(Angle)])
    				resize([SpringID,0,0])
    					intersection() {
    						rotate([Angle,0,0])
    							translate([0,0,-(HornAddBase + Protrusion)])
    								cylinder(r1=HornBaseOD/2,
    										r2=HornTipOD/2,
    										h=(HornLength + HornAddLength + Protrusion),
    										$fn=NumSides);
    					cube([2*SpringID,Length,2*(HornLength*cos(Angle) + Protrusion)],center=true);
    				}
    		}
    
    		translate([0,ScrewOffset,-Protrusion])		// screw
    			rotate(180/6)
    				PolyCyl(ScrewOD,(ScrewLength + Protrusion),6);
    
    		translate([0,ScrewOffset,NutOffset])		// nut trap in center
    			rotate(180/6)
    				PolyCyl(NutOD,NutTrapLength,6);
    
    		translate([0,ScrewOffset,-Protrusion])		// nut clearance at base
    			rotate(180/6)
    				PolyCyl(NutOD,(1.1*NutThick + Protrusion),6);
    
    		translate([SpringID/2,TrimOffset,-Protrusion])
    			rotate(180)
    				cube([SpringID,Length,(TrimHeight + Protrusion)],center=false);
    
    		if (AlignPins)								// alignment pins
    			for (i=[-1,1])
    				rotate([Angle,0,0])
    				translate([0,
    							(i*((Length/2)*cos(Angle) - PinInset)),
    							(CropHeight/2 - i*2*PinInset)])
    					rotate([0,90,0]) rotate(45 - Angle)
    						LocatingPin(PinOD,PinLength);
    
    		translate([0,(ScrewOffset + 1.25*NutOD),ScrewLength])	// air vent
    			rotate([90,0,0]) rotate(180/8)
    				PolyCyl(VentDia,3*NutOD,8);
    		translate([0,VentOffset,-(VentDia/2)*tan(Angle)])
    			rotate([Angle,0,0]) rotate(180/8)
    				PolyCyl(VentDia,(RecessLength + (VentDia/2)*tan(Angle)),8);
    
    		translate([0,(RecessOffset + ScrewOffset),0])			// weight reduction recess
    			rotate([Angle,0,0]) rotate(180/8)
    				translate([0,0,-((RecessDia/2)*tan(Angle))])
    				PolyCyl(RecessDia,(RecessLength + (RecessDia/2)*tan(Angle)),8);
    
    		if (SectionSelect == 1)
    			translate([0*SpringID,-2*Length,-Protrusion])
    				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
    		else if (SectionSelect == -1)
    			translate([-2*SpringID,-2*Length,-Protrusion])
    				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
    	}
    
    SupportBars = floor((SupportWidth/2) / (4*ThreadWidth));
    
    	if (Support) {									// add support structures
    		for (i = [-SupportBars:SupportBars])
    			translate([i*4*ThreadWidth,
    					   (TrimOffset - SupportLength/2 - ThreadWidth),
    					   (TrimHeight - ThreadThick)/2])
    				color("Yellow")
    				cube([(2*ThreadWidth),SupportLength,(TrimHeight - ThreadThick)],center=true);
    
    		translate([0,(TrimOffset - SupportLength - ThreadWidth),(TrimHeight - ThreadThick)/2])
    			color("Yellow")
    			cube([SupportWidth,(2*ThreadWidth),(TrimHeight - ThreadThick)],center=true);
    
    		translate([0,ScrewOffset,0])
    			for (j=[0:5]) {
    			rotate(30 + 360*j/6)
    				translate([(NutOD/2 - ThreadWidth)/2,0,(1.1*NutThick - ThreadThick)/2])
    					color("Yellow")
    					cube([(NutOD/2 - ThreadWidth),
    						  (2*ThreadWidth),
    						  (1.1*NutThick - ThreadThick)],
    						  center=true);
            }
    	}
    
    }
    
    //-------------------
    // Build it...
    
    ShowPegGrid();
    
    if (Layout == "Show")
    	Block(1);
    
    if (Layout == "Whole")
    	Block(0);
    
    if (Layout ==  "Split") {
    	translate([(Offset + Length/2),Height/2,0])
    		rotate(90) rotate([0,-90,-Angle])
    			Block(-1);
    	translate([-(Offset + Length/2),Height/2,0])
    		rotate(-90) rotate([0,90,Angle])
    			Block(1);
    }
    
  • Planetary Gear Bearing: Now With Knurling!

    OK, I couldn’t resist. Tweaking a few lines of code wrapped a knurl around emmitt’s Gear Bearing for enhanced griptivity:

    Knurled vs original Planetary Gear Bearing
    Knurled vs original Planetary Gear Bearing

    That image has desaturated red to suppress the camera’s red burnout. It looks better in the realm of pure math:

    Planetary Gear Bearing - Kurled - solid model
    Planetary Gear Bearing – Kurled – solid model

    Reducing the tolerance parameter to 0.4 produced a surprisingly rigid, yet freely turning, bearing that required no cleanup: it popped off the plate ready to roll!

    The heavy lifting in the OpenSCAD source code remains emmitt’s work. I replaced the outer cylinder with a knurl and simplified his monogram to stand out better amid the diamonds. This is the affected section:

    ... snippage ...
    translate([0,0,T/2]){
    	difference(){
    //		cylinder(r=D/2,h=T,center=true,$fn=100);
    		render(convexity=10)
    		translate([0,0,-T/2])
    			knurl(k_cyl_hg=T,
    			k_cyl_od=D,
    			knurl_wd=5.0,
    			knurl_hg=5.0,
    			knurl_dp=0.5,
    			e_smooth=5.0/2);
    		herringbone(nr,pitch,P,DR,-tol,helix_angle,T+0.2);
    //		difference(){
    			translate([0,-(D/2+4.5),0])rotate([90,0,0])monogram(h=10);
    //			cylinder(r=D/2-0.25,h=T+2,center=true,$fn=100);
    //		}
    	}
    	rotate([0,0,(np+1)*180/ns+phi*(ns+np)*2/ns])
    	difference(){
    		mirror([0,1,0])
    			herringbone(ns,pitch,P,DR,tol,helix_angle,T);
    		cylinder(r=w/sqrt(3),h=T+1,center=true,$fn=6);
    	}
    	for(i=[1:m])rotate([0,0,i*360/m+phi])translate([pitchD/2*(ns+np)/nr,0,0])
    		rotate([0,0,i*ns/m*360/np-phi*(ns+np)/np-phi])
    			render(convexity=10)
    			herringbone(np,pitch,P,DR,tol,helix_angle,T);
    }
    

    I also added a few render(convexity=n) operations to improve the preview, but that’s just cosmetic.

  • Bell Helmet Visor Mount Tabs

    Santa delivered a pair of helmets that will require mirror mounts and a mic boom before the spring riding season kicks in. The visor has tabs that snap into sockets on each side of the helmet:

    Bell Helmet Visor Mount - socket
    Bell Helmet Visor Mount – socket

    It occurred to me that I could make an interposer between the helmet and the visor that could anchor the mic boom, with a tab for the helmet and a socket of some sort for the visor. While that’s still on the to-do list, the tab looks like this:

    Bell Helmet Visor Mount
    Bell Helmet Visor Mount

    Those are 1 mm cubes on 10 mm centers, so this is a teeny little thing.

    I don’t have a good idea for the corresponding socket, because those little grippers seem much too small for 3D printing, but now I have some tabs to play with:

    Bell Helmet Visor Mount - OEM vs 3D Printed
    Bell Helmet Visor Mount – OEM vs 3D Printed

    The OpenSCAD source code puts the tab atop an oval base plate, but it’ll eventually stick out of the boom mount:

    // Bell Helmet Visor Mount
    // Ed Nisley KE4ZNU
    // December 2013
    
    // Layout options
    
    Layout = "Build";			// Build Show
    
    //-----
    // Extrusion parameters must match reality!
    
    ThreadThick = 0.20;
    ThreadWidth = 0.4;
    
    HoleWindage = 0.2;
    
    //-- Handy stuff
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;			// make holes end cleanly
    
    inch = 25.4;
    
    //----------------------
    // Dimensions
    
    //----------------------
    // Useful routines
    
    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);
    }
    
    //- Put peg grid on build surface
    
    module ShowPegGrid(Space = 10.0,Size = 1.0) {
    
    	RangeX = floor(100 / Space);
    	RangeY = floor(125 / Space);
    
    	for (x=[-RangeX:RangeX])
    		for (y=[-RangeY:RangeY])
    			translate([x*Space,y*Space,Size/2])
    			%cube(Size,center=true);
    
    }
    
    //-------------------
    // Shapes
    
    TabBaseLength = 17.0;
    TabTopLength = 15.5;
    
    TabWidth = 4.00;
    TabHeight = 5.5;
    TabEmbed = 0.5;
    TabTaperHeight = 3.70;
    TabBaseHeight = TabHeight - TabTaperHeight;
    
    LatchBar = 2.25;					// square cross section
    WebIndent = 1.60;					// from outside edge of post
    WebThick = TabWidth - 2*WebIndent;
    LatchIndentTall = TabHeight - LatchBar;
    
    PostLength = 5.00;
    PostTaper = 1.25;
    LatchIndentLength = TabBaseLength - 2*(PostLength + PostTaper);
    
    module BellLatch() {
    
    	difference() {
    		intersection() {
    			translate([0,TabWidth/2,0]) rotate([90,0,0])				// side view
    			linear_extrude(height=TabWidth)
    				polygon(points=[
    					[-TabBaseLength/2,-TabEmbed],[-TabBaseLength/2,TabBaseHeight],[-TabTopLength/2,TabHeight],
    					[TabTopLength/2,TabHeight],[TabBaseLength/2,TabBaseHeight],[TabBaseLength/2,-TabEmbed]
    				]);
    
    			translate([0,0,-TabEmbed])
    			linear_extrude(height=(TabHeight + TabEmbed),convexity=3)				// top view
    				polygon(points=[
    					[-TabBaseLength/2,-TabWidth/2],
    					[-TabBaseLength/2, TabWidth/2],
    					[-(TabBaseLength/2 - PostLength), TabWidth/2],
    					[-(TabBaseLength/2 - PostLength - PostTaper),LatchBar/2],
    					[ (TabBaseLength/2 - PostLength - PostTaper),LatchBar/2],
    					[ (TabBaseLength/2 - PostLength),TabWidth/2],
    					[ TabBaseLength/2, TabWidth/2],
    					[ TabBaseLength/2,-TabWidth/2],
    					[ (TabBaseLength/2 - PostLength),-TabWidth/2],
    					[ (TabBaseLength/2 - PostLength - PostTaper),-LatchBar/2],
    					[-(TabBaseLength/2 - PostLength - PostTaper),-LatchBar/2],
    					[-(TabBaseLength/2 - PostLength),-TabWidth/2]
    				]);
    		}
    		for (y=[-1,1])
    		translate([0,y*((TabWidth/2 + WebThick/2)),LatchIndentTall/2])
    			cube([LatchIndentLength,TabWidth,LatchIndentTall],center=true);
    	}
    
    /*	difference() {
    		translate([0,0,TabHeight/2])
    		cube([TabLength,TabWidth,TabHeight],center=true);
    	}
    */
    }
    
    //-------------------
    // Build things...
    
    ShowPegGrid();
    
    if (Layout == "Show")
    	BellLatch();
    
    if (Layout == "Build") {
    	translate([0,0,2.0])
    		BellLatch();
    	difference() {
    		resize([20.0,10.5,2.0])
    			cylinder(r=2,h=2,$fn=32);
    		for (x=[-1,1])
    			translate([x*(5/2 + TabBaseLength/2 + 0.5),0,5+0.6])
    				cube([5,25,10],center=true);
    	}
    
    }
    
  • Planetary Gear Bearing

    Most of the things I design don’t have moving parts, so I printed emmitt’s Gear Bearing as a fondletoy:

    Planetary Gear Bearing
    Planetary Gear Bearing

    Setting the clearance to 0.5 produced a free fit with absolutely no cleanup or run-in required; the center hole is a sliding fit for a 6 mm hex wrench.

    I should do another one with knurling around the outside…

    The picture has strongly desaturated reds, which reveals the top surface a bit more clearly.

  • Sherline CNC Mill Z-axis Overrun Prevention Block

    The alert reader will already have noticed the absence of the Z-axis home switch in this picture from yesterday’s post:

    Sherline CNC mill - tommy bar and collet pusher
    Sherline CNC mill – tommy bar and collet pusher

    Turns out that I managed to crunch it, exactly as I expected: I’d added a block to the Z-axis stage that poked the home switch just slightly before the anti-backlash nut unscrewed from the top of the leadscrew, but the stage could continue moving another few millimeters.

    You can see the gap just above the brass anti-backlash nut:

    Sherline Z-axis leadscrew nut - top end
    Sherline Z-axis leadscrew nut – top end

    At that point, the nut has barely a single micro-smidgen of thread engaged; that last 0.1340 inch of travel (yeah, I measured it) isn’t usable.

    Rather than put a collar around the end of the leadscrew, I opted for a brute-force block atop the Z-axis saddle nut that will slam into the bottom of the stepper motor mount just before the anti-backlash nut disengages:

    Sherline Z-axis Overrun Block - rear view
    Sherline Z-axis Overrun Block – rear view

    A strip of tapeless sticky (double-sided tape, minus the tape) holds the block in place on the saddle nut. It’s not subject to any particular stress: as long as it doesn’t fall off, it’s all good.

    I ran the stage upward until it stalled, then epoxied a new switch (with the old fluorescent tape) in place. This shows the result after backing the stage down a few millimeters:

    Sherline Z-axis Overrun Block - side view
    Sherline Z-axis Overrun Block – side view

    The solid model shows off the bevel that provides a bit more room for anti-backlash nut adjustment, not that I ever adjust it that much:

    Sherline Z-Axis Overrun Prevention Block - solid model
    Sherline Z-Axis Overrun Prevention Block – solid model

    Obviously, it doesn’t print in that position, but it’s easier to design it in the natural orientation and flip it around for printing.

    The OpenSCAD source code:

    // Sherline Z-axis Overrun Prevention Block
    // Ed Nisley KE4ZNU December 2013
    
    Layout = "Show";			// Show Build
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    BlockZ = 30.0;				// overall height
    ZLimit = 17.0;				// Z travel limit
    
    TongueX = 9.0;				// beside Z axis dovetail
    TongueY = 10.0;
    
    StubX = 6.0;				// behind Z axis pillar
    StubY = 3.0;
    
    BlockX = TongueX + StubX;	// overall X
    
    TabY = 3.0;					// behind brass bracket
    TabX = BlockX - sqrt(2)*TabY;
    TabZ = BlockZ - ZLimit;
    
    BlockY = TongueY + StubY + TabY;	// overall Y
    
    //----------------------
    // Useful routines
    
    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);
    
    }
    
    //- The Block
    
    module Block() {
    
    	difference() {
    		cube([BlockX,BlockY,BlockZ]);
    
    		translate([-Protrusion,-Protrusion,-Protrusion])	// remove column
    			cube([(StubX + Protrusion),(TongueY + Protrusion),2*BlockZ]);
    
    		translate([-BlockX/2,-Protrusion,-Protrusion])		// form tab
    			cube([2*BlockX,(TongueY + StubY),(TabZ + Protrusion)]);
    
    		translate([0,BlockY,(BlockZ/2 - 0*Protrusion)])
    			rotate(45)
    				cube([3*StubY,2*StubY,(BlockZ + 2*Protrusion)],center=true);
    
    		translate([0,0,-Protrusion])
    			cube([sqrt(2)*TabY,2*BlockY,(TabZ + Protrusion)]);
    	}
    }
    
    //-------------------
    // Build it...
    
    ShowPegGrid();
    
    if (Layout == "Show")
    	Block();
    
    if (Layout == "Build")
    	translate([-BlockZ/2,-BlockY/2,BlockX])
    	rotate([0,90,0])
    		Block();
    
  • Sherline Tommy Bar Handles

    While putting the speed wrenches in the box with the Sherline four-jaw chuck, it occurred to me that I had all the makings of a handle for Sherline’s steel tommy bars:

    Sherline Tommy Bar Handle - solid model
    Sherline Tommy Bar Handle – solid model

    Because these are intended for pushing, rather than twisting, I dialed the knurl back to 32 DP, reduced the depth to 0.5 mm, and ran the bar almost all the way through the handle for strength:

    Sherline Tommy Bar Handles
    Sherline Tommy Bar Handles

    A dab of urethane adhesive inside the handle holds the bar in place. They started out a snug slip fit, so we’ll see how well that holds the bars in place.

    A tommy bar holds the spindle against the torque from the collet pusher:

    Sherline CNC mill - tommy bar and collet pusher
    Sherline CNC mill – tommy bar and collet pusher

    A pair will come in handy with the three-jaw chuck the next time that one appears.

    The white slab is a very early 3D printed tool from my Thing-O-Matic, made to hold the pin at exactly the proper distance from the pulley so it fits squarely into the pusher and locks it to the spindle:

    Locking pin holder - spindle end view
    Locking pin holder – spindle end view

    Other folks make much nicer tommy bar handles than mine, but I’d say my 3D printed handles beat a common nail any day!

    The OpenSCAD source code:

    // Knurled handles for Sherline tommy bars
    // Ed Nisley - KE4ZNU - December 2013
    
    use <knurledFinishLib_v2.scad>
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;			// extra clearance
    
    Protrusion = 0.1;			// make holes end cleanly
    
    PI = 3.14159265358979;
    inch = 25.4;
    
    //----------------------
    // Dimensions
    
    ShaftDia = 10.0;				// un-knurled section diameter
    ShaftLength = 10.0;				//  ... length
    
    SocketDia = 4.0;				// tommy bar diameter
    SocketDepth = 40.0;
    
    KnurlLen = 35.0;				// length of knurled section
    KnurlDia = 15.0;				//   ... diameter
    KnurlDPNom = 32;				// Nominal diametral pitch = (# diamonds) / (OD inches)
    
    DiamondDepth = 0.5;				//   ... depth of diamonds
    DiamondAspect = 2;				// length to width ratio
    
    NumDiamonds = floor(KnurlDPNom * KnurlDia / inch);
    echo(str("Num diamonds: ",NumDiamonds));
    
    NumSides = 4*(NumDiamonds - 1);		// 4 facets per diamond. Library computes diamonds separately!
    
    KnurlDP = NumDiamonds / (KnurlDia / inch);				// actual DP
    echo(str("DP Nom: ",KnurlDPNom," actual: ",KnurlDP));
    
    DiamondWidth = (KnurlDia * PI) / NumDiamonds;
    
    DiamondLenNom = DiamondAspect * DiamondWidth;					// nominal diamond length
    DiamondLength = KnurlLen / round(KnurlLen/DiamondLenNom);		//  ... actual
    
    TaperLength = 0.75*DiamondLength;
    
    //----------------------
    // Useful routines
    
    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);
    }
    
    //- Build it
    
    ShowPegGrid();
    
    difference() {
    	union() {
    		render(convexity=10)
    		translate([0,0,TaperLength])
    			knurl(k_cyl_hg=KnurlLen,
    				  k_cyl_od=KnurlDia,
    				  knurl_wd=DiamondWidth,
    				  knurl_hg=DiamondLength,
    				  knurl_dp=DiamondDepth,
    				  e_smooth=DiamondLength/2);
    		color("Orange")
    		cylinder(r1=ShaftDia/2,
    					r2=(KnurlDia - DiamondDepth)/2,
    					h=(TaperLength + Protrusion),
    					$fn=NumSides);
    		color("Orange")
    		translate([0,0,(TaperLength + KnurlLen - Protrusion)])
    			cylinder(r2=ShaftDia/2,
    					r1=(KnurlDia - DiamondDepth)/2,
    					h=(TaperLength + Protrusion),
    					$fn=NumSides);
    		color("Moccasin")
    		translate([0,0,(2*TaperLength + KnurlLen - Protrusion)])
    			cylinder(r=ShaftDia/2,h=(ShaftLength + Protrusion),$fn=NumSides);
    
    	}
    	translate([0,0,(2*TaperLength + KnurlLen + ShaftLength - SocketDepth + Protrusion)])
    		PolyCyl(SocketDia,(SocketDepth + Protrusion),6);
    }
    
  • Browning Hi-Power Magazine Capacity Reduction Block

    Based on tweaking the measurements from the nut trap block trial, this block attaches to the inner floor plate of the magazine and reduces the magazine’s capacity to 10 rounds:

    Browning Hi-Power Magazine Block - solid model - whole
    Browning Hi-Power Magazine Block – solid model – whole

    The horn fits between the follower’s pegs, so that chopping the pegs off won’t increase the magazine’s capacity. Chopping the horn off without modifying the follower won’t make any difference, either. As nearly as I can tell, chopping the pegs off the follower will destabilize it enough that it’ll roll over atop the spring, but I admit to not actually trying that.

    The yellow comb supports the overhang that captures the tab around the magazine spring and there’s a tiny support spider inside the lower nut clearance that holds the ceiling in place:

    Browning Hi-Power Magazine Block - solid model - section
    Browning Hi-Power Magazine Block – solid model – section

    The inner nut trap probably droops a bit without any support, but there’s no way to tell when it’s printed as one solid piece. That trap will hold the blob of steel-filled epoxy that secures the screw and helps prevent the block from turning, so it’s not really a nut trap and doesn’t require a precision fit. The vent tube from the top of the screw shaft gives the air and any excess epoxy an exit path.

    Here’s a bottom view of two blocks, showing the support structures and the results:

    Browning Hi-Power magazine - block support detail
    Browning Hi-Power magazine – block support detail

    I poked the tips of a snap ring pliers into the spider and twisted it out. The comb snaps off with fingernail pressure.

    You could also print it without support by laying it flat, then glue the halves together with alignment pins. This is a bottom view:

    Browning Hi-Power Magazine Block - solid model - split bottom
    Browning Hi-Power Magazine Block – solid model – split bottom

    The OpenSCAD program has a handful of configuration settings that determine which of those blocks it produces, which components appear, and how it’s oriented.

    Installed in a Browning magazine, the block looks like this:

    Browning Hi-Power magazine - block in place
    Browning Hi-Power magazine – block in place

    A detail of the bottom shows the notch capturing the spring tab:

    Browning Hi-Power magazine - block detail
    Browning Hi-Power magazine – block detail

    I think the top surface would benefit from a small bevel to ease the spring around the block, but that’s in the nature of fine tuning.

    Not having heard back from my legislators yet, I still don’t know whether this counts as a readily reversible modification. I have my doubts, what with it being plastic and all, but we shall see.

    The OpenSCAD source code:

    // Browning Hi-Power Magazine Block
    // Ed Nisley KE4ZNU December 2013
    
    Layout = "Whole";			// Show Whole Split
    							//  Show = section view for demo, not for building
    							//  Whole = upright for steel or plastic
    							//  Split = laid flat for plastic show-n-tell assembly
    
    AlignPins = (Layout == "Split");					// pins only for plastic show-n-tell
    
    Support = (Layout != "Split");						// no support for split
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    ThreadThick = 0.15;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    Angle = 12.5;				// from vertical
    
    SpringID = 10.3;			// magazine spring curvature (measure with drill shank)
    SpringRadius = SpringID / 2;
    
    Length = 24.0;				// front-to-back perpendicular to magazine shaft
    Height = 18.0;				// bottom-to-top, parallel to magazine shaft
    							//  18 = 10 round capacity
    
    RectLength = Length - SpringID;	// block length between end radii
    
    HornBaseOD = 8.0;			// fits between follower pegs to prevent shortening
    HornTipOD = 5.0;
    HornAddTip = (HornTipOD/2)*tan(Angle);
    HornAddBase = (HornBaseOD/2)*tan(Angle);
    HornAddLength = HornAddTip + HornAddBase + 2*Protrusion;
    HornLength = 12.0;			// should recompute ODs, but *eh*
    
    TrimHeight = 2.5;			// vertical clearance for spring clip on base plate
    							//   OEM = 2.5
    							//   generic A = 2.5
    
    TrimInset = 1.5;			// ... horizontal
    							//	 OEM = 0.0
    							//   generic A = 1.5
    
    ScrewOD = 3.0 - 0.25;		// screw hole dia - minimal thread engagement
    ScrewLength = 11.0;
    ScrewOffset = -1.5;			//   ... from centerline
    							//  OEM = 0.0
    							//  generic A = -1.5
    
    NutOD = 5.6;				// hex nut dia across flats
    NutThick = 2.4;				//  ... then add 50% to trap for thread engagement & epoxy
    NutOffset = 6.0;			//  ... base height from floor
    
    VentDia = 2.0;				// air vent from back of screw recess
    
    PinOD = 1.72;				// alignment pins
    PinLength = 6.0;
    PinInset = 0.6*SpringRadius;	// from outside edges
    echo(str("Alignment pin length: ",PinLength));
    
    NumSides = 8*4;				// default cylinder sides
    
    Offset = 5.0/2;				// from centerline for build layout
    
    //----------------------
    // Useful routines
    
    function Delta(a,l) = l*tan(a);				// incremental length due to angle
    
    // Locating pin hole with glue recess
    //  Default length is two pin diameters on each side of the split
    
    module LocatingPin(Dia=PinOD,Len=0.0) {
    
    	PinLen = (Len != 0.0) ? Len : (4*Dia);
    
    	translate([0,0,-ThreadThick])
    		PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4);
    
    	translate([0,0,-2*ThreadThick])
    		PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4);
    
    	translate([0,0,-(Len/2 + ThreadThick)])
    		PolyCyl(Dia,(Len + 2*ThreadThick),4);
    
    }
    
    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);
    
    }
    
    //----------------------
    // The magazine block
    
    module Block(SectionSelect = 0) {
    
    CropHeight = Height*cos(Angle);				// block height perpendicular to base
    echo(str("Perpendicular height: ",CropHeight));
    
    	difference() {
    		union() {
    			intersection() {
    				rotate([Angle,0,0])
    					hull() {
    						for (i=[-1,1])
    							translate([0,i*RectLength/2,-((Length/2)*sin(Angle) + Protrusion)])						cylinder(r=SpringRadius,
    										h=(Height + 2*(Length/2)*sin(Angle) + 2*Protrusion),
    										$fn=NumSides);
    					}
    				translate([0,0,CropHeight/2])
    					cube([2*SpringID,3*Length,CropHeight],center=true);
    			}
    			translate([0,-Height*sin(Angle),Height*cos(Angle)])
    				resize([SpringID,0,0])
    					intersection() {
    						rotate([Angle,0,0])
    							translate([0,0,-(HornAddBase + Protrusion)])
    								cylinder(r1=HornBaseOD/2,
    										r2=HornTipOD/2,
    										h=(HornLength + HornAddLength + Protrusion),
    										$fn=NumSides);
    					cube([2*SpringID,Length,2*(HornLength*cos(Angle) + Protrusion)],center=true);
    				}
    		}
    
    		translate([0,ScrewOffset,-Protrusion])		// screw
    			rotate(180/6)
    				PolyCyl(ScrewOD,(ScrewLength + Protrusion),6);
    
    		translate([0,ScrewOffset,NutOffset])		// nut trap in center
    			rotate(180/6)
    				PolyCyl(NutOD,1.5*NutThick,6);
    
    		translate([0,ScrewOffset,-Protrusion])		// nut clearance at base
    			rotate(180/6)
    				PolyCyl(NutOD,(1.1*NutThick + Protrusion),6);
    
    		translate([SpringID/2,-((Length/2)/cos(Angle) - TrimInset),-Protrusion])
    			rotate(180)
    				cube([SpringID,2*TrimInset,(TrimHeight + Protrusion)],center=false);
    
    		if (AlignPins)								// alignment pins
    			for (i=[-1,1])
    				rotate([Angle,0,0])
    				translate([0,
    							(i*((Length/2)*cos(Angle) - PinInset)),
    							(CropHeight/2 - i*2*PinInset)])
    					rotate([0,90,0]) rotate(45 - Angle)
    						LocatingPin(PinOD,PinLength);
    
    		translate([0,(ScrewOffset - NutOD),-Protrusion])	// air vent
    			rotate(180/8)
    				PolyCyl(VentDia,(ScrewLength + Protrusion),8);
    		translate([0,(ScrewOffset + VentDia/2),ScrewLength])
    			rotate([90,0,0]) rotate(180/8)
    				PolyCyl(VentDia,(NutOD + VentDia),8);
    
    		if (SectionSelect == 1)
    			translate([0*SpringID,-2*Length,-Protrusion])
    				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
    		else if (SectionSelect == -1)
    			translate([-2*SpringID,-2*Length,-Protrusion])
    				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
    
    	}
    
    	NumBars = floor((SpringID/2)/(5*ThreadWidth));
    
    	if (Support) {									// add support structures
    		for (i = [-NumBars:NumBars])
    			translate([i*5*ThreadWidth,
    					   -((Length/2)/cos(Angle) + TrimInset/2 + ThreadWidth),
    					   (TrimHeight - ThreadThick)/2])
    				color("Yellow")
    				cube([(2*ThreadWidth),(3*TrimInset),(TrimHeight - ThreadThick)],center=true);
    
    		translate([-SpringID/2,-((Length/2)/cos(Angle) + 2*TrimInset + ThreadWidth),0])
    			color("Yellow")
    			cube([SpringID,(2*ThreadWidth),(TrimHeight - ThreadThick)],center=false);
    
    		translate([0,ScrewOffset,0])
    			for (j=[0:5]) {
    			rotate(30 + 360*j/6)
    				translate([(NutOD/2 - ThreadWidth)/2,0,(1.1*NutThick - ThreadThick)/2])
    					color("Yellow")
    					cube([(NutOD/2 - ThreadWidth),
    						  (2*ThreadWidth),
    						  (1.1*NutThick - ThreadThick)],
    						  center=true);
            }
    	}
    
    }
    
    //-------------------
    // Build it...
    
    ShowPegGrid();
    
    if (Layout == "Show")
    	Block(1);
    
    if (Layout == "Whole")
    	Block(0);
    
    if (Layout ==  "Split") {
    	translate([(Offset + Length/2),Height/2,0])
    		rotate(90) rotate([0,-90,-Angle])
    			Block(-1);
    	translate([-(Offset + Length/2),Height/2,0])
    		rotate(-90) rotate([0,90,Angle])
    			Block(1);
    }