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: Improvements

Making the world a better place, one piece at a time

  • LED Ring Desk Lamp

    A defunct desk lamp emerged from the clutter and cried out for bright, new LEDs. This adapter puts a small LED ring and nine white LEDs on the original lamp head:

    Ring Light Mount - in operation
    Ring Light Mount – in operation

    Peering into the business end, before mounting it on the lamp, shows some abrasive adjustment on the inside layer:

    Ring Light Mount - LEDs installed
    Ring Light Mount – LEDs installed

    That layer printed over a quick-and-easy support spider:

    Ring Light Mount - solid model - bottom
    Ring Light Mount – solid model – bottom

    The Slic3r preview looking down through the layer just over the support shows that the perimeter of those LED holes doesn’t have much support:

    Ring Light Mount - Slic3r preview - bridge layer
    Ring Light Mount – Slic3r preview – bridge layer

    The obvious threads drooped in the predictable way, so I just clipped them off, sanded the high spots into submission, and epoxied everything in place:

    Ring Light Mount - LED wiring
    Ring Light Mount – LED wiring

    That nice Hilbert Curve infill is completely wasted inside the OEM shade, but the smooth curve around the rim had to be on the top surface.

    Rather than beefing up the support, you should print the bottom ring (or the top rim) separately, then glue it back on, but I wanted to see how well simple support worked with PETG.

    It came out reasonably well:

    Ring Light Mount - support spider
    Ring Light Mount – support spider

    That’s far more hair than usual, even for PETG, because I made the spider’s legs exactly three thread widths wide. Slic3r reduced the single infill thread to, literally, a hair that didn’t stick to the platform; the model now has four-thread-wide legs.

    Slic3r’s automatic support would do a better job of holding up the underside, albeit with more plastic and printing time:

    Ring Light Mount - Slic3r preview - auto support
    Ring Light Mount – Slic3r preview – auto support

    The top view looks about like you’d expect:

    Ring Light Mount - solid model - top
    Ring Light Mount – solid model – top

    Those two solid models show the small hole for the LED ring wiring, which I drilled into the as-printed plastic. The original layout included just the LED ring, with the wire through a big central hole, but then I realized the wall wart had enough moxie for a few more LEDs. So it goes.

    Anyhow, the lamp provides just enough illumination below my big monitors to suffice. The gooseneck might not be quite long enough, but that’ll be another project…

    The OpenSCAD source code:

    // LED Ring Light Mount
    // Ed Nisley KE4ZNU October 2015
    
    DoSupport = true;
    
    //- Extrusion parameters must match reality!
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    inch = 25.4;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    //----------------------
    // Dimensions
    
    NumSides = 8*4;						// number of sides on each "cylinder"
    
    LENGTH = 0;
    ID = 1;
    OD = 2;
    
    Shade = [6.0,45.2,47.5];			// threaded end of OEM lamp shade
    RingLED = [4.5,36.0,51.0];
    
    SpotLED = [2.0,0,5.0];				// discrete LEDs in center
    NumSpots = 8;						// discrete LEDs around the one in the middle
    
    Support = [(RingLED[LENGTH] - 1*ThreadThick),0,(RingLED[OD] - 4*ThreadWidth)];
    NumSupports = NumSides/2;
    
    ThreadBase = RingLED[LENGTH] + SpotLED[LENGTH];
    OAHeight = ThreadBase + Shade[LENGTH];
    
    //----------------------
    // 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);
    }
    
    //----------------------
    // Build it
    
    	difference() {
    		union() {																				// overall shape
    			translate([0,0,ThreadBase])
    				rotate_extrude(convexity = 2, $fn=NumSides)
    					translate([Shade[OD]/2,0])
    						circle(r=Shade[LENGTH],$fn=NumSides);
    			cylinder(d=(Shade[OD] + 2*Shade[LENGTH]),h=ThreadBase,$fn=NumSides);
    			translate([0,0,ThreadBase])
    				cylinder(d=Shade[OD],h=Shade[LENGTH],$fn=NumSides);
    		}
    		
    		translate([0,0,ThreadBase - Protrusion])
    			cylinder(d=(Shade[ID] + HoleWindage),h=(Shade[LENGTH] + 2*Protrusion),$fn=NumSides);	// opening for shade thread
    			
    		translate([0,0,-Protrusion])
    			cylinder(d=(RingLED[OD] + HoleWindage),h=(RingLED[LENGTH] + Protrusion),$fn=NumSides);	// opening for LED ring
    			
    		rotate(180/NumSides)																		// LED ring power wire
    			translate([RingLED[ID]/2,0,0])
    				rotate(180/6)
    					PolyCyl(2.5,OAHeight,6);
    			
    		rotate(180/8  - 180/NumSides)
    			PolyCyl(SpotLED[OD],OAHeight,8);														// central LED SpotLED
    			
    		for (i=[0:NumSpots-1])																		// surrounding spots
    			rotate(i*360/NumSpots - 180/NumSides)
    				translate([(RingLED[ID] - 2*SpotLED[OD])/2,0,0])
    						rotate(180/8)
    							PolyCyl(SpotLED[OD],OAHeight,8);
    	}
    	
    //-- Support structure
    
    	if (DoSupport)
    		color("Yellow")
    		rotate(180/NumSides)													// align bars to flat internal faces
    			for (i=[0:NumSupports/2 - 1]) {
    				rotate(i * 360 / NumSupports)
    					translate([0,0,Support[LENGTH]/2])
    						cube([Support[OD],4*ThreadWidth,Support[LENGTH]],center=true);
    			}
    
    
  • Monthly Image: A Year in the Life of a Maple Twig, Resurrected

    In 1991 we lived in Tolland CT, where I took one picture of a maple twig every week:

    This slideshow requires JavaScript.

    That was with a film camera, of course, with negatives. I assembled the printed images into a poster and eventually (perhaps in 2001) scanned / digitally photographed them four-at-a-time, saved the result as a 330 MB Photoshop file with one 2×2 group in each of 13 layers (there are 50 images, probably because vacations), and burned that to a CD.

    All I can say: it must have made sense at the time.

    Anyhow, here in the future, I found that CD in a pile destined for the shredder, which shouldn’t ought to happen without some attention.

    Here’s how I extracted the separate images from that file into standalone JPEGs, cropped them to a uniform size, and smushed them to suitably low quality:

    convert A\ Year\ in\ the\ Life\ of\ Tolland\ CT\ -\ 1991.psd -quality 95 Tolland-1991-%02d.jpg
    for f in {01..13} ; do convert Tolland-1991-$f.jpg -crop "1212x1775+0+0" img-$f-0.jpg ; done
    for f in {01..13} ; do convert Tolland-1991-$f.jpg -crop "1212x1775+1212+0" img-$f-1.jpg ; done
    for f in {01..13} ; do convert Tolland-1991-$f.jpg -crop "1212x1775+0+1775" img-$f-2.jpg ; done
    for f in {01..13} ; do convert Tolland-1991-$f.jpg -crop "1212x1775+1212+1775" img-$f-3.jpg ; done
    for f in {01..13} ; do for g in {0..3} ; do convert img-$f-$g.jpg -crop "1100x1650+50+50" out-$f-$g.jpg ; done ; done
    sn=1 ; for f in {01..13} ; do for g in {0..3} ; do printf -v dn 'Tolland-1991-Maple-%02d.jpg' "$(( sn++ ))" ; convert img-$f-$g.jpg -crop "1100x1650+50+50" +repage -rotate 90 -define jpeg:extent=200KB $dn ; done ; done
    

    Then WordPress assembles the 50 images into a slide show.

    Of course, it didn’t go quite as smoothly as all that, but it took maybe half an hour of fiddling to get it right by iterating on the commands until I liked the results. One might tweak the exposures and suchlike, but that’s in the nature of fine tuning.

    Now I can shred the CD…

  • Beckman DM73 Circuitmate Multimeter: Ground Clip

    Among the many treasures Mad Phil left me was a Beckman DM73 CircuitMate multimeter (manual at the Boat Anchor Manual Archive):

    Beckman DM73 - new ground clip
    Beckman DM73 – new ground clip

    Although it’s rated to 500 V, it violates the fundamental principle of high-voltage electronics debugging:

    Always keep one hand in your pocket!

    The scorched and truncated probe tip on the “ground wire” shows Phil slipped at least once:

    Beckman DM73 - probe tip
    Beckman DM73 – probe tip

    After far too long, I sacrificed a black multimeter probe from the heap, soldered an alligator clip on the end, and, henceforth, will use it appropriately. Mostly, I never do any high-voltage work, but you never know.

    I suppose I should splice that nice black probe onto the end of the Beckman wire for low-voltage work…

  • Sony HDR-AS30V Audio Mute

    The Sony HDR-AS30V has extremely high audio gain, which is precisely what you need for the mic on an action camera. It sends that audio, along with the video, through its HDMI output, so when you drive a display from the camera in enclosed space, the audio is REALLY LOUD and causes severe feedback. For obscure reasons, given the staggering cost of the venue’s AV system, there’s no way to mute the audio channel of the video input when you’re also using a mic attached to someone giving a presentation.

    The obvious solution, a shorted jumper (formerly an earbud plug) in the external mic jack, looked like this:

    Sony HDR-AS30V - Dummy external mic
    Sony HDR-AS30V – Dummy external mic

    Contrary to what I expected, the camera doesn’t disable the internal mic with the jumper in place. The amp probably uses an analog multiplexer, rather than a mechanical switch, and even an off-channel isolation of, say, 76 dB (from the MAX4544 spec, for example) isn’t enough to completely mute the mic. You could, given sufficient motivation, measure the actual isolation, but the surviving audio isn’t subtle at all.

    The not-obvious solution turned out to be putting the camera into either single or interval photo mode, rather than the movie mode I use for bike rides. It seems that when the video format doesn’t require audio, the camera either disables the audio inputs or (more likely) just doesn’t include audio data in the HDMI output.

    Which produces exactly what I want: a video output with no accompanying audio.

  • Improved Gas Cartridge Fins

    A trio of N2O cartridges / capsules made their way into the Basement Laboratory and cried out to be fitted with fins:

    N2O Capsule Fins - installed
    N2O Capsule Fins – installed

    My original model tinkered up a cartridge from solid object primitives, but I’ve since discovered that cheating produces a much better and faster and easier result for cylindrical objects:

    N2O Capsule - solid model - bottom view
    N2O Capsule – solid model – bottom view

    The trick is getting an image of the original object from the side, taken from far enough away to flatten the perspective:

    N2O capsule - side view
    N2O capsule – side view

    Then overlay and scale a grid to match the actual length:

    N2O capsule - grid overlay
    N2O capsule – grid overlay

    The grid has 1 mm per minor square, centered along the cartridge’s axis, and zeroed at the tip; I rotated the cartridge image by half a degree to line it up with the grid.

    Print it out on actual paper so you can eyeball the measurements and write ’em where you need ’em:

    N2O capsule - grid overlay - printed
    N2O capsule – grid overlay – printed

    Which becomes an OpenSCAD polygon definition:

    RADIUS = 0;				// subscript for radius values
    HEIGHT = 1;				//   ... height above Z=0 at seal flange
    
    //-- N2O 8 g capsule
    
    CartridgeOutline = [			// X values = measured radius, Y as distance from tip
    	[0.0,0.0],					//  0 cartridge seal tip
    	[2.5,0.1],					//  1 seal disk
    	[3.5,0.5],[4.0,1.0],		//  2 tip end
    	[4.2,2.0],[4.3,3.0],		//  4 tip
    	[4.3,6.0],					//  6 chamfer
    	[4.5,8.0],					//  7 taper
    	[4.9,9.0],					//  8
    	[5.5,10.0],					//  9
    	[6.0,11.0],					// 10
    	[6.7,12.0],					// 11
    	[7.1,13.0],					// 12
    	[7.5,14.0],					// 13
    	[8.0,15.0],					// 14
    	[8.4,16.0],					// 15
    	[8.8,17.0],					// 16
    	[9.0,18.0],[9.0,58.0],		// 17 body
    	[0.0,65.0]					// 19 dummy end cone
    	];
    
    TipLength = CartridgeOutline[6][HEIGHT];
    TipOD = 2*CartridgeOutline[5][RADIUS];
    	
    BodyOD = 2*CartridgeOutline[17][RADIUS];
    BodyOAL = CartridgeOutline[19][HEIGHT];
    

    Because the rounded end of the cartridge doesn’t matter, I turned it into a cone.

    Twirl that around the Z axis and It Just Works:

    module Cartridge() {
    
     rotate_extrude($fn=CartridgeSides)
     polygon(points=CartridgeOutline);
    
    }
    

    Which then punches a matching dent in the fin structure:

    Gas Capsule Fins - Slic3r preview
    Gas Capsule Fins – Slic3r preview

    The lead picture doesn’t quite match the Slic3r preview, as I found the single-width diagonal fins weren’t strong enough. Making them two (nominal) threads wide lets Slic3r lay down three thinner threads in the same space:

    Gas Capsule Fins - thicker - Slic3r preview
    Gas Capsule Fins – thicker – Slic3r preview

    That’s letting Slic3r automagically determine the infill and perimeter thread width to make the answer come out right. As nearly as I can tell, the slicing algorithms have become smart enough to get the right answer nearly all of the time, so I can-and-should relinquish more control over the details.

    The OpenSCAD source code:

    // CO2 capsule tail fins
    // Ed Nisley KE4ZNU - October 2015
    
    Layout = "Build"; // Show Build FinBlock Cartridge Fit
    
    //-------
    //- Extrusion parameters must match reality!
    // Print with +0 shells and 3 solid layers
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1; // make holes end cleanly
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    //-------
    // Capsule dimensions
    
    CartridgeSides = 12*4; // number of sides
    
    RADIUS = 0; // subscript for radius values
    HEIGHT = 1; // ... height above Z=0 at seal flange
    
    //-- N2O 8 g capsule
    
    RW = HoleWindage/2; // enlarge radius by just enough
    
    CartridgeOutline = [ // X values = measured radius, Y as distance from tip
     [0.0,0.0], // 0 cartridge seal tip
     [2.5 + RW,0.1], // 1 seal disk
     [3.5 + RW,0.5],[4.0 + RW,1.0], // 2 tip end
     [4.2 + RW,2.0],[4.3 + RW,3.0], // 4 tip
     [4.3 + RW,6.0], // 6 chamfer
     [4.5 + RW,8.0], // 7 taper
     [4.9 + RW,9.0], // 8
     [5.5 + RW,10.0], // 9
     [6.0 + RW,11.0], // 10
     [6.7 + RW,12.0], // 11
     [7.1 + RW,13.0], // 12
     [7.5 + RW,14.0], // 13
     [8.0 + RW,15.0], // 14
     [8.4 + RW,16.0], // 15
     [8.8 + RW,17.0], // 16
     [9.0 + RW,18.0],[9.0 + RW,58.0], // 17 body
     [0.0,65.0] // 19 dummy end cone
     ];
    
    TipLength = CartridgeOutline[6][HEIGHT];
    TipOD = 2*CartridgeOutline[5][RADIUS];
    
    CylinderBase = CartridgeOutline[17][HEIGHT];
    
    BodyOD = 2*CartridgeOutline[17][RADIUS];
    BodyOAL = CartridgeOutline[19][HEIGHT];
    
    //-------
    // Fin dimensions
    
    FinThick = 1.5*ThreadWidth; // outer square
    StrutThick = 2.0*ThreadWidth; // diagonal struts
    
    FinSquare = 1.25*BodyOD;
    FinTaperLength = sqrt(2)*FinSquare/2 - sqrt(2)*FinThick - ThreadWidth;
    
    FinBaseLength = 0.7 * CylinderBase;
    FinTop = 0.9*CylinderBase;
    
    //-------
    
    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() {
    
     rotate_extrude($fn=CartridgeSides)
     polygon(points=CartridgeOutline);
    
    }
    
    //-------
    // Diagonal fin strut
    
    module FinStrut() {
     intersection() {
     rotate([90,0,45])
     translate([0,0,-StrutThick/2])
     linear_extrude(height=StrutThick)
     polygon(points=[
     [0,0],
     [FinTaperLength,0],
     [FinTaperLength,FinBaseLength],
     [0,(FinBaseLength + FinTaperLength)]
     ]);
     translate([0,0,FinTop/2])
     cube([2*FinSquare,2*FinSquare,FinTop], center=true);
     }
    }
    
    //-------
    // Fin outline
    
    module FinBlock() {
     
    $fn=12;
     render(convexity = 4)
     union() {
     translate([0,0,FinBaseLength/2])
     difference() {
     intersection() {
     minkowski() {
     cube([FinSquare - 2*ThreadWidth,
     FinSquare - 2*ThreadWidth,
     FinBaseLength],center=true);
     cylinder(r=FinThick,h=Protrusion,$fn=8);
     }
     cube([2*FinSquare,2*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=2*StrutThick,h=(FinBaseLength + 2*Protrusion),center=true,$fn=16);
     }
     }
     
     for (Index = [0:3])
     rotate(Index*90)
     FinStrut();
     
     rotate(180/12)
     cylinder(d=IntegerMultiple(TipOD + 6*ThreadWidth,ThreadWidth),h=TipLength);
     }
    }
    
    //-------
    // Fins
    
    module FinAssembly() {
    
     difference() {
     FinBlock();
     translate([0,0,2*ThreadThick]) // add two layers 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 == "FinStrut")
     FinStrut();
    
    if (Layout == "FinBlock")
     FinBlock();
    
    if (Layout == "Cartridge")
     Cartridge();
    
    if (Layout == "Show") {
     FinAssembly();
     color("LightYellow") Cartridge();
    }
    
    if (Layout == "Fit")
     FinFit();
    
    if (Layout == "Build")
     FinAssembly();
    
  • HP 7475A Plotter: Inmac Ball-Point Pen Tweakage

    The big bag o’ new-old-stock Inmac ball-point plotter pens had five different colors, so I popped a black ceramic tip pen in Slot 0 and ran off Yet Another Superformula Demo Plot:

    HP 7475A - Inmac ball pens - weak blue
    HP 7475A – Inmac ball pens – weak blue

    All the ball pens produce spidery lines, but the blue pen seemed intermittent. Another blue pen from the bag behaved the same way, so I pulled the tip outward and tucked a wrap of fine copper wire underneath. You can see the wire peeking out at about 5 o’clock, with the end at 3-ish:

    HP 7475A - Inmac ball pen - wire spacer
    HP 7475A – Inmac ball pen – wire spacer

    The wire holds the tip slightly further away from the locating flange and, presumably, makes it press slightly harder against the paper:

    HP 7475A - Inmac ball pen - stock vs. extended
    HP 7475A – Inmac ball pen – stock vs. extended

    A bit more pressure helped, but not enough to make it dependable, particularly during startup on the legend characters:

    HP 7475A - Inmac ball pens - extended blue
    HP 7475A – Inmac ball pens – extended blue

    That black line comes from an ordinary fiber-tip pen that looks like a crayon on a paper towel by comparison with the hair-fine ball point lines.

    Delicacy doesn’t count for much in these plots, so I’ll save the ball pens for special occasions. If, that is, I can think of any…

  • Victoreen 710-104 Ionization Chamber: Shield Planes and Batteries

    An undrilled double-sided circuit board with the edges bonded together doesn’t look like much:

    Electrometer amp - undrilled shield planes
    Electrometer amp – undrilled shield planes

    Soldering a smaller hex to the center of the bonded plate produces an isolated plane:

    Electrometer amp - finished shield planes
    Electrometer amp – finished shield planes

    The copper fabric tape wrapped around a brass tube soldered to the isolated plane contacts the ionization chamber shell around the central contact and (should) provide complete shielding. Kapton tape around the edges reduces the likelihood of inadvertent shorts.

    Working with a shield at +24 V gave me the shakes, so this one confines the chamber bias to the isolated hex and shell, with the larger hex at circuit common (a.k.a. ground). The isolated plane has about 275 pF to the ground plane, which isn’t a Bad Thing at all. In principle, the chamber bias doesn’t need a switch, because there’s no current drain, but I vastly prefer having cold circuitry before popping the lid.

    If I had a small DPST switch, I’d use it:

    Electrometer amp - chamber - shield planes
    Electrometer amp – chamber – shield planes

    As it stands, one switch controls the +24 V chamber bias and the other switches +12 V power to the electrometer amp front end, with simpleminded connectors so I can separate the pieces.

    We’ll see how well all that works in practice.

    An alert reader will notice the tiny difference between the blue PETG shapes in the two pictures. The bottom one comes from the revised code, of course.