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.

Category: Machine Shop

Mechanical widgetry

  • Monthly Science: Basement Safe Humidity

    A plot of the temperature and humidity inside the basement safe over the last year-and-a-half:

    Basement Safe
    Basement Safe

    The tray of silica gel (or whatever those granules might be) holds the humidity firmly at 14 to 15 %, at least with a simple masking tape seal around the door opening that dates back to early 2012. Less water vapor gets through the door during the winter, due to the lower basement humidity when it’s cold outside, but it looks like four regenerations per year with just under a kilogram of desiccant in the tray.

    Ordinarily, it’d be time for those granules to endure another oven session, but I just picked up a bunch of real silica gel beads and must conjure up some porous bags.

    The Bash / Gnuplot script that cleaned the CSV files and produced the plot:

    #!/bin/sh
    #-- overhead
    export GDFONTPATH="/usr/share/fonts/truetype/"
    base="${1%.*}"
    echo Base name: ${base}
    ofile=${base}.png
    tfile=$(tempfile)
    echo Input file: $1
    echo Temporary file: ${tfile}
    echo Output file: ${ofile}
    #-- prepare csv Hobo logger file
    sed 's/^\"/#&/' "$1" | sed 's/^.*Logged/#&/' | sed 's/ ,/,/' | sed 's/\/\([0-9][0-9]\) /\/20\1 /' > ${tfile}
    #-- do it
    gnuplot << EOF
    #set term x11
    set term png font "arialbd.ttf" 18 size 950,600
    set output "${ofile}"
    set title "${base}"
    set key noautotitles
    unset mouse
    set bmargin 4
    set grid xtics ytics
    set timefmt "%m/%d/%Y %H:%M:%S"
    set xdata time
    set xlabel "Date"
    set format x "%y-%m"
    #set xrange [1.8:2.2]
    set xtics font "arial,12"
    #set mxtics 2
    #set logscale y
    set ytics nomirror autofreq
    set ylabel "Temperature - F"
    set format y "%3.0f"
    set yrange [40:90]
    set mytics 2
    set y2label "Relative Humidity - %"
    set y2tics nomirror autofreq
    set format y2 "%3.0f"
    set y2range [10:60]
    #set y2tics 32
    #set rmargin 9
    set datafile separator ","
    #set label 1 "label text" at 2.100,110 right font "arialbd,18"
    #set arrow from 2.100,110 to 2.105,103 lt 1 lw 2 lc 0
    plot    \
        "${tfile}" using 2:3 axes x1y1 with lines lt 3 title "Temperature",\
        "${tfile}" using 2:4 axes x1y2 with lines lt 4 title "Humidity"
    EOF
    
  • Makergear M2: Platform Stabilization

    The M2’s build platform consists of an 8×10 inch glass slab atop an aluminum spider, all supported by a trio of fairly stiff springs. Back when I was experimenting with excessive acceleration, I inserted some silicone rubber cylinders to boost the spring constant and stabilize the platform

    The same vibration isolators that provided the vacuum cleaner’s floor brush rollers came through again:

    Silicone rubber pads for M2 platform - punching
    Silicone rubber pads for M2 platform – punching

    I removed the screws and springs one by one, tucked a cylinder inside the spring, and reinstalled it:

    Silicone rubber pads for M2 platform - installed
    Silicone rubber pads for M2 platform – installed

    The trick is to park the nozzle near the edge of the platform where it will rise without the screw holding it down, measure the distance twixt nozzle and platform, lower the platform by a (known!) 50 mm, install the cylinder, raise the platform, then tweak the screw to put the same distance between the nozzle and the platform as you started with.

    This probably doesn’t make much difference with the default 3 m/s2 acceleration, but up around 10 m/s2 it seemed wobbly. No suprise: that’s over 1 G of lateral acceleration and the platform weighs a pound or so.

  • Hexagonal Chain Mail

    Everybody at the Squidwrench Show-n-Tell loved the chain mail. I printed up a sheet of Ablapo’s hexagonal chain mail before the show (because it takes forever) and it came out very nicely on the M2’s larger platform:

    Hexagonal Chain Mail - on platform
    Hexagonal Chain Mail – on platform

    The first layers sprinkle a bazillion little shapes all over the platform:

    Hexagonal Chain Mail - first layer
    Hexagonal Chain Mail – first layer

    It makes a nice doily for my desk lamp:

    Hexagonal Chain Mail - lamp doily
    Hexagonal Chain Mail – lamp doily

    The odd blue coloration must be an optical effect, because it’s not visible on the black PLA.

  • 3D Printing Show-n-Tell

    So the local hackerspace put on a show in Highland and, being the guy with the 3D printer, I volunteered for a few hours of stand-up comedy.

    A pile of stuff provided talking points:

    3D Printing This Way - with clutter
    3D Printing This Way – with clutter

    Four pages of solid model images on nice glossy photo paper let me talk about what must happen before the plastic emerges:

    I loves me some OpenSCAD modeling, yes, I do…

    Signage festooned with sample critters dragged ’em in off the street:

    3D Printing This Way - signs on post
    3D Printing This Way – signs on post

    The printer produced a steady stream of tiny owls:

    M2 - Mini owl on platform
    M2 – Mini owl on platform

    And the ever-popular whistle:

    M2 - Whistle on platform
    M2 – Whistle on platform

    A good time was had by all!

  • Garden Sprayer: End of Life

    The hose attached to this garden sprayer had failed last season, but the hose fitting had become one with the sprayer. Soaking it with penetrating oil for far longer than seemed necessary didn’t help, so I tried brute force:

    Garden sprayer hose fitting
    Garden sprayer hose fitting

    After convincing myself that wasn’t going to work, I cut the fitting off and tried the old standby of collapsing the threaded shell inward with a small punch:

    Garden sprayer - rolled-in fitting
    Garden sprayer – rolled-in fitting

    That didn’t work, either: the shell really had become one with the sprayer.

    As it turned out, the plastic sprayer body had begun to crack in several high-stress locations and would shortly become Yet Another Project. I cut my losses and tossed the hose and the sprayer.

  • LED + Photodiode: Verifying Linearity

    Given that test fixture, the obvious question is whether the PIN-10AP photodiode’s output current varies linearly with light intensity, just like the specs would lead you to believe. I excavated the sheet of 2-stop neutral density filter gel from the Parts Warehouse Wing and cut some 30 mm disks:

    LED Photodiode test fixture - ND filter disks
    LED Photodiode test fixture – ND filter disks

    A single filter layer should reduce the light intensity by 2 f/stops = a factor of 4. Each successive layer reduces the intensity by another factor of 4. They’re all at least reasonably clean and free of defects, but they’re definitely not optical lens quality.

    Running the LED with a 100 mA pulse at 20% duty cycle and stacking the disks in the fixture, one by one, between the LED and photodiode, produces this data:

    Layers Attenuation Scale V I – uA Ratio
    0 1 1.0000 8.7 87  
    1 4 0.2500 1.9 19 4.58
    2 16 0.0625 0.43 4.3 4.42
    3 64 0.0156 0.097 0.97 4.43
    4 256 0.0039 0.022 0.22 4.41

    The Ratio column divides successive pairs of current values. The first step, from “no filter” to “one filter”, came out a bit larger than the rest, probably because the gel sheet isn’t anti-reflective and some light bounces off the top.

    After that, though, it looks just like I’m cheating, doesn’t it?

    The ratios should be 4.0, but the actual 4.4 means it’s a 2.1 stop filter. Close enough, methinks.

  • LED + Photodiode Test Fixture

    An upcoming Circuit Cellar column calls for a way to measure LED light output vs. current input, which means I need some way to hold LEDs directly over a photodiode while excluding ambient light. Fortunately, the M2 had black PLA filament already loaded:

    LED Photocell Fixture - parts
    LED Photocell Fixture – parts

    That honkin’ big photodiode is a surplus PIN-10AP that’s been lying in wait for an opportunity just like this. The green filter matches the silicon response to CIE-standard human eye sensitivity, so the output tracks what you’d actually see. That’s irrelevant for testing red LEDs that all have pretty much the same wavelength, but it might come in handy for something.

    The main body of the fixture holds the LED about 1 mm from the front of the photodiode, indexed against the LED flange so they’re all at a consistent location. The cap has three locating pins made of 3 mm orange filament, with black foam rubber to push the LED into position and block ambient light.

    The business end looks like this:

    LED Photocell Fixture - LED view
    LED Photocell Fixture – LED view

    The most convenient way to mount the thing involves a right-angle BNC adapter in my trusty bench vise:

    LED Photocell Fixture - with breadboard
    LED Photocell Fixture – with breadboard

    The circuitry has a voltage-to-current driver for the LED and a zero-bias current-to-voltage converter for the photocell. The zero-bias trick keeps the voltage across the photodiode at zero, so the current varies linearly with illumination.

    The solid model laid out for printing along the X axis:

    LED Fixture for PIN-10AP Photodiode - solid model overview
    LED Fixture for PIN-10AP Photodiode – solid model overview

    It obviously has some improvements over the as-printed one in the pictures, in the unlikely event I need another fixture. The most important: a rear ring covering the back of the photodiode. Turns out that the PIN-10AP filter cap leaks a surprising amount of light around the body; I covered the gap with black tape to make the measurements, but that’s crude.

    I added a few screw holes to hold the parts together, but the cap (with the foam and pegs) must come off easily while swapping LEDs. I’d be tempted to sink studs into the body and use wing nuts to hold the lid in place, but I don’t have any 4-40 wing nuts…

    There’s a tiny bit of support under the central hole to support the LED flange recess and the trench for some foam under the leads:

    LED Fixture for PIN-10AP Photodiode - support
    LED Fixture for PIN-10AP Photodiode – support

    That’s another improvement; the as-printed one has foam on only one side of the leads.

    The OpenSCAD source code:

    // LED test fixture for PIN-10AP photodiode
    // Ed Nisley KE4ZNU May 2013
    
    // Layouts: Adapter AdapterSupport Cap Shield Build Show
    
    Layout = "Build";
    
    Gap = 8;		// between parts in Show
    
    //- Extrusion parameters must match reality!
    //  Print with +1 shells and 3 solid layers
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;			// make holes end cleanly
    
    Spacing = 5;				// between parts on build platform
    
    inch = 25.4;
    
    Tap2_56 = 0.070 * inch;
    Clear2_56 = 0.082 * inch;
    Head2_56 = 0.156 * inch;
    Head2_56Thick = 0.055 * inch;
    
    //----------------------
    // Dimensions
    
    PhotoDiodeOD = 31.3;
    PhotoDiodeStemOD = 16.0;
    PhotoDiodeStemLength = 8.0;
    PhotoDiodeWindowDia = 17.7;
    PhotoDiodeHeight = 14.0;
    
    FixtureOD = PhotoDiodeOD + 2*7.0;
    
    LEDDia = 5.0;				// LED body
    LEDFlangeOD = 6.0;			// flange at base of LED
    LEDFlangeThick = IntegerMultiple(1.5,ThreadThick);
    LEDLength = 10.0;			// overall length
    LEDRecess = 4.0;			// tube to fit LED body
    LEDSides = 8;
    
    FixtureLength = PhotoDiodeHeight + LEDLength + IntegerMultiple(1.0,ThreadThick);
    
    CapLength = 15.0;			// LED cover
    
    FoamOD = FixtureOD/2;
    FoamThick = IntegerMultiple(2.0,ThreadThick);
    
    TrenchDepth = 2*FoamThick;
    TrenchWidth = LEDDia;
    
    ShieldThick = 5.0;
    ShieldScrewCircle = PhotoDiodeOD + (FixtureOD - PhotoDiodeOD)/2;
    
    PinOD = 3.0;				// alignment pin (filament)
    PinLength = 10.0;			//   ... total length
    PinCircle = FixtureOD/2;
    
    GrubScrewOD = Tap2_56;
    
    $fn=4*6;					// default cylinder sides
    
    //----------------------
    // 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) {
    
      RangeX = floor(95 / Space);
      RangeY = floor(125 / Space);
    
    	for (x=[-RangeX:RangeX])
    	  for (y=[-RangeY:RangeY])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    
    }
    
    //-----------------------
    // Parts
    
    module Adapter() {
    
    	difference() {
    		cylinder(r=FixtureOD/2,h=FixtureLength);
    		translate([0,0,-Protrusion]) {
    			PolyCyl(LEDDia,2*FixtureLength,LEDSides);
    			PolyCyl(PhotoDiodeWindowDia,(FixtureLength - LEDRecess + Protrusion));
    			PolyCyl(PhotoDiodeOD,(PhotoDiodeHeight + Protrusion));
    		}
    		translate([0,0,(FixtureLength - LEDFlangeThick)])
    			PolyCyl(LEDFlangeOD,2*LEDFlangeThick,LEDSides);
    		translate([FixtureOD/2,0,(FixtureLength + FoamThick/2 - LEDFlangeThick)]) {
    			cube([FixtureOD,TrenchWidth,FoamThick],center=true);
    		}
    		for (angle = [90:90:270])
    			rotate(angle)
    				translate([0.75*PinCircle,0,(FixtureLength - PinLength/2)])
    					PolyCyl(PinOD,PinLength,6);
    		for (angle = [0:120:240])
    			rotate(angle)
    				translate([ShieldScrewCircle/2,0,-Protrusion])
    					rotate(45)
    						PolyCyl(Tap2_56,(ShieldThick - 6*ThreadThick + Protrusion));
    		if (0)
    			translate([0,0,FixtureLength/4])
    				rotate([0,90,0])
    					PolyCyl(GrubScrewOD,FixtureOD);
    	}
    
    }
    
    module AdapterSupport() {
    
    spiderthick = IntegerMultiple(LEDFlangeThick - ThreadThick,ThreadThick);
    
    	color("Yellow")
    		union() {
    			for (leg = [0:LEDSides/2 - 1])
    				rotate(leg*360/LEDSides)
    					translate([0,0,spiderthick/2])
    					cube([(LEDFlangeOD - 0.5*ThreadWidth),
    							2.5*ThreadWidth,
    							spiderthick],
    							center=true);
    			cylinder(r=LEDDia/2,h=spiderthick,$fn=LEDSides);
    			for (bar = [-1:1])
    				translate([LEDDia/3,(bar*3*ThreadWidth - ThreadWidth),0])
    					cube([FixtureOD/2,2*ThreadWidth,IntegerMultiple(LEDFlangeThick - ThreadThick)]);
    		}
    }
    
    module Cap() {
    
    	difference() {
    		cylinder(r=FixtureOD/2,h=CapLength);
    		translate([(FixtureOD/2 - LEDDia/2),0,CapLength]) {
    			cube([FixtureOD,TrenchWidth,2*TrenchDepth],center=true);
    		}
    		translate([0,0,(CapLength - FoamThick)])
    			PolyCyl(FoamOD,(FoamThick + Protrusion));
    		for (angle = [90:90:270])
    			rotate(angle)
    				translate([0.75*PinCircle,0,(CapLength - PinLength/2)])
    					PolyCyl(PinOD,PinLength,6);
    	}
    
    }
    
    module Shield() {
    
    	difference() {
    		cylinder(r=FixtureOD/2,h=ShieldThick);
    		translate([0,0,-Protrusion])
    			PolyCyl(PhotoDiodeStemOD,(ShieldThick + 2*Protrusion));
    		for (angle = [0:120:240])
    			rotate(angle) {
    				translate([ShieldScrewCircle/2,0,-Protrusion])
    					rotate(180/5)
    						PolyCyl(Clear2_56,(ShieldThick + 2*Protrusion));
    				if (0)
    				translate([ShieldScrewCircle/2,0,(ShieldThick - 1.5*Head2_56Thick)])
    					rotate(180/6)
    						PolyCyl(Head2_56,4*Head2_56Thick);
    			}
    	}
    }
    
    //-------------------
    // Build it...
    
    ShowPegGrid();
    
    if (Layout == "Adapter")
    	Adapter();
    
    if (Layout == "Cap")
    	Cap();
    
    if (Layout == "Shield")
    	Shield();
    
    if (Layout == "Show") {
    	translate([0,0,(ShieldThick + Gap)]) {
    		translate([0,0,FixtureLength + CapLength + Gap])
    			rotate([180,0,0])
    				Cap();
    		Adapter();
    
    		color("Orange")
    		for (angle = [90:90:270])
    			rotate(angle)
    				translate([0.75*PinCircle,0,(FixtureLength + Gap - PinLength/2)])
    					PolyCyl(PinOD,PinLength,6);
    	}
    
    	Shield();
    }
    
    	if (Layout == "AdapterSupport") {
    	translate([0,0,FixtureLength])
    		rotate([180,0,0])
    			%Adapter();
    	AdapterSupport();
    }
    
    if (Layout == "Build") {
    	translate([(Spacing + FixtureOD),0,0]) {
    		translate([0,0,FixtureLength])
    			rotate([180,0,0])
    				Adapter();
    		AdapterSupport();
    	}
    	translate([0,0,0])
    		Cap();
    
    	translate([-(Spacing + FixtureOD),0,0])
    		Shield();
    }