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

  • 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
  • Flex-fatigued Helmet Cable

    I cable-tied the mic/earphone cable on Mary’s bike helmet to a rib on the fancy air vents near the back end, hoping that would reduce the inevitable flexing. Alas, it didn’t work out that way and the cable lasted only two seasons. This cut-away view shows the pulverized shield braid inside the jacket:

    Fatigue-failed helmet cable
    Fatigue-failed helmet cable

    The symptoms were totally baffling: the mic worked perfectly, but the earphones cut out for at most a few syllables. Of course, I can’t wear her helmet and it only failed occasionally while riding. I barked up several wrong trees, until it got so bad that I could make it fail in the garage while listening to the local NWS weather radio station.

    I spliced in a new USB male-A connector and (re-)discovered that the braid seems to be aluminum, rather than tinned copper. In any event, the wire is completely unsolderable; I crimped the braid from the new connector to a clean section of the old braid. The braid serves only as an electrostatic shield, as it’s not connected to anything on the helmet end. That should suffice until I rebuild the headsets this winter.

  • Ubuntu 11.10 vs. Epson R380 Printer: FAIL

    After getting everything configured, I hauled the Atom to the Basement Laboratory Computational Center, set the IP address, brought it up as the new file server, backed up the changed files, and everything worked fine. Then I plugged in all three printers, lit up the CUPS configuration screen, and configured the, uh-oh, two printers it could find.

    Turns out that the Epson R380 printer, being a member of the Epson R300 series of printers, doesn’t work with the USB subsystem in Ubuntu 11.10, for reasons that’ll surely get sorted out in a few months. Until then, it’s a showstopper for me.

    So I shut everything down, yanked out the Atom, plugged in the old AMD box, reconnected everything, restored the changed files from backup, and we’re back to where we were a few days ago. I’ll swap in the old drive and slide the Atom underneath the Thing-O-Matic again.

    Drat!

  • NFS V4 Tweaks

    Come to find out that Ubuntu 11.10 uses NFS V4 by default, which means the various clients scattered around here, all of which use NFS V3 by default, report all files have user & group 232 – 2: an awkward and unforgettable unsigned 4294967294. That’s -2 in 2’s complement notation with 32 bit hex numbers, corresponding to the unsigned 16-bit 65534 = -2 for the nobody user & group.

    Fix that by editing /etc/default/nfs-common to set NEED_IDMAPD=yes. Unmount the NFS share, do sudo start idmapd, remount, and it’s all good. The next time the client boots, the idmapd daemon starts automagically, and that’s all good, too.

    Adding the -t nfs4 filetype in /etc/fstab seems to be not necssary.

    How I got into this mess: the Intel Atom D525 that had been driving the Thing-O-Matic has a bog-standard Intel graphics chip that, despite (or perhaps because of) having an open-source video driver, reports doing only OpenGL 1.4. OpenSCAD, however, requires OpenGL 2.0 and those hacks don’t allow it to run properly, which makes it awkward for demos. The AMD that’s currently the file server has, IIRC, better graphics that might improve the situation; I think it sports a somewhat peppier processor, too. The fact that it’s running Ubuntu 8.10 says that it’s time for an update.

    Soooo, I swapped in a new 1.5 TB SATA drive, installed hot-from-the-oven Ubuntu 11.10, replaced Unity with XFCE, inhaled all the current data from the file server’s external USB backup drive, configured ssh / nfs / etc, and I’m now doing some simpleminded tests before I swap the IP addresses.

    Now, if the AMD has craptastic graphics hardware, it’s unhappy dance time…

  • Sawed-off Sawhorse

    As part of sawing a kitchen countertop apart to fit it into the bathroom, this happened:

    Sawed-off sawhorse
    Sawed-off sawhorse

    I’d very carefully checked the clearance for the first two cuts, but …

    The sawhorse is polyethylene, which cannot be glued, so I drilled holes in the internal bulkheads, slobbered JB Industro-Weld epoxy through them, and filled the gaps with wood blocks:

    Wood-epoxy PE repair
    Wood-epoxy PE repair

    The goal being to not have metallic fasteners where the saw blade can find them.

    This should work for a while:

    Sawhorse cap repaired
    Sawhorse cap repaired

    If that’s never happened to you, I’d say you aren’t doing enough circular saw work…

  • Bathroom Sink Replacement

    I finally got around to replacing the sink in the front bathroom, which required a surprising number of tools:

    Bathroom tool midden heap
    Bathroom tool midden heap

    As with the three other sinks I’ve replaced over the years, this one was a beautiful cast-iron monster made by the American Regulator & Standard Sanitary company, back before the name mushed into American Standard. This casting shows the original typography:

    Bathroom sink by American Regulator and Standard Sanitary
    Bathroom sink by American Regulator and Standard Sanitary

    A thin stainless steel trim ring and 16 (!) clamps held the sink in place on the countertop. Harsh experience taught me to support the sink while removing the clamps, because without the clamps there is nothing holding the sink up and I no longer enjoy stopping the tailpiece of a cast-iron sink with my chest…

    Supporting the old sink
    Supporting the old sink

    As it turned out, the sink required two pumps on the jack to break it free from the gunk gluing it in place; I was pleased to be wrong. I toted it to the end of the driveway, put a FREE sign on it, wherefrom it vanished within two hours. We’ll never know if it became someone’s precious antique or just a source of heavy brass fittings at the scrap metal recycler.

    The original vanitory countertop had been recessed into the corner walls before the tiles went up, so I sawed out a chunk of the front edge and bent the plywood enough to tap it out without destroying anything. The countertop rotated around the left-front corner and the right-rear corner looked like this when the dust settled:

    Extracted vanitory countertop
    Extracted vanitory countertop

    Half a century ago, the tile installers did a lovely mud job; the tiles adjoin and the grout is barely 1/16 inch wide. The vanitory case top was dead level, but the tiles weren’t quite aligned and my carefully applied and very neat 5 mm stripe of new caulk looks downright amateurish.

    For what it’s worth, the new countertop started life as a stock kitchen countertop. I sawed off the backsplash, trimmed the length, cut a pair of notches to match the recesses, sawed a hole for the sink, rotated it into place, and screwed it down. You can go the custom-top route, but given that you only see about two square feet when you’re done, dropping $400 for 6 ft2 of fancy material with a gaping sink hole or over a kilobuck for a countertop with built-in recessed sink didn’t make enough sense to us.

    And, no, vanitory is not a misspelling; I learned a new word during this project:

    Vanitory job label
    Vanitory job label

    After we sell the house, the new owners will rip all this out without a second thought. After all, Dusky Rose went out of style a long time ago, a perfect hand-set array of 3/4 x 1-5/8 inch floor tiles isn’t attractive, and nobody cares about mud jobs. We’d rather keep that nice work around (even if we’re willing to put up with a simple countertop), but that’s just us; we’re the type of people who think keeping the original spring-loaded turned-wood dowel in the toilet paper holder is charming.

    They’ll junk that space heater recessed into the wall, too: it has a long coily 120 V heating element strung inside, easily within the reach of questing little fingers. I added a GFI to that circuit, but I can’t imagine anybody else tolerating it. Times change.

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