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

  • Vacuum Tube LEDs: Platter Chassis

    Given that Greenlee chassis punches fit the ersatz vacuum tube sockets, this makes a certain perverse sense:

    Greenlee puch on hard drive platter
    Greenlee puch on hard drive platter

    This set of punches is probably worth its weight in, uh, tool steel, because Greenlee got out of the Radio Chassis Punch business quite a while ago:

    Greenlee 730 Radio Chassis Punch Assortment
    Greenlee 730 Radio Chassis Punch Assortment

    As far as a Greenlee punch is concerned, a hard drive platter looks a lot like thin aluminum sheet:

    Greenlee punched drive platter
    Greenlee punched drive platter

    I lathe-turned that white bushing to align the hard drive platter around the screw inside the punch. The right way to make that bushing in this day & age definitely involves 3D printing, but I was standing next to the lathe and spotted a nylon rod in the remnants bucket underneath.

    The inner ring crumples around the bushing inside the die, while the platter outside remains flat & undamaged through the entire experience.

    I match-marked the socket & “plate cap lead” holes on the punched platter and introduced it to Mr Drill Press, but the right way to do that for more than one socket / plate involves a Sherline mill fixture and some CNC.

    And then It Just Worked:

    Vacuum Tube LEDs - IBM 21HB5A drive platter socket
    Vacuum Tube LEDs – IBM 21HB5A drive platter socket

    That’s obviously a proof of concept; the socket rests on the desk with the rest of the tubes / sockets / Neopixels tailing off to the right. The plate cap lead should pass through a brass tube fitting on the platter, just for pretty.

    The 7- and 9-pin sockets have a raised disk that’s slightly smaller than the 25 mm hard drive hole; the easiest fix involves slightly enlarging the disk to match the hole. Although CDs / DVDs have a 15 mm hole and Greenlee punches work surprisingly well on polycarbonate, if I’m going to CNC-drill the screw / wire holes anyway, CNC milling the middle hole should go quickly and eliminate a messy manual process.

    Come to think of it, that big tube would look better in the middle of a DVD amid all those nice diffraction patterns from the RGB LEDs in the cap…

     

  • Improved Lip Balm / Lipstick Holder

    Mary asked for a less angular version of the Lip Balm Holder, which gave me a chance to practice my list comprehension:

    Improved Lipstick and Balm Holder
    Improved Lipstick and Balm Holder

    You hand the OpenSCAD program a list of desired tube diameters in the order you want them, the program plunks the first one (ideally, the largest diameter) in the middle, arranges the others around it counterclockwise from left to right, then slips a lilypad under each tube.

    As long as you don’t ask for anything egregiously stupid, the results look reasonably good:

    Improved Lipstick and Balm Holder - 8 tubes
    Improved Lipstick and Balm Holder – 8 tubes

    As before, each tube length is 1.5 times its diameter; the lipsticks / balms fit loosely and don’t flop around.

    Given the tube diameters and the wall thickness, list comprehensions simplify creating lists of the radii from the center tube to each surrounding tube, the center-to-center distances between each of the outer tubes, and the angles between successive tubes:

    // per-tube info, first element forced to 0 to make entries match RawDia vector indexes
    
    Radius = [0, for (i=[1:NumTubes-1]) (TubeRad[0] + TubeRad[i] + Wall)];			// Tube[i] distance to center pointRadius = [0, for (i=[1:NumTubes-1]) (TubeRad[0] + TubeRad[i] + Wall)];			// Tube[i] distance to center point
    echo(str("Radius: ",Radius));
    
    CtrToCtr = [0, for (i=[1:NumTubes-2]) (TubeRad[i] + TubeRad[i+1] + Wall)];		// Tube[i] distance to Tube[i+1]
    echo(str("CtrToCtr: ",CtrToCtr));
    
    Angle = [0, for (i=[1:NumTubes-2]) acos((pow(Radius[i],2) + pow(Radius[i+1],2) - pow(CtrToCtr[i],2)) / (2 * Radius[i] * Radius[i+1]))];
    echo(str("Angle: ",Angle));
    
    TotalAngle = sumv(Angle,len(Angle)-1);
    echo(str("TotalAngle: ",TotalAngle));
    

    The angles come from the oblique triangle solution when you know all three sides (abc) and want the angle (C) between a and b:

    C = arccos( (a2 + b2 - c2) / (2ab) )

    Peering down inside, the Slic3r preview shows the lily pads are the tops of squashed spheres:

    Improved Lipstick and Balm Holder - Slic3r preview
    Improved Lipstick and Balm Holder – Slic3r preview

    The pads are 2.0 times the tube diameter, which seemed most pleasing to the eye. They top out at 2.0 mm thick, which might make the edges too thin for comfort.

    Update: Here’s what it looks like with a convex hull wrapped around all the lilypads:

    Improved Lipstick and Balm Holder - hulled base
    Improved Lipstick and Balm Holder – hulled base

    I’m awaiting reports from My Spies concerning the typical diameter(s) of lipstick tubes, then I’ll run off a prototype and see about the lily pad edges.

    The OpenSCAD source code as a GitHub gist:

    // Lipstick and Balm Tube Holder
    // Ed Nisley KE4ZNU – February 2016
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //——
    // Dimensions
    RawDia = [26,21,19,17,19,21]; // actual tube diameters in desired order, center = largest first
    NumTubes = len(RawDia);
    Clearance = 2.0;
    TubeDia = [for (i=[0:NumTubes-1]) (RawDia[i] + Clearance)]; // actual tube diameters
    TubeRad = TubeDia / 2;
    echo(str("NumTubes: ",NumTubes));
    Wall = 2.0;
    BaseThick = 2.0;
    BaseFactor = 2.0;
    NumSides = 8*4;
    // per-tube info, first element forced to 0 to make entries match RawDia vector indexes
    Radius = [0, for (i=[1:NumTubes-1]) (TubeRad[0] + TubeRad[i] + Wall)]; // Tube[i] distance to center point
    echo(str("Radius: ",Radius));
    CtrToCtr = [0, for (i=[1:NumTubes-2]) (TubeRad[i] + TubeRad[i+1] + Wall)]; // Tube[i] distance to Tube[i+1]
    echo(str("CtrToCtr: ",CtrToCtr));
    Angle = [0, for (i=[1:NumTubes-2]) acos((pow(Radius[i],2) + pow(Radius[i+1],2) – pow(CtrToCtr[i],2)) / (2 * Radius[i] * Radius[i+1]))];
    echo(str("Angle: ",Angle));
    TotalAngle = sumv(Angle,len(Angle)-1);
    echo(str("TotalAngle: ",TotalAngle));
    //———————-
    // Useful routines
    // vector sum cribbed from doc
    function sumv(v,i,s=0) = (i==s ? v[i] : v[i] + sumv(v,i-1,s));
    //———————-
    //- Build it
    for (i=[0:NumTubes-1])
    rotate(90 – TotalAngle/2 + sumv(Angle, (i>0) ? (i-1) : 0))
    translate([Radius[i],0,0]) {
    resize([0,0,2*BaseThick]) // bases
    difference() {
    sphere(r=BaseFactor*TubeRad[i],$fn=NumSides);
    translate([0,0,-BaseFactor*TubeDia[i]])
    cube(2*BaseFactor*TubeDia[i],center=true);
    }
    difference() { // tubes
    cylinder(r=TubeRad[i] + Wall,h=1.5*TubeDia[i] + BaseThick,$fn=NumSides);
    cylinder(d=TubeDia[i],h=1.5*TubeDia[i] + BaseThick + Protrusion,$fn=NumSides);
    }
    }
  • Improved Fireball Cocoa Recipe

    It turns out that non-alkalized / non-Dutch-process cocoa has a much lower surface energy than good old Hershey’s, to the extent that my Fireball Cocoa Recipe produces powder bombs, even after far more stirring than I’m willing to exert.

    The trick is to stir the mud for a while, then let it set for 15 minutes:

    Cocoa mud
    Cocoa mud

    That apparently gives the cocoa time to get along with the milk and join forces. Stir it up again, let it sit for a few minutes, then proceed with the recipe: smooooth cocoa with no powder bombs.

    A bit more Vietmamese cinnamon is no bad thing, either …

  • Removing Old Kernels

    Mostly, I don’t worry about the accumulation of old kernels building up in /boot and sudo apt-get autoremove may scrub most of them, but sometimes it doesn’t when I’m doing something else and I must wade through the accumulation of old packages in Synaptic. Removing all those packages by hand gets tedious, but I’m reluctant to unleash a rarely used script on the clutter for fear of creating a worse problem.

    The iterator in this burst of Bash line noise:

    for f in $(ls /boot | grep vmlinuz | cut -d\- -f2,3 | sort | head -n -1) ; do dpkg -l | grep "^ii\ \ linux-" | grep $f | cut -d" " -f 3 >> /tmp/pkgs.txt ; done
    

    … parses the list of kernels in /boot into version numbers, finds the corresponding installed packages, sorts them in ascending order, discards the last entry so as to not uninstall the most recent kernel, and passes each line of the resulting list into the loop.

    N.B: The grep argument has two spaces after the ii that WordPress would destroy without the escaping backslashes. You can try "^ii linux-", but if the loop puts nothing in the file, that’s why.

    Given each kernel version number, the loop extracts the package names from the installed kernel packages and glues the result onto a file that looks like this:

    cat /tmp/pkgs.txt
    linux-headers-3.13.0-73
    linux-headers-3.13.0-73-generic
    linux-image-3.13.0-73-generic
    linux-image-extra-3.13.0-73-generic
    linux-headers-3.13.0-74
    linux-headers-3.13.0-74-generic
    linux-image-3.13.0-74-generic
    linux-image-extra-3.13.0-74-generic
    linux-headers-3.13.0-76
    linux-headers-3.13.0-76-generic
    linux-image-3.13.0-76-generic
    linux-image-extra-3.13.0-76-generic
    

    Convert that file into a one-line string of package names and verify what would happen:

    paste -s -d " " /tmp/pkgs.txt | xargs sudo apt-get --dry-run purge
    

    If everything looks good, change --dry-run to --yes and blow ’em away.

    No, I can’t possibly remember or type that gibberish by hand, but I do know where to find it…

  • Knurled Inch Inserts

    Tagging along behind the metric inserts, a sack of knurled brass inch-size screw inserts arrived:

    Threaded Inserts - metric and inch
    Threaded Inserts – metric and inch

    The nice stainless steel screws on the right range from 4-40 to 10-32, which suffice for nearly everything I build around here.

    Unlike the splined metric inserts on the left, these inserts have actual knurls and ridges that should hold them firmly in place. The specs give hard-inch dimensions, of course, that (seem to) correspond to the root diameter of the knurls. You can find nice engineering drawings of precise tapered holes (by drilling down into the Heat-Set Inserts for Plastics item on that page), but a few metric measurements of the actual parts on hand should suffice for my simple needs.

    Thread: overall length x small rim OD x (knurl length x larger knurl OD)

    • 4-40: 5.8 x 3.9 x (4.0 x 4.6)
    • 6-32: 7.1 x 4.7 x (4.6 x 5.5)
    • 8-32: 8.1 x 5.5 x (5.9 x 6.3)
    • 10-32: 9.5 x 6.3 x (7.0 x 7.1)

    Rather than fussing with a tapered hole, just punch a cylinder with the small rim OD (to clear the screw) through the part and put a cylinder with the knurl OD x length at the surface.

    Using cylinders without diameter correction will make them slightly undersized for heat bonding. The usual 3D printing tolerances don’t justify anything fussier than that.

    Using PolyCyl diameter correction will make the holes nearly spot on for epoxy bonding: butter ’em up, ram ’em in, pause for curing, done.

    That’s the plan, anyhow…

  • Sears Sewing Table Hinge Covers

    The extension surfaces on the Sears sewing table in the Basement Sewing Room unfold from the top, leaving the hinges exposed:

    Sears Sewing Table - hinge
    Sears Sewing Table – hinge

    Alas, quilts snag on the squared-off ends of the hinges, a situation that is not to be tolerated…

    This protective cap isn’t as small as we’d like, but it must be that thick to cover the hinge, that long to cover the squared-off ends, and that wide for symmetry:

    Sears Sewing Table Hinge Cover - solid model
    Sears Sewing Table Hinge Cover – solid model

    Two neodymium magnets fit in the holes and secure the cover to the all-steel “bronzed” hinges:

    Sears Sewing Table - hinge covers
    Sears Sewing Table – hinge covers

    We’re not sure how well that will work in the long term, but early returns seem promising.

    It could be slightly narrower left-to-right and maybe fewer vertices should be oriented differently.

    The OpenSCAD source code as a GitHub gist:

    // Vacuum Tube LED Lights
    // Ed Nisley KE4ZNU January 2016
    //- Extrusion parameters must match reality!
    ThreadThick = 0.20;
    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
    Hinge = [7.0,52.0,6.0];
    TopThick = 3*ThreadThick;
    PlateThick = Hinge[2] + TopThick;
    NumSides = 8*4;
    //———————-
    // 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() {
    hull()
    for (a=[0:7])
    rotate(a*360/8)
    translate([Hinge[1]/2,0,0])
    scale([1.5,1.5,1])
    sphere(r=PlateThick,$fn=NumSides);
    hull()
    for (k=[-1,1])
    translate([0,Hinge[1]/2,k*(Hinge[2] – Hinge[0]/2)])
    rotate([90,0,0]) rotate(180/8)
    PolyCyl(Hinge[0],Hinge[1],8);
    for (i=[-1,1])
    translate([i*Hinge[1]/2,0,-Protrusion])
    PolyCyl(4.8,2.5 + Protrusion,8);
    translate([0,0,-PlateThick])
    cube(2*[Hinge[1],Hinge[1],PlateThick],center=true);
    }
  • Raspberry Pi: Jessie Lite Setup for Streaming Audio

    As a first pass at a featureless box that simply streams music from various sources, I set up a Raspberry Pi with a Jessie Lite Raspbian image. I’m mildly astonished that they use dd to transfer the image to the MicroSD card, but it certainly cuts out a whole bunch of felgercarb that comes with a more user-friendly interface.

    I used dcfldd (for progress reports while copying) and verify the copied image:

    sudo dcfldd statusinterval=10 bs=4M if=/mnt/diskimages/ISOs/Raspberry\ Pi/2015-11-21-raspbian-jessie-lite.img of=/dev/sdb
    sudo dcfldd statusinterval=10 bs=4M if=/dev/sdb of=/tmp/rpi.img count=350
    truncate --reference /mnt/diskimages/ISOs/Raspberry\ Pi/2015-11-21-raspbian-jessie-lite.img /tmp/rpi.img
    diff -s /tmp/rpi.img /mnt/diskimages/ISOs/Raspberry\ Pi/2015-11-21-raspbian-jessie-lite.img
    

    That fits neatly on a minuscule 2 GB MicroSD card:

    df -h
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/root       1.8G  1.1G  549M  67% /
    devtmpfs        214M     0  214M   0% /dev
    tmpfs           218M     0  218M   0% /dev/shm
    tmpfs           218M  4.5M  213M   3% /run
    tmpfs           5.0M  4.0K  5.0M   1% /run/lock
    tmpfs           218M     0  218M   0% /sys/fs/cgroup
    /dev/mmcblk0p1   60M   20M   41M  34% /boot
    

    Set the name of the Raspberry Pi to something memorable, perhaps streamer1.

    Disable IPV6, because nothing around here supports it, by tweaking /etc/modprobe.d/ipv6.conf:

    alias ipv6 off
    

    Enable the USB WiFi dongle by adding network credentials to /etc/wpa_supplicant/wpa_supplicant.conf:

    ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
    update_config=1
    
    network={
     ssid="your network SSID goes here"
     psk="pick your own"
    }
    

    Nowadays, there’s no need for a fixed IP address, because after adding your public key to the (empty) list in ~/.ssh/authorized_keys, you can sign in using a magic alias:

    ssh -p12345 pi@streamer1.local
    

    I have absolutely no idea how that works, nor how to find out. If it ever stops working, I’m doomed.

    The Raspberry Pi Model B+ has “improved” audio that, to Mary’s ears, comes across as pure crap; even my deflicted ears can hear low-level hissing and bad distortion at moderate volumes. An old Creative Labs Sound Blaster USB box sidesteps that problem, but requires a tweak in /etc/asound.conf to route the audio to the proper destination:

    # Make USB sound gadget the default output
    
    pcm.!default {
     type hw card 1
    }
    ctl.!default {
     type hw card 1
    }
    

    ALSA then seems to default to the wrong channel (or something), although this tweak in the middle of /usr/share/alsa/alsa.conf may not be needed:

    #pcm.front cards.pcm.front
    pcm.front cards.pcm.default
    

    Good old mplayer seems to handle everything involved in streaming audio from the Interwebs.

    Set up blank /etc/mplayer/input.conf and ~/.mplayer/input.conf files to eliminate kvetching:

    # Dummy file to quiet the "not found" error message
    

    Set up ~/.mplayer/config thusly:

    prefer-ipv4=true
    novideo=true
    #ao=alsa:device=hw=1.0
    ao=alsa
    format=s16le
    #mixer-channel=Master
    softvol=true
    volume=25
    quiet=true
    

    The commented-out ao option will force the output to the USB gadget if you want to route the default audio to the built-in headphone jack or HDMI output.

    Telling mplayer to use its own software volume control eliminates a whole bunch of screwing around with the ALSA mixer configuration.

    The quiet option silences the buffer progress display, while still showing the station ID and track information.

    With that in hand, the Public Domain Project has a classical music stream that is strictly from noncommercial:

    mplayer -playlist http://relay.publicdomainproject.org/classical.aac.m3u
    

    Send them a sack of money if you like them as much as we do.

    By contrast, the local NPR station comes across as talk radio:

    mplayer http://live.str3am.com:2070/wmht1
    

    You can’t feed nested playlists into mplayer, but fetching the contents of the stream playlists produces a one-station-per-line playlist file that one might call RadioList.txt:

    http://relay.publicdomainproject.org:80/classical.aac
    http://relay.publicdomainproject.org:80/jazz_swing.aac
    http://live.str3am.com:2070/wmht1
    

    So far, I’ve been manually starting mplayer just to get a feel for reliability and suchlike, but the setup really needs an autostart option with some user-friendly way to select various streams, plus a way to cleanly halt the system. A USB numeric keypad may be in order, rather than dinking around with discrete buttons and similar nonsense.

    There exists a horrible hack to transfer the stream metadata from mplayer onto an LCD, but I’m flat-out not using PHP or Perl. Perhaps the Python subprocess management module will suffice to auto-start a Python program that:

    • starts mplayer with the default playlist
    • parses mplayer’s piped output
    • updates the LCD accordingly
    • reads / translates keypad input

    This being a Pi, not an Arduino, one could actually use a touchscreen LCD without plumbing the depths of absurdity, but that starts looking like a lot of work…