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.

Author: Ed

  • Thing-O-Matic: Wade/ScribbleJ Filament Tensioner

    Putting a geared stepper motor on the MK5 filament drive produced greatly improved print quality, which meant I could finally print ScribbleJ’s version of the classic Wade Filament Tensioner and expect that it’d come out right. It’s a rather large lump of plastic that printed quite nicely on an aluminum plate.

    Wade-ScribbleJ Filament Tensioner on plate
    Wade-ScribbleJ Filament Tensioner on plate

    The whole thing looks like this when it’s all assembled and adjusted:

    Complete Geared Stepper Extruder
    Complete Geared Stepper Extruder

    [Update: The motor comes directly from the usual eBay supplier. You won’t find another one like it, but this is directly from the label…

    • 38 mm case
    • Minebea-Matsushita 17PM-K150-P1V
    • No. T6824-02

    ]

    You can see the two thermal switches near the bottom of the picture. The 100 °C switch is inside the acrylic frame on the Thermal Riser, the 40 °C switch is just taped to the filament tensioner’s base. The former is OK, the latter isn’t as secure as it should be. FWIW, Riser temperatures run around 70 °C during normal extrusion, albeit in a chilly Basement Laboratory without covers on the TOM’s windows.

    A better view of the shaft bearings and filament position:

    Filament tensioner - overview
    Filament tensioner – overview

    The four long screws are 1.5 inch 4-40 from my heap, although 2 inch screws would give more room for adjustment. Some folks mount the screws the other way around, with the nuts pressing on the springs and little knobs on the nuts. I gave up on the washers to get a bit more adjustment range.

    The springs came from my Big Box o’ Little Springs, sporting absolutely no pedigree at all. They’re not quite completely compressed, so there’s a bit of push left in them to handle filament diameter variations (which is the whole point of this exercise). I added four nuts (between the shaft bearing plate and the idler block) to keep the idler block from resting against the drive gear when there’s no filament in place: inserting a new filament became much easier.

    Somewhat to my surprise, the entire filament drive gear assembly is free-floating and self-aligning within the housing:

    Filament drive gear detail
    Filament drive gear detail

    I enabled the option to put a 5 mm diameter cleanout hole in the bearing housing, which turned out to be absolutely essential for monitoring the location of the drive gear inside all the machinery. You can barely see the hole in the first picture, on the left side of the curved section.

    A floating shaft means the 7-tooth motor drive gear’s position must line up with wherever the 51-tooth filament drive gear happens to be. There’s not much room to adjust the motor gear, but a few iterations sorted out the proper number and placement of all the filament drive shaft washers, nuts, and bearings.

    Filament drive gear - shaft spacing
    Filament drive gear – shaft spacing

    You (well, I) really really must put a flat on the shaft and use full-strength Loctite to hold the setscrew in place. I used an all-thread M6x40 bolt because that’s what I had on hand, but a partially threaded M6x50 bolt would provide better support for the bearings, more clearance for the spacers, and look a lot better; it’d require a custom-turned bushing instead of the nut against the big gear, though.

    Flatted filament drive shaft
    Flatted filament drive shaft

    I initially used low-strength Loctite. Word: a loose drive gear setscrew can convince you that Skeinforge’s Reversal plugin isn’t working after you make many changes with worsening results. Those fast reversals loosen the setscrew in short order.

    The diameters of the 7- and 51-tooth herringbone gears determine the center-to-center distance between the motor shaft and the extruder shaft. Alas, two of the motor mounting bolts wind up directly behind the larger gear. I marked the gear adjacent to the bolt heads and drilled a hole that just barely admits the hex wrench:

    Stepper Extruder - motor mount access hole
    Stepper Extruder – motor mount access hole

    If you knew where that hole was supposed to be, you could print it right into the gear, but I haven’t a clue as to how you might algorithmically determine the precise location on the as-printed gears.

    The modified OpenSCAD source produces two recesses for the bolt head and nut, but I just applied an end mill to the head side of the finished idler block. There’s no room for the bolt head between the block and the motor mounting plate.

    Idler housing with recessed bolt
    Idler housing with recessed bolt

    Of course, I modified the OpenSCAD code along the way:

    • Changing the bearing size moved the base: use front_bearing_r in the routine that punches the holes
    • Add a complete outer surface on the idler block; I thought I might want a flat metal plate to distribute the stress.
    • Add bolt head / nut recesses for idler block pulley shaft
    • Include base_wall_h in the calculation for idler_max_h
    • Tweaked spacing to get idler bolt heads out of the walls
    • Extend motor wall rightward to cover all of the base plate
    • Adjust base hole positioning: -10 / +4.0, not -10 / +3.5
      • But not all instances of 3.5 must change, I think
    • Filament offset may need further tweakage
    • Other miscellaneous tweaks

    Not all of those changes made it to the printed object shown here; if I ever print another one, they’ll be included. Use at your own risk!

    The OpenSCAD source, which is almost entirely ScribbleJ’s work:

    //  MK5 Wade's-Style Tensioner
    // (C)2011, Christopher "ScribbleJ" Jansen
    //
    // Released under the BSD license.
    
    // Modifications: Ed Nisley - KE4ZNU - Mar 2011
    
    // Parametric Settings
    
    // INTERESTING OPTIONS
    // 1 = on, 0 = off
    extend_shaft = 1 ;		// 0 will allow a bridge over the front of the motor hole
    make_stepper_holes = 1;	// 1 will create mounting holes for a stepper mount.
    make_dc_holes = 0;		// 1 will create mounting holes for a MK5 DC motor.
    motor_shaft_supports = 1;	// 1 will create angle supports to the motor shaft. (See options below for support angle/size)
    generate_for_viewing  = 0;	// 1 creates the model suitable for viewing.  0 creates the model suitable for printing.
    
    cleaning_hole_d = 5;		// The diameter of a cleaning hole to punch.  (0 = no cleaning hole)
    cleaning_hole_r = cleaning_hole_d/2;
    cleaning_hole_angle = 75;	// Angle offset from 9 o'clock position (i.e. directly left)
    
    hole_protrusion = 0.05;	// surface clearance for holes and suchlike
    
    hole_windage = 0.4;			// allowance for small hole shrinkage
    
    // _r = radius, _d = diameter, _h = height
    
    // REAR BEARING = bearing closest to motor
    rear_bearing_d = 17.0 + hole_windage;
    rear_bearing_r = rear_bearing_d/2;
    rear_bearing_h = 6.0;
    
    // EXTRA SHAFT = include an extra length of motor shaft.  This is useful
    // for giving your idler bolts enough room depth-wise.
    extra_shaft = 5;
    
    // FRONT BEARING = bearing furthest from motor  (Technically, front bearing diameter must be >= than rear to print properly... so
    // there are many places in the code that we assume the front bearing is the largest d.
    front_bearing_d = 17.0 + hole_windage;
    front_bearing_r  = front_bearing_d/2;
    front_bearing_h = 6;
    
    // EXTRA FILAMENT = extend the length of the filament column.  This is useful
    // for giving your idler bolts enough room height-wise.
    extra_filament = 40;
    
    filament_margin = 3.0;	// How wide is shaft on either side of filament?
    filament_d = 4;			// How wide is filament shaft hole?
    filament_r = filament_d/2;
    
    // FILAMENT OFFSET = How far from the center of the motor axis your MK5 plastic pusher gear thingy
    // or hobbed bolt groove is.
    filament_offset = 6;
    
    // MOTOR WALL = "rear" wall of tensioner.
    motor_wall_h = 5;  		// thickness of wall
    motor_bolt_d = 3.0 + hole_windage;		// diameter of motor mounting bolts
    motor_bolt_r  = motor_bolt_d/2;
    motor_bolt_h = motor_wall_h;
    motor_bolt_hex_d = 6.3 + hole_windage;	// diameter of motor mounting bolt hex caps
    motor_bolt_hex_r  = motor_bolt_hex_d/2;
    motor_bolt_hex_h = 3;			// height of hex caps
    motor_dropbolts   = 2.0;		// distance to sink bolts into wall
    motor_boltmargin = 5;			// Distance to allow between bolts and edges of wall.
    motor_shaft_width = 5;			// How thick is the wall around the motor shaft?
    motor_shaft_support_width = 10;	// How thick are the motor supports (if any)?
    motor_shaft_support_angle = 0;	// if non-0, will create three supports spaced apart this many degrees.
    								// 0 creates a single support if enabled above.
    
    // 31 is distance from center to bolt holes... do not chang this without changing hardcoded numbers
    // in the motor bolt generating module.
    motor_wall_w = 31 + (motor_boltmargin * 2) + motor_bolt_d;
    motor_wall_d = 31 + (motor_boltmargin * 2) + motor_bolt_d;
    
    // IDLER BEARING = the bearing that holds the plastic against the hobbed bolt/MK5 plastic pusher.
    // You should include about an extra 2mm over your actual bearing measurements here so it can spin freely.
    idler_bearing_d = 19 + 3;
    idler_bearing_r  = idler_bearing_d/2;
    idler_bearing_h = 6 + 2;
    h_i = idler_bearing_h/2;
    idler_bearing_bolt_d = 5;	// This is the size of the bolt holding the bearing in place.
    idler_bearing_bolt_r = idler_bearing_bolt_d/2;
    
    idler_bolt_d = 3.0 + hole_windage;		// This is the size of the (4) bolts holding the idler block in place.
    idler_bolt_r  = idler_bolt_d/2;
    idler_bolt_margin = 4;	// How much room to allow between bolt holes and edge of block.
    idler_bolt_hex_d = 7.0 + hole_windage;
    idler_bolt_hex_r  = idler_bolt_hex_d/2;
    idler_bolt_hex_h = 5;         // Arbitrarily large to be sure to punch through supports/shaft.
    idler_dropbolts   = 2.0;           // Try negative numbers here to catch your nuts on the supports.
    
    idler_wall = 2.5;			// thickness of wall to left of idler bearing
    
    idler_recess_dia = 9.0 + hole_windage;		// recess idler shaft bolt head & nut
    idler_recess_r = idler_recess_dia/2;
    idler_recess_depth = 3.0;
    
    // BASE = the bottom part that bolts onto the hot end.
    base_wall_h = 6;		// How thick or tall is the base.
    base_h=base_wall_h;
    base_bolt_d  = 3.0 + hole_windage;		// Size of bolts used to hold base -- rest of base settings are same as motor wall settings above.
    base_bolt_r   = base_bolt_d/2;
    base_bolt_hex_d = 6.3 + hole_windage;
    base_bolt_hex_r  = base_bolt_hex_d/2;
    base_bolt_hex_h = 5;
    base_dropbolts = 1.5;
    base_boltmargin = 6;
    base_filament_offset_x = 6;
    
    // 30/14.0 is distance from center to bolt holes...
    // do not change this without changing hardcoded numbers
    //  in the base bolt generating module.
    base_w = 30 + (base_boltmargin*2) + base_bolt_r;
    base_d  = 14.0 + (base_boltmargin*2) + base_bolt_r;
    
    // base_z_extra is used for configurations where base wall or motor wall
    //  would be unprintable due to differential in height.
    // shrinks or grows the base to fit.
    base_z_extra = ((idler_bearing_h/2) + rear_bearing_h + motor_wall_h) - ((base_d/2) + 3.5);
    base_filament_offset_z = -3.5;  // How far the filament hole is from the bolts furthest from the motor.
    base_d_use = base_d + base_z_extra;
    
    // make up difference between bottom of wall and base... not really necessary but more support more better.
    motor_wall_extra = front_bearing_r + (extra_filament/2) + base_h - (motor_wall_d/2);
    
    // Calculate maximum space for idler block.
    idler_max_h = idler_bearing_d + extra_filament + ((-base_bolt_hex_h+base_dropbolts)*2) - base_wall_h;
    half_idler_max_h = idler_max_h/2;
    idler_bolt_y = half_idler_max_h - idler_bolt_r - idler_bolt_margin;
    
    //idler_max_w = .55 * idler_bearing_d;
    idler_max_w = idler_bearing_r + idler_wall;			// enforce wall thickness on right side
    
    idler_max_d = extra_shaft+front_bearing_h+idler_bearing_h+rear_bearing_h+((-motor_bolt_hex_h+motor_dropbolts)*2);
    half_idler_max_d = idler_max_d/2;
    idler_bolt_z = half_idler_max_d - idler_bolt_r - idler_bolt_margin;
    
    echo(str("Idler block size: ",idler_max_d," x ",idler_max_h," x ",idler_max_w));
    echo(str("Idler bolt spacing: ",2*idler_bolt_z," x ",2*idler_bolt_y));
    
    // This module generates the bolt pattern for the idler, trying to fill the maximum space available.
    module IDLERBOLTS()
    {
    	// echo(idler_bolt_r, idler_bolt_d, idler_max_d, idler_bolt_z, half_idler_max_d);
    	// echo(idler_bolt_r, idler_bolt_d, idler_max_h, idler_bolt_y, half_idler_max_h);
    
    	translate([idler_bolt_z,idler_bolt_y,0]) cylinder(r=idler_bolt_r, h=40);
    	translate([idler_bolt_z,-idler_bolt_y,0]) cylinder(r=idler_bolt_r, h=40);
    	translate([-idler_bolt_z,idler_bolt_y,0]) cylinder(r=idler_bolt_r, h=40);
    	translate([-idler_bolt_z,-idler_bolt_y,0]) cylinder(r=idler_bolt_r, h=40);
    
    	translate([0,0,-idler_bolt_hex_h])
    	{
    		translate([idler_bolt_z,idler_bolt_y,0]) cylinder(r=idler_bolt_hex_r, h=idler_bolt_hex_h,$fn=6);
    		translate([idler_bolt_z,-idler_bolt_y,0]) cylinder(r=idler_bolt_hex_r, h=idler_bolt_hex_h,$fn=6);
    		translate([-idler_bolt_z,idler_bolt_y,0]) cylinder(r=idler_bolt_hex_r, h=idler_bolt_hex_h,$fn=6);
    		translate([-idler_bolt_z,-idler_bolt_y,0]) cylinder(r=idler_bolt_hex_r, h=idler_bolt_hex_h,$fn=6);
    	}
    
    }
    
    // This module generates an idler block, filling the maximum space available.
    module IDLER()
    {
    	difference()
    	{
    		translate([0,0,(idler_max_w/2)-(.25 * idler_bearing_bolt_d)]) cube([idler_max_d, idler_max_h, idler_max_w], center=true);
    		translate([0,0,(-.5 * idler_bearing_bolt_r) + idler_max_w]) rotate([0,180,0]) IDLERBOLTS();
    		#rotate([0,90,0]) cylinder(h=idler_max_d+1, r=idler_bearing_bolt_r,center=true);
    		#rotate([0,90,0]) cylinder(h=idler_bearing_h, r=idler_bearing_r, center=true);
    		translate([(idler_max_d/2 - idler_recess_depth),0,0])
    		  rotate([0,90,0])
    		  #cylinder(r=idler_recess_r,h=(idler_recess_depth + hole_protrusion),$fn=10);
    		translate([(-idler_max_d/2 + idler_recess_depth),0,0])
    		  rotate([0,270,0])
    		  #cylinder(r=idler_recess_r,h=(idler_recess_depth + hole_protrusion),$fn=10);
    	}
    	echo(str("IDLER BEARING BOLT LENGTH REQUIRED (longer is OK): ", idler_max_d, "mm"));
    }
    
    // This module creates the motor shaft hole pattern.
    module MOTORSHAFT()
    {
    	// idler bearing
    	cylinder(h=idler_bearing_h, r=rear_bearing_r, center=true);
    	// front bearing
    	translate([0,0,h_i]) cylinder(h=front_bearing_h, r=front_bearing_r);
    	if(extend_shaft == 1)
    	{
    		translate([0,0,h_i]) cylinder(h=front_bearing_h+(extra_shaft/2)+50, r=front_bearing_r);
    	}
    	// rear bearing
    	translate([0,0,0 - h_i - rear_bearing_h - (extra_shaft/2) - motor_wall_h])
    		cylinder(h=         rear_bearing_h + (extra_shaft/2) + motor_wall_h, r=rear_bearing_r);
    
    	echo(str("MOTOR SHAFT/BOLT LENGTH REQUIRED (longer is OK): ", front_bearing_h+idler_bearing_h+rear_bearing_h+motor_wall_h+(extra_shaft/2), "mm"));
    	echo(str("MOTOR SHAFT LENGTH FROM REAR OF MOUNT TO FILAMENT:", motor_wall_h+(extra_shaft/2)+rear_bearing_h+(idler_bearing_h/2), "mm"));
    }
    
    // This module creates an MK5 mount motor hole pattern with optional hex insets for bolt heads/nuts.
    module MK5_MOTORHOLES(include_dc = 1, include_stepper = 1, include_hex = 1)
    {
    	// The hardcoded numbers in the module below are simply the coordinates of the motor holes,
    	// relative to the center of the motor shaft.
    
    	// STEPPER MOUNT HOLES
    	if(include_stepper == 1)
    	{
    		translate([15.5,15.5,-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_r, h=motor_bolt_h+(2*hole_protrusion));
    		translate([15.5,-15.5,-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_r, h=motor_bolt_h+(2*hole_protrusion));
    		translate([-15.5,-15.5,-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_r, h=motor_bolt_h+(2*hole_protrusion));
    		translate([-15.5,15.5,-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_r, h=motor_bolt_h+(2*hole_protrusion));
    
    		if(include_hex == 1)
    		{
    			translate([15.5,15.5,motor_bolt_h-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_hex_r, h=motor_bolt_hex_h + (2*hole_protrusion), $fn=6);
    			translate([15.5,-15.5,motor_bolt_h-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_hex_r, h=motor_bolt_hex_h + (2*hole_protrusion), $fn=6);
    			translate([-15.5,-15.5,motor_bolt_h-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_hex_r, h=motor_bolt_hex_h + (2*hole_protrusion), $fn=6);
    			translate([-15.5,15.5,motor_bolt_h-motor_dropbolts-hole_protrusion])	cylinder(r=motor_bolt_hex_r, h=motor_bolt_hex_h + (2*hole_protrusion), $fn=6);
    		}
    	}
    
    	// DC MOUNT HOLES
    	if(include_dc == 1)
    	{
    		// DC MOUNT HOLES
    		translate([0,-15.5,-motor_dropbolts])					cylinder(r=motor_bolt_r,h=motor_bolt_h);
    		rotate([0,0,-60])  translate([0,-15.5,-motor_dropbolts]) 		cylinder(r=motor_bolt_r,h=motor_bolt_h);
    		rotate([0,0,-120]) translate([0,-15.5,-motor_dropbolts])	cylinder(r=motor_bolt_r,h=motor_bolt_h);
    		rotate([0,0,-180]) translate([0,-15.5,-motor_dropbolts])	cylinder(r=motor_bolt_r,h=motor_bolt_h);
    		if(include_hex == 1)
    		{
    			translate([0,-15.5,motor_bolt_h-motor_dropbolts])					cylinder(r=motor_bolt_hex_r,h=motor_bolt_hex_h, $fn=6);
    			rotate([0,0,-60])  translate([0,-15.5,motor_bolt_h-motor_dropbolts]) 	cylinder(r=motor_bolt_hex_r,h=motor_bolt_hex_h, $fn=6);
    			rotate([0,0,-120]) translate([0,-15.5,motor_bolt_h-motor_dropbolts])	cylinder(r=motor_bolt_hex_r,h=motor_bolt_hex_h, $fn=6);
    			rotate([0,0,-180]) translate([0,-15.5,motor_bolt_h-motor_dropbolts])	cylinder(r=motor_bolt_hex_r,h=motor_bolt_hex_h, $fn=6);
    		}
    	}
    }
    
    // This module creates MK5 hot-end hole patterns with optional hex heads for the bolts/nuts.
    module MK5_BASEHOLES(include_MK5boltheads = 1, include_filament = 1, include_hex = 1)
    {
    
    	// The hardcoded numbers in the routine below are simply the coordinates of the base holes,
    	// relative to the filament hole.
    	if(include_filament == 1)
    	{
    		translate([0,0,100/4]) cylinder(r=filament_r, h=100,center=true);
    	}
    
    	translate([-15,4.0,-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_r,h=base_h+(2*hole_protrusion));
    	translate([-15,-10,-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_r,h=base_h+(2*hole_protrusion));
    	translate([15,4.0,-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_r,h=base_h+(2*hole_protrusion));
    	translate([15,-10,-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_r,h=base_h+(2*hole_protrusion));
    
    	if(include_MK5boltheads == 1)
    	{
    		translate([17,9.5,-hole_protrusion]) cylinder(r=3, h=4);
    		translate([17,-15.5,-hole_protrusion]) cylinder(r=3, h=4);
    		translate([-17,9.5,-hole_protrusion]) cylinder(r=3, h=4);
    		translate([-17,-15.5,-hole_protrusion]) cylinder(r=3, h=4);
    	}
    
    	if(include_hex == 1)
    	{
    		translate([-15,4.0,base_h-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_hex_r,h=base_bolt_hex_h+(2*hole_protrusion), $fn=6);
    		translate([-15,-10,base_h-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_hex_r,h=base_bolt_hex_h+(2*hole_protrusion), $fn=6);
    		translate([15,4.0,base_h-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_hex_r,h=base_bolt_hex_h+(2*hole_protrusion), $fn=6);
    		translate([15,-10,base_h-base_dropbolts-hole_protrusion]) cylinder(r=base_bolt_hex_r,h=base_bolt_hex_h+(2*hole_protrusion), $fn=6);
    	}
    
    }
    
    // This module generates the mounting part of the Wade's-style tensioner.
    // The generated item is centered on the motor shaft in X,Y and the filament in Z.
    module MOUNT()
    {
    
    	difference()
    	{
    
    	union()
    	{
    		translate([0,0,-1 * (motor_wall_h/2)])
    		{  // MOTOR SHAFT RELATIVE TO FILAMENT IN Z, MOTOR SHAFT IN X,Y
    			cylinder(h=front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft + motor_wall_h, r=front_bearing_r+motor_shaft_width, center=true);
    			translate([base_filament_offset_x,0,0]) cube([filament_d + (2 * filament_margin),   front_bearing_d + extra_filament,  front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft + motor_wall_h], center=true);
    
    			if(motor_shaft_supports == 1)
    			{
    				translate([0,0,(motor_wall_h/2)]) {
    				intersection()
    				{
    					cylinder(	h=front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft,  r1=(motor_wall_w/2),  r2=front_bearing_r+motor_shaft_width, center=true);
    					rotate([0,0,90]) translate([0,50,0]) cube([motor_shaft_support_width, 100, front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft], center=true);
    				}
    				intersection()
    				{
    					cylinder(	h=front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft,  r1=(motor_wall_w/2),  r2=front_bearing_r+motor_shaft_width, center=true);
    					rotate([0,0,90-motor_shaft_support_angle]) translate([0,50,0]) cube([motor_shaft_support_width, 100, front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft], center=true);
    				}
    				intersection()
    				{
    					cylinder(	h=front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft,  r1=(motor_wall_w/2),  r2=front_bearing_r+motor_shaft_width, center=true);
    					rotate([0,0,90+motor_shaft_support_angle]) translate([0,50,0]) cube([motor_shaft_support_width, 100, front_bearing_h + idler_bearing_h + rear_bearing_h + extra_shaft], center=true);
    				}
    				}
    			}
    		}
    
    		translate([0,motor_wall_extra/-2,((motor_wall_h/-2) + rear_bearing_h+(extra_shaft/2)+motor_wall_h+(idler_bearing_h/2)) * -1])
    		{ // MOTOR WALL RELATIVE TO MOTOR SHAFT X,Y
    			  union() {
    				cube([motor_wall_w, motor_wall_d+motor_wall_extra, motor_wall_h], center=true);
    				translate([base_filament_offset_x - (motor_wall_w - base_w)/2 + motor_wall_w/4,0,0])
    				  cube([motor_wall_w/2,motor_wall_d+motor_wall_extra, motor_wall_h],center=true);
    			  }
    		}
    
    		translate([filament_offset, -1* (front_bearing_r + (extra_filament/2) + (base_h/2)), base_filament_offset_z])
    		{  // BASE RELATIVE TO FILAMENT HOLE IN Z,X
    			translate([0,0,-(base_z_extra/2)])
    			  cube([base_w, base_h, base_d_use], center=true);
    			// extend filament shaft
    			translate([0,(front_bearing_d + extra_filament + base_h)/2, 0])
    			  cube([filament_d + (2 * filament_margin), front_bearing_d + extra_filament, base_d], center=true);
    		}
    
    	}
    
    	// Punch motor holes
    	translate([0,0,-1*((idler_bearing_h/2) + (rear_bearing_h) + (motor_wall_h) + (extra_shaft/2))])
    	  # MK5_MOTORHOLES(include_stepper=make_stepper_holes, include_dc = make_dc_holes);
    	// Punch motor shaft
    	MOTORSHAFT();
    	// Punch idler bolt holes
    	translate([filament_offset - filament_margin - (filament_r) + idler_dropbolts,0,0])
    	  rotate([90,90,90]) # IDLERBOLTS();
    	// Punch baseplate holes
    	translate([filament_offset, -1* (front_bearing_r + (extra_filament/2) + base_h), 0])
    	  rotate([-90,180,0]) # MK5_BASEHOLES();
    	// Punch idler bearing clearance
    	translate([filament_offset + idler_bearing_r - filament_r, 0,0])
    	  cylinder(h=idler_bearing_h + front_bearing_h + rear_bearing_h + extra_shaft + hole_protrusion, r=idler_bearing_r, center=true);
    	// Punch cleaning hole
    	rotate([-1 * cleaning_hole_angle,-90,0])
    	  cylinder(h=50,r=cleaning_hole_r);
    
    	}
    }
    
    if(generate_for_viewing == 1)
    {
    	MOUNT();
    	translate([filament_offset + idler_bearing_r,0,0]) rotate([0,90,0]) IDLER();
    }
    else if(generate_for_viewing == 0)
    {
    	translate([15.5+motor_boltmargin+motor_bolt_r+2.5,    0,   (motor_wall_h+(extra_shaft/2)+rear_bearing_h+(idler_bearing_h/2))]) MOUNT();
    	translate([(idler_max_d/-2)-2.5, 0, (-.5 * idler_bearing_bolt_r) + idler_max_w]) rotate([180,0,0]) IDLER();
    }
    
    

    Just. Do. It.

  • Thing-O-Matic: Manual Wipe and Splodge

    The first step of a good print requires nailing the extrusion to the build platform. The Skeinforge Splodge plugin seems to thicken the first part of each filament on the first layer, which is not helpful. So I turned that off and added a few lines to start.gcode that do a much better job.

    I also disabled the Wipe plugin, because you really can’t wipe the nozzle after the first few layers without having some part of the Z stage clobber the object. Rather than enable Wipe for just the first layer, I put a manual wipe in start.gcode, too.

    The relevant sections look like this; they fit after the homing sequence at the end of the file:

    (--- manual wipe ---)
    G0 X54 Y-57.0 Z15	(move above wipe start)
    G0 Z8   			(down to wipe level)
    M6 T0				(wait for temperature settling)
    M101				(Extruder on, forward)
    G4 P4000			(take up slack, get pressure)
    M103				(Extruder off)
    G4 P4000			(Wait for filament to stop oozing)
    G0 Y-40				(wipe nozzle)
    (--- manual splodge)
    G0 X-50 Y-55		(to front left corner)
    G1 Z0.50			(just over surface)
    M108 R2.0			(set stepper extruder speed)
    M101				(start extruder)
    G4 P2000			(build up a turd)
    

    Depending on a myriad imponderable factors, the manual wipe sequence flips off either a huge tangle or a tiny strand. That’s why I used a 4 second delay: it’s long enough to leave the extruder pressure in a consistent state no matter how it starts.

    The manual splodge location depends on your platform layout; I’m thinking of putting it entirely outside the build area. It must be somewhere near the front left corner, because Skeinforge starts each new layer from that direction. Two seconds of extrusion at 2 rev/min forms a blob with a generous contact patch, although the nozzle must plow through the side on its way out.

    Note that I leave the extruder running at the end of start.gcode, which means that it’s printing all the way to the outline. That won’t interfere with any part of the object, because (by definition) the first layer of the object lies entirely within the outline.

    The Outline plugin puts a single filament around the entire object, allowing me to measure the actual nozzle height and extrusion width on the first layer. More on that later.

    The final result looks like this:

    Manual Splodge with Companion Cube
    Manual Splodge with Companion Cube

    Notice that the splodge turd isn’t firmly glued to the platform, but the thread leading to the outline sticks like it was glued and the outline comes out perfectly formed. That’s the whole idea in a nutshell: paste the thread down from a stationary nozzle, then start moving with the turd acting as an anchor.

    Trying to start pasting the filament with the nozzle moving doesn’t work well, as witness the left edge of the outline around these test pieces:

    ABS coating on aluminum build plate
    ABS coating on aluminum build plate

    Admittedly, that was with a DC extruder, but the same principle applies to stepper extruders.

  • MK5 Extruder: Thermal Riser Temperatures – Operating

    Thermal Switches in place
    Thermal Switches in place

    My Parts Heap disgorged a somewhat larger TO-5 heatsink (a Thermalloy 228B, which they no longer make) with three fins and a collar having enough spring to fit tightly around the Thermal Riser Tube. It was intended for transistors on PCBs with horizontal air flow, but I hoped it would be more effective than the smaller heatsink that comes stock with the TOM.

    There’s certainly some air flow through the heatsink at the top of the arches, but I have no way of measuring that. The picture there shows another, much flatter, heatsink that I’d been using to cool the Thermal Riser after I found out how hot it was getting near the top.

    This heatsink didn’t get a thermocouple mount epoxied to it and, given my experience with the first set of measurements, I didn’t bother stuffing a thermocouple between the fins.

    The Thermal Switch Block now has a 100 °C NC Thermal Switch epoxied to it and, barely visible to the lower right, a 40 °C NO Switch is taped to the Z stage in the corner of the acrylic support base. The switch cable looks like this:

    Themal Switches - prepped and mounted
    Themal Switches – prepped and mounted

    With the meter’s T1 thermocouple bead behind the 40 °C switch and T2 tucked into the Thermal Switch Block, the results look thusly:

    Thermal Riser and Z stage Temperature Graph - block top
    Thermal Riser and Z stage Temperature Graph – block top

    The core went to 220 °C this time, with the ABP at 120 °C, and I started extruding at 20 minutes when the temperature had stabilized. The Switch Block temperature promptly dropped 6 °C as room-temperature filament entered the top of the Thermal Riser Tube at 2 rev/min × 10 cm drive dia × π = 63 mm/min ≈ 1 mm/sec.

    The previous test showed that the Thermal Switch Block stabilized at 90 °C and I think this one will be about the same, despite the larger heatsink, although the while-extruding temperature hovers around 70 °C. That’s better than 90 °C, so I’ll keep monitoring it and see how it plays in warmer weather inside a cozy build chamber. Obviously, having the Extruder ram cool filament into the Thermal Core holds the temperature down.

    Given those numbers, a 110 to 120 °C NC switch would be better; I’m sure one will eventually appear in my usual surplus sources. With a 30 °C margin and an assumed rise of 7 °C per 25 °C Thermal Core increase, the switch will trip when the Core passes 225 + (4 × 25) = 325 °C. That’s rather toasty, but the alternative seems to be having a switch that kicks out on a hot day.

    As expected, the Z stage temperature passed 40 °C at 10 minutes and the (yellow) Low Overtemperature LED blinked on. I wasn’t too surprised at that; the previous test had a cold ABP. I’ll move that switch to the top of the acrylic arch, taped against the base of the Filament Drive frame where it can measure the effect of the Thermal Riser on the plastic base. That picture shows the potential for high temperatures at that spot.

    The original data:

    Thermal Riser and Z stage Temperatures - block at top
    Thermal Riser and Z stage Temperatures – block at top
  • MK5 Extruder: Thermal Riser Temperatures 2

    Switch block - top
    Switch block – top

    Using pretty much the same setup as before, I put the Thermal Switch Block at the top of the MK5 Thermal Riser Tube and the little heatsink at the bottom. The heatsink sat between the bolt head and left just enough room that I could snake the thermocouple bead into the brass tube, so these temperatures should be much more representative of the actual Thermal Riser.

    After getting everything stuck together, I discovered that I’d interchanged the thermocouple leads. Rather than fixing that, take note that the T1 and T2 datasets represent different objects, but the same physical position: T1 on the bottom, T2 on the top.

    I skipped the staged warmup, cried “Fire the Thing-O-Matic!” and ran it to 225 °C while recording temperatures every 5 minutes along the way. The graph looks like this:

    Thermal Riser Tube Temperature Graph - block on top
    Thermal Riser Tube Temperature Graph – block on top

    They’re not quite exponentials, because the Core temperature gets flattened at the top, but they’re still pretty.

    The top-to-bottom temperature differential has increased to 35 °C, although the top temperature still hits 90 °C. I think there are countervailing forces at work:

    • The thermocouple is in better contact with the Heatsink: the bottom of the tube really is hotter with the Heatsink at that end.
    • The Thermal Block gives a better measure of the top-of-Tube temperature, because that thermocouple is intimately connected to the Block. The Tube top is about the same temperature, but the previous Heatsink temperatures were lower.

    In short, I trust these readings a bit more than the previous ones.

    But, as before, the Switch Block is still too hot for a 100 °C Thermal Switch. The next step is to add a somewhat larger heatsink from my Parts Heap and see what happens.

    The original data:

    Thermal Riser Temperatures - block at top
    Thermal Riser Temperatures – block at top
  • MK5 Extruder: Thermal Riser Temperatures 1

    Switch block - bottom
    Switch block – bottom

    The general idea: measure the Thermal Riser Tube temperatures, so as to figure out where to put the Thermal Cutout Switch that will kill the Thing-O-Matic if the cartridge heater drive circuitry gets stuck on. Ideally, there will be a location suitable for the 100 °C NC switch I have on hand, but you’d use the same technique to make sure any switch would work.

    So, to begin.

    With the Thermal Switch Block just over the bolt heads and the small heatsink just under the acrylic sheet at the top, a pair of thermocouples attached to my old Fluke 52 meter reported temperatures.

    The top thermocouple (T2 data) touches, ever so gently, the small heatsink, so it’s reporting mostly heatsink temperature and bit of the surrounding air. It moved slightly after the first measurement, despite the masking tape visible in the upper right corner of the picture.

    The bottom thermocouple (T1 data) is tucked into the small hole in the Switch Block, so it’s reporting the real block temperature that the Thermal Switch will eventually experience.

    A third thermocouple is taped in the corner of the Z axis stage against the acrylic arch, directly beside a cartridge heater inside the insulation wrap. During these proceedings that temperature rose from 25 °C ambient (due, most likely, to hand warmth while positioning all this stuff) to about 35 °C.

    And, of course, the standard thermocouple on the MK5 Core reports the actual temperature inside the insulation wrap.

    I raised the MK5 temperature in 50 °C steps, then 25 °C to 225 °C, waiting until the Block temperature more-or-less stabilized, while recording temperatures every 5 minutes. On this time scale, the Thermal Core temperature stabilized over the course of a single measurement.

    With at that in mind, the results look like this:

    Thermal Riser Tube Temperature Graph - block on bottom
    Thermal Riser Tube Temperature Graph – block on bottom

    At normal extruding temperatures above 200 °C, the red trace shows the Switch Block running about 15°C above green Heatsink trace and topping out at 91 °C. The top of the Riser Tube is somewhat cooler with that big Block hanging on the bottom, too: 70 to 74 °C, rather than the 83 °C I measured there.

    The Block temperature increases by 7 °C when the Core increases by 25 °C, obviously depending on a bunch of nonlinear effects. A rash extrapolation suggests a 100 °C switch would trip before the Core hit 275 °C.

    However, that Block gets uncomfortably close to 100 °C, which is the point where the Thermal Switch will go click and kill the whole show. I’d rather have a bit more headroom to allow for warm summer weather and a heated build chamber.

    So the next experiment puts the Thermal Switch Block at the top of the Thermal Riser Tube…

    The original data:

    Thermal Riser Temperatures - block at bottom
    Thermal Riser Temperatures – block at bottom
  • Thing-O-Matic / MK5 Extruder: Thermal Switch Block

    Thermal Switch Block on Thermal Riser
    Thermal Switch Block on Thermal Riser

    The best place to mount a thermal switch (or a thermal sensor, depending on how much you trust your circuitry) is on the MK5 Thermal Core, but that’s far too hot for the switches I have in hand. As a compromise, I decided to mount the switch on the Thermal Riser tube leading vertically upward to the Filament Drive gear: good thermal contact, a solid mount, and out of harm’s way.

    All the alternative locations seem worse. Tucking it inside the insulation wrap doesn’t provide a solid mechanical mount, so you don’t get a repeatable position and the leads get bent every time you move something. Bolting it to the plate over the Core looks solid, but that’s just a flat sheet of metal with four screws connecting it to the Core: no real thermal contact surrounded by lots of cooling air.

    One good omen: with an operating temperature well under 100 °C, JB Industro Weld epoxy will work fine and eliminate any need for fussy clamps and fittings.

    So I sawed off a random chunk of aluminum plate, squared it up in the Sherline mill, and poked a few holes in it. This doodle has dimensions roughly equivalent to the final object, but absolutely nothing is critical other than the 5/16 inch central hole:

    Switch block sketch
    Switch block sketch

    The 4-40 setscrew secures the block to the Thermal Riser. Aluminum expands considerably more than stainless steel, so I dropped a snippet of PTFE wire insulation into the hole as a rubberdraulic plunger.

    The lug on the top provides strain relief for the wires; it’s not an electrical connection. The modular phone cable trailing off to the Thermal Cutout box has wires insulated with low-temperature plastic, so a few inches of Teflon hookup wire keep them out of the Danger Zone.

    The small hole is just big enough for a thermocouple bead.

    This is what the thing eventually looked like, but I made some measurements before sticking that switch in place:

    Themal Switches - prepped and mounted
    Themal Switches – prepped and mounted

    Up next: measurements!

  • Tire Liner Abrasion

    Just fixed a flat on my bike which, like that one, came from the tire liner chewing through the tube. The holes are above the raised 28″ molded into the tube, at the upper-left corner of the tire liner impression.

    Schwalbe tube with tire liner abrasion
    Schwalbe tube with tire liner abrasion

    In this case, the tire liner (which, judging from the color, was a Slime) was too short by maybe 50 mm. This view inside the tire shows a 10 mm gap where the ends didn’t overlap as they should:

    Schwalbe Maration tire with liner abrasion
    Schwalbe Maration tire with liner abrasion

    I don’t trim the rear-tire  liners, but comparing a handful in the drawer shows that the as-sold lengths differ by a few tens of millimeters. The Marathons are husky tires, but the tread OD isn’t all that much different from stock tires: that’s the definition of a 700-series tire.

    That we’re getting repeated flats from tire liners intended to eliminate flats is, mmmm, disturbing. Looking at the condition of the tire treads, however, shows we’re not getting an order of magnitude more flats from road debris, so it’s a net win. I doubt we could get through a month of riding without a flat; I replace tires when the carcasses accumulate enough gashes that the tire liners begin extruding through the tread.

    Also, remember that these samples come from three bikes that travel upwards of 2000 miles a year (each!), not just one bike ridden along a nice rail trail on weekends…