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: Thing-O-Matic

Using and tweaking a Makerbot Thing-O-Matic 3D printer

  • Companion Cube Array

    Companion Cubes make good tchotchkes for presentations:

    Companion cube array
    Companion cube array

    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:

    Companion Cubes - detail
    Companion Cubes – detail
  • Zombie Apocalypse Preparations

    When in doubt, nuke ’em from orbit. It’s the only way to be sure:

    Finned CO2 Cartridge on build platform
    Finned CO2 Cartridge on build platform

    When confronted with a zombie horde, though, nothing exceeds like excess:

    Finned CO2 Cartridge Array
    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
    Fin Array on build platform

    The solid model looks about like you’d expect:

    Fin Assembly- solid model
    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.

    Now that I’m trying to be a subtractive kind of guy, that’s actually a fin block:

    Fin Block - solid model
    Fin Block – solid model

    Minus the CO2 cartridge that should fit inside:

    CO2 Cartridge - solid model
    CO2 Cartridge – solid model

    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
    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
    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();
    

    The original doodles:

    CO2 Cartridge Fin Doodles
    CO2 Cartridge Fin Doodles
  • Thing-O-Matic: Triple Cylinder Thing

    My buddy Mark One asked me to make a golf-ball sized Thing that’s the intersection of three mutually orthogonal cylinders. He claims I (subtractively) machined one from solid plastic, many many years ago, but I cannot imagine I ever had that level of machine shop fu; right now, I’m not sure how I’d fixture the thing.

    Cylinder Thing - solid model
    Cylinder Thing – solid model

    It’s much easier with a 3D printer…

    Of course, spheroids aren’t printable without support, but you can chop one in half to reveal the nice, flat interior surfaces, then add holes for alignment pegs. Using 0.50 infill makes for a compact mesh inside the ball:

    Cylinder Thing - building
    Cylinder Thing – building

    Smooth a few imperfections from the mating surfaces and add four pegs (the other two are busy propping the right-hand half off the countertop). Somewhat to my surprise, the alignment holes came out a perfect push fit for the 2.9 mm actual-OD filament with my more-or-less standard 0.2 mm HoleWindage Finagle Constant. This also uses the 1.005 XY scale factor to adjust for ABS shrinkage, not that that matters in this case:

    Cylinder Thing - alignment pegs
    Cylinder Thing – alignment pegs

    Then solvent-bond everything together forever more:

    Cylinder Thing - clamped
    Cylinder Thing – clamped

    The seam is almost imperceptible around the equator, perhaps because I didn’t slobber solvent right up to the edge. I did print one without the alignment pegs and demonstrated that you (well, I) can’t glue a spheroid without fixturing the halves; that one goes in my Show-n-Tell heap.

    The 0.33 mm Z resolution produces sucky North and South poles; the East, West, Left, and Right poles are just fine, as are the eight Tropical Vertices. After mulling for a bit, I rotated a cylindrical profile upward:

    Cylinder Thing Rotated - solid model
    Cylinder Thing Rotated – solid model

    The obvious contour lines fit the cylinder much better, although you can see where better Z resolution would pay off:

    Cylinder Thing - rotated
    Cylinder Thing – rotated

    This was at 0.33 mm x 0.66 mm, 200 °C, 30 & 100 mm/s, 2 rpm. No delamination problems; I applied a wood chisel to persuade those big flat surfaces to part company with the Kapton tape.

    The OpenSCAD source code:

    // Three intersecting cylinders
    // Ed Nisley KE4ZNU - Oct 2011
    
    Layout = "Build";			// Show Build
    
    //- Extrusion parameters must match reality!
    //  Print with +1 shells and 3 solid layers
    //  Use infill solidity = 0.5 or more...
    
    ThreadThick = 0.33;
    ThreadWidth = 2.0 * ThreadThick;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //------ Model dimensions
    
    CylDia = 2*IntegerMultiple(40.0/2,ThreadThick);
    CylRad = CylDia/2;
    
    echo(str("Actual diameter: ",CylDia));
    
    Angle = [45,0,0];			// rotate to choose build orientation
    
    $fn=128;
    
    AlignPegDia = 2.90;
    
    //-------
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    module PolyCyl(Dia,Height,ForceSides=0) {			// based on nophead's polyholes
    
      Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    
      FixDia = Dia / cos(180/Sides);
    
      cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    
    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);
    
    }
    
    //------- Model bits & pieces
    
    module OneCyl() {
      cylinder(r=CylRad,h=CylDia,center=true);
    }
    
    module ThreeCyl() {
      intersection() {
    	OneCyl();
    	rotate([90,0,0]) OneCyl();
    	rotate([0,90,0]) OneCyl();
      }
    }
    
    module HemiThing() {
      difference() {
    	rotate(Angle)
    	  ThreeCyl();
    	translate([0,0,-CylRad])
    		cube(CylDia,center=true);
    	for (Index = [0:3])
    	  rotate(Index*90)
    		translate([CylRad/2,0,-Protrusion])
    		  PolyCyl(AlignPegDia,5+Protrusion);
      }
    }
    
    //---------
    
    ShowPegGrid();
    
    if (Layout == "Show")
      ThreeCyl();
    
    if (Layout == "Build") {
      translate([CylRad,CylRad,0])
    	HemiThing();
    
      translate([-CylRad,-CylRad,0])
    	  HemiThing();
    }
    
  • KG-UV3D GPS+Voice: Radio Base Interface

    The Wouxun KG-UV3D has three holes along the base that capture three tabs in the battery case, with tapered edges to align the case with the contacts. After a few passes to get the dimensions right, the plate matching those features came out like this:

    Base plate with tabs
    Base plate with tabs

    The solid model shows the edge tapering down to a single layer:

    Case Tab Base - Solid Model
    Case Tab Base – Solid Model

    The compound taper on the corners must match both the base and the sides of the radio. The bottom plate and shell have corresponding tapers that extend across the glued joints:

    Radio interface tapers
    Radio interface tapers

    That worked out surprisingly well, given the small dimensions and odd angles. The tabs, in particular, bumped right up against the 0.66 mm extrusion width; they’re 2.0 mm thick, so there’s barely one thread width inside the perimeter for fill. A bit of filing & slicing removed the usual enlargement at the end / start of each perimeter thread on the tabs, which is entirely acceptable for something this finicky.

    The OpenSCAD source code with dimensions is all part of that post, but here’s the radio base shape that gets subtracted from the plate to make those tabs:

    Radio Base Polygon - solid model
    Radio Base Polygon – solid model

    This seemed easier than adding a bunch of tiny pegs & triangles, but it’s certainly tedious working around a polygon:

    module RadioBase() {
    
    linear_extrude(height=(BaseOpeningDepth + Protrusion),center=false,convexity=5)
    polygon(points=[
    [-BaseOpeningMax/2,-Protrusion],
    
    [-BaseOpeningMin/2,BaseOpeningY],
    [-(BaseToothOC/2 + BaseToothBase/2),BaseOpeningY],
    
    [-(BaseToothOC/2 + BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    [-(BaseToothOC/2 - BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    [-(BaseToothOC/2 - BaseToothBase/2),BaseOpeningY],
    
    [ (BaseToothOC/2 - BaseToothBase/2),BaseOpeningY],
    [ (BaseToothOC/2 - BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    [ (BaseToothOC/2 + BaseToothTip/2),(BaseOpeningY - BaseToothThick)],
    [ (BaseToothOC/2 + BaseToothBase/2),BaseOpeningY],
    [ BaseOpeningMin/2,BaseOpeningY],
    
    [ BaseOpeningMax/2,-Protrusion],
    
    [ (BaseTabOC + BaseTabWidth/2),-Protrusion],
    [ (BaseTabOC + BaseTabWidth/2),BaseTabThick],
    [ (BaseTabOC - BaseTabWidth/2),BaseTabThick],
    [ (BaseTabOC - BaseTabWidth/2),-Protrusion],
    
    [ BaseTabWidth/2,-Protrusion],
    [ BaseTabWidth/2,BaseTabThick],
    [-BaseTabWidth/2,BaseTabThick],
    [-BaseTabWidth/2,-Protrusion],
    
    [-(BaseTabOC + BaseTabWidth/2),-Protrusion],
    [-(BaseTabOC + BaseTabWidth/2),BaseTabThick],
    [-(BaseTabOC - BaseTabWidth/2),BaseTabThick],
    [-(BaseTabOC - BaseTabWidth/2),-Protrusion],
    ],
    convexity=5
    );
    }
    

    Then subtracting that shape and some inclines…

    Radio Base Interface - solid model - thrown together
    Radio Base Interface – solid model – thrown together

    … lets the base plate pop out of this code:

    module Base() {
    
      difference() {
    
    	translate([0,0,(BaseThick + BaseOpeningDepth)/2])
    	  rotate([-90,0,0])
    		CaseEnvelope(BaseThick + BaseOpeningDepth);
    
    	translate([0,0,BaseThick])
    	  RadioBase();
    
    	translate([(BaseToothOC + BaseTabWidth/2),
    			  -(BaseThick + BaseEndLip)/tan(BaseEndAngle),
    			  0])
    	  rotate([BaseEndAngle,0,0])
    		cube([BaseEndWidth,3*BaseOpeningY,BaseOpeningDepth],center=false);
    
    	translate([-(BaseToothOC + BaseTabWidth/2 + BaseEndWidth),
    			  -(BaseThick + BaseEndLip)/tan(BaseEndAngle),
    			  0])
    	  rotate([BaseEndAngle,0,0])
    		cube([BaseEndWidth,3*BaseOpeningY,BaseOpeningDepth],center=false);
      }
    }
    

    I’m still doodling the electronics, alas…

  • LILUG Meeting Presentation

    Multicolored Chalk People
    Multicolored Chalk People

    In the admittedly unlikely event you happen to be near the left-center part of Long Island this evening, drop in on my DIY 3D Printing & the Makerbot Thing-O-Matic presentation for the Long Island Linux Users Group meeting and pick up a tchotchke!

    Many thanks to LILUG for ruthlessly eliminating all my objections to leaving the Basement Laboratory…

  • KG-UV3D GPS+Voice: Plug Mounting Plate

    Unlike my old ICOM IC-Z1A, the Wouxun KG-UV3D radio has mic and speaker jacks recessed into the case, so that a custom plug plate can absorb all the stress from forces applied to the cables without wiggling the plugs. Even better, there’s a removable cover with a mounting screw that can hold the new plate in place!

    Wouxun plug mounting plate - overview
    Wouxun plug mounting plate – overview

    The first pass at the mount required a bit of filing, as the deepest part of the recess turns out to be not exactly rectangular. That’s (probably) fixed in the source code:

    Wouxun plug plate - detail
    Wouxun plug plate – detail

    The solid model looks about like you’d expect, with terribly thin side walls between the plugs and the not-quite-rectangular section. The whole affair is asymmetrical around the long axis; the not-quite-rectangular block and hole really are offset:

    Plug Mount Plate - Solid Model
    Plug Mount Plate – Solid Model

    When printed, the thin sections come out one 0.66 mm plastic thread wide:

    Wouxun plug mounting plate - build
    Wouxun plug mounting plate – build

    I spent quite some time iterating through OpenSCAD, RepG, and SkeinLayer to make sure that came out right. This is from a later version with larger recesses around the plugs:

    Plug Mount Plate - skeinlayer
    Plug Mount Plate – skeinlayer

    Some epoxy eased down along the plugs will lock them into the plastic, with an epoxy putty turd over the top to stabilize the cables and terminal connections. That’s a T6 Torx bit to mate with the 2 mm screw (with a captive washer!) pulled from the Small Drawer o’ Salvaged Metric Screws:

    Wouxun plug plate - trial fit
    Wouxun plug plate – trial fit

    The OpenSCAD source code is part of the huge block of code at the bottom of that post, but here’s the relevant section:

    module PlugPlate() {
    
      BaseX = PlugBaseWidth/2 - PlugBaseRadius;
      BaseY = PlugBaseLength/2 - PlugBaseRadius;
    
      difference() {
    	union() {
    	  linear_extrude(height=PlugBaseThick,center=false,convexity=3)
    		hull() {
    		  translate([-BaseX,-BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([-BaseX, BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([ BaseX, BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		  translate([ BaseX,-BaseY,0])
    			circle(r=PlugBaseRadius,$fn=8);
    		}
    
    	  translate([PlugFillOffsetX,
    				(PlugFillLength/2 - PlugBaseLength/2 + PlugFillOffsetY),
    				PlugBaseThick])
    		linear_extrude(height=PlugFillThick,center=false,convexity=5)
    		  hull() {
    			translate([0,-(PlugFillLength/2 - PlugFillRadius2),0])
    			  circle(r=PlugFillRadius2,$fn=10);
    			translate([-(PlugFillWidth/2 - PlugFillRadius1),-PlugBaseLength/2,0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([-(PlugFillWidth/2 - PlugFillRadius1),
    					  (PlugFillLength/2 - PlugFillRadius1),0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([(PlugFillWidth/2 - PlugFillRadius1),
    					  (PlugFillLength/2 - PlugFillRadius1),0])
    			  circle(r=PlugFillRadius1,$fn=8);
    			translate([(PlugFillWidth/2 - PlugFillRadius1),-PlugBaseLength/2,0])
    			  circle(r=PlugFillRadius1,$fn=8);
    		  }
    	}
    
    	translate([0,-JackOC/2,-Protrusion])
    	  rotate(360/16) {
    		PolyCyl(Plug3BezelDia,(Plug3BezelThick + Protrusion),8);
    		PolyCyl(Plug3ScrewDia,(PlugBaseThick + PlugFillThick + 2*Protrusion),8);
    	  }
    
    	translate([0,+JackOC/2,-Protrusion])
    	  rotate(360/16) {
    		PolyCyl(Plug2BezelDia,(Plug2BezelThick + Protrusion),8);
    		PolyCyl(Plug2ScrewDia,(PlugBaseThick + PlugFillThick + 2*Protrusion),8);
    	  }
    
    	translate([JackScrewOffsetX,-(PlugBaseLength/2 + JackScrewOffsetY),0])
    	  PolyCyl(JackScrewDia,(PlugBaseThick + PlugFillThick + Protrusion));
      }
    
    }
    
  • Thing-O-Matic: Delamination

    ABS plastic shrinks as it cools and large objects with thin sections tend to delaminate, as seen in the Barbie Pistol and a few other objects. The box for the GPS+voice interface is four threads thick and 35 mm tall, which provided enough energy to rip the side apart:

    Box wall delamination
    Box wall delamination

    Solvent glue and a clamp shoved it back together again:

    Clamping delamination
    Clamping delamination

    This one was extruded at 190 °C, which works fine for small objects and isn’t quite enough to fuse something like this. I’ll crank it up to 210 °C for the next iteration to see if that improves the result.