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

  • Chipmunk Gibbage

    Mary found the north end of a southbound chipmunk just outside the garden gate, at the foot of the utility pole that often serves as a hawk perch:

    Chipmunk tail tip
    Chipmunk tail tip

    Shortly thereafter, she found piles of gibbage atop the retaining wall by the basement door:

    Raptor vs. Rodent gibbage
    Raptor vs. Rodent gibbage

    It looks too loose for an owl pellet, but hawks also blurp up the indigestible bits. We have definitely have a pair of Cooper’s Hawks nesting in the area again; most likely, this is what’s left of the south end of that chipmunk.

    The next morning, we had a feeding frenzy out there:

    Raptor vs. Rodent gibbage - feeding frenzy
    Raptor vs. Rodent gibbage – feeding frenzy

    I’m not sure if the snail over on the right is a participant or a bystander. It’s certainly outclassed by the slugs, which are basically soft-shell snails.

    As dBm points out, nothing goes to waste in Nature:

    Raptor vs. Rodent gibbage - cleanup squad
    Raptor vs. Rodent gibbage – cleanup squad

    After the crowd left and the remains dried out a bit, one chunk had a tuft of brown-tipped fur with gray roots that definitely looks like it came from a chipmunk.

    Good work, hawks: go, go, go!

  • M2 Platform Alignment Check

    As part of setting the Makergear M2 up after The Great Cleanout, I ran off a set of thinwall calibration squares that showed the left rear corner was high by 0.12 mm: the square in that corner measured 2.88 mm, rather than the intended 3.0 mm. The walls were 0.43 mm, about 10% above the nominal 0.40 mm.

    That’s similar in both absolute values and alignment to the measurements from two months back.

    I tightened the rear platform screw by a bit under 1/12 turn, less than half a flat on the hex nut, and dialed the Extrusion Multiplier back by 10%. The next set of squares, set up for walls made of three parallel threads, came out with heights within 0.08 mm of each other and 1.15 mm thick (rather than the nominal 1.20 mm).

    They’re 40 mm on a side, mostly to produce bigger handouts for the next show-n-tell:

    Thinwall open box - array on platform - 3w 40 3.0
    Thinwall open box – array on platform – 3w 40 3.0

    Letting it sit for a few days and running the same G-Code produced heights within 0.07 mm and wall thickness at 1.18, which I defined to be Good Enough.

    Recent versions of Slic3r have been adjusting the various thread widths on the fly, as I’ve let everything except the basic extrusion width go with the default values. As a result, setting the wall width to 2 threads (0.80 mm) can produce an extremely thin third thread between the two perimeter threads that doesn’t extrude well. Making the wall three threads wide works much better:

    Calibration Box - open - 3w 40 3.0
    Calibration Box – open – 3w 40 3.0

    The slicing algorithms may be smart enough to make all the tricks I’ve learned completely obsolete; that’s fine with me!

    The raw measurements:

    Calibration box measurements - 2016-06
    Calibration box measurements – 2016-06
  • Raspberry Pi Streaming Radio Player: Mostly Viable Product

    The latest version of my simpleminded streaming radio player includes:

    • More durable parsing for track titles with embedded quotes and semicolons
    • Muting during empty / non-music Radionomy tracks
    • The Dutchess County E911 service

    Audionomy’s empty / non-music tracks include a remarkable number of mis-encoded MP3 sections triggering decoding problems; those problems don’t occur during music tracks. Some tracks come through as advertisements, which would be mostly OK apart from the garbled / high-volume gibberish, but on the whole they’re un-listenable:

    ICY Info: StreamTitle='';
    A:1271.0 (21:10.9) of 0.0 (00.0)  4.0% 44%
    [mp3float @ 0x7623e080]overread, skip -7 enddists: -5 -5
    [mp3float @ 0x7623e080]overread, skip -9 enddists: -6 -6
    A:1271.2 (21:11.2) of 0.0 (00.0)  4.0% 45%
    [mp3float @ 0x7623e080]overread, skip -7 enddists: -5 -5
    A:1309.1 (21:49.1) of 0.0 (00.0)  4.0% 42%
    ICY Info: StreamTitle='Targetspot - TargetSpot';
    A:1316.4 (21:56.4) of 0.0 (00.0)  4.0% 40%
    [mp3float @ 0x7623e080]overread, skip -5 enddists: -4 -4
    [mp3float @ 0x7623e080]overread, skip -5 enddists: -2 -2
    

    Muting happens in the mixer, because that seems easier than messing with mplayer in mid-flight. Rather than attempt to control the muted state with specific timeouts, I just un-mute after a new track title arrives; that has no effect if it’s already un-muted. The delays depend on the buffer fill level and avoid the worst of the gibberish.

    The player still falls over dead / jams solid on occasion, generally because the incoming data has stopped streaming or delivered severe encoding problems. Other than that, it runs pretty much all day, every day, on at least one of the Raspberry Pi streamers.

    Still no track display. Mostly, we still don’t miss it.

    The Python source code as a GitHub Gist:

    from evdev import InputDevice,ecodes,KeyEvent
    import subprocess32 as subprocess
    import select
    import re
    import sys
    import time
    Media = {'KEY_KP7' : ['Classical',['mplayer','-playlist','http://stream2137.init7.net/listen.pls'%5D%5D,
    'KEY_KP8' : ['Jazz',['mplayer','-playlist','http://stream2138.init7.net/listen.pls'%5D%5D,
    'KEY_KP9' : ['WMHT',['mplayer','http://live.str3am.com:2070/wmht1'%5D%5D,
    'KEY_KP4' : ['Classic 1000',['mplayer','-playlist','http://listen.radionomy.com/1000classicalhits.m3u'%5D%5D,
    'KEY_KP5' : ['DCNY 911',['mplayer','-playlist','http://www.broadcastify.com/scripts/playlists/1/12305/-5857889408.m3u'%5D%5D,
    'KEY_KP6' : ['WAMC',['mplayer','http://pubint.ic.llnwd.net/stream/pubint_wamc'%5D%5D,
    'KEY_KP1' : ['60s',['mplayer','-playlist','http://listen.radionomy.com/all60sallthetime-keepfreemusiccom.m3u'%5D%5D,
    'KEY_KP2' : ['50-70s',['mplayer','-playlist','http://listen.radionomy.com/golden-50-70s-hits.m3u'%5D%5D,
    'KEY_KP3' : ['Soft Rock',['mplayer','-playlist','http://listen.radionomy.com/softrockradio.m3u'%5D%5D,
    'KEY_KP0' : ['Zen',['mplayer','-playlist','http://listen.radionomy.com/zen-for-you.m3u'%5D%5D
    }
    CurrentKC = 'KEY_KP7'
    Controls = {'KEY_KPSLASH' : '//////',
    'KEY_KPASTERISK' : '******',
    'KEY_KPENTER' : ' ',
    'KEY_KPMINUS' : '<',
    'KEY_KPPLUS' : '>',
    'KEY_VOLUMEUP' : '*',
    'KEY_VOLUMEDOWN' : '/'
    }
    MuteDelay = 5.5 # delay before non-music track; varies with buffering
    UnMuteDelay = 7.3 # delay after non-music track
    MixerChannel = 'PCM' # which amixer thing to use
    # set up event inputs and polling objects
    # This requires some udev magic to create the symlinks
    k = InputDevice('/dev/input/keypad')
    k.grab()
    kp = select.poll()
    kp.register(k.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    v = InputDevice('/dev/input/volume')
    v.grab()
    vp = select.poll()
    vp.register(v.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    # set up files for mplayer pipes
    lw = open('/tmp/mp.log','w') # mplayer piped output
    lr = open('/tmp/mp.log','r') # … reading that output
    # set the mixer output low enough that the initial stream won't wake the dead
    subprocess.call(['amixer','sset',MixerChannel,'10'])
    # Start the player with the default stream
    print 'Starting mplayer on',Media[CurrentKC][0],' -> ',Media[CurrentKC][-1][-1]
    p = subprocess.Popen(Media[CurrentKC][-1],
    stdin=subprocess.PIPE,stdout=lw,stderr=subprocess.STDOUT)
    print ' … running'
    #——————–
    #— Play the streams
    while True:
    # pluck next line from mplayer and decode it
    text = lr.readline()
    if 'ICY Info: ' in text: # these lines may contain track names
    trkinfo = text.split(';') # also splits at semicolon embedded in track name
    for ln in trkinfo:
    if 'StreamTitle' in ln: # this part contains a track name
    if 2 == ln.count("'"): # exactly two single quotes = probably none embedded in track name
    trkhit = re.search(r"StreamTitle='(.*)'",ln)
    if trkhit: # true for valid search results
    TrackName = trkhit.group(1) # the string between the two quotes
    print 'Track name: ', TrackName
    if ('' == TrackName or 'TargetSpot' in TrackName) and 'radionomy' in Media[CurrentKC][-1][-1]:
    print ' … muting empty Radionomy track'
    time.sleep(MuteDelay)
    subprocess.call(['amixer','-q','sset',MixerChannel,'mute'])
    else:
    print ' … unmuting'
    time.sleep(UnMuteDelay)
    subprocess.call(['amixer','-q','sset',MixerChannel,'unmute'])
    else:
    print ' … semicolon in track name: ', ln
    else:
    print ' … quotes in track name: ', ln
    elif 'Exiting…' in text:
    print 'Got EOF / stream cutoff'
    print ' … killing dead mplayer'
    p.kill()
    print ' … flushing pipes'
    lw.truncate(0)
    print ' … discarding keys'
    while [] != kp.poll(0):
    kev = k.read
    print ' … restarting mplayer: ',Media[CurrentKC][0]
    p = subprocess.Popen(Media[CurrentKC][-1],
    stdin=subprocess.PIPE,stdout=lw,stderr=subprocess.STDOUT)
    print ' … running'
    continue
    # accept pending events from volume control knob
    if [] != vp.poll(10):
    vev = v.read()
    for e in vev:
    if e.type == ecodes.EV_KEY:
    kc = KeyEvent(e).keycode
    # print 'Volume kc: ',kc
    if kc in Controls:
    print 'Vol Control: ',kc
    try:
    p.stdin.write(Controls[kc])
    except Exception as e:
    print "Can't send control: ",e
    print ' … restarting player: ',Media[CurrentKC][0]
    p = subprocess.Popen(Media[CurrentKC][-1],
    stdin=subprocess.PIPE,stdout=lw,stderr=subprocess.STDOUT)
    print ' … running'
    # accept pending events from keypad
    if [] != kp.poll(10):
    kev = k.read()
    for e in kev:
    if e.type == ecodes.EV_KEY:
    kc = KeyEvent(e).keycode
    if kc == 'KEY_NUMLOCK':
    continue
    # print 'Got: ',kc
    if (kc == 'KEY_BACKSPACE') and (KeyEvent(e).keystate == KeyEvent.key_hold):
    if True:
    print 'Backspace = shutdown!'
    p = subprocess.call(['sudo','shutdown','-HP','now'])
    else:
    print 'BS = bail from main, ssh to restart!'
    sys.exit(0)
    if KeyEvent(e).keystate != KeyEvent.key_down:
    continue
    if kc in Controls:
    print 'Control:', kc
    try:
    p.stdin.write(Controls[kc])
    except Exception as e:
    print "Can't send control: ",e
    print ' … restarting player: ',Media[CurrentKC][0]
    p = subprocess.Popen(Media[CurrentKC][-1],
    stdin=subprocess.PIPE,stdout=lw,stderr=subprocess.STDOUT)
    print ' … running'
    if kc in Media:
    print 'Switching stream to ',Media[kc][0],' -> ',Media[kc][-1][-1]
    CurrentKC = kc
    print ' … halting player'
    try:
    p.communicate(input='q')
    except Exception as e:
    print 'Perhaps mplayer died?',e
    print ' … killing it for sure'
    p.kill()
    print ' … flushing pipes'
    lw.truncate(0)
    print ' … restarting player: ',Media[CurrentKC][0]
    p = subprocess.Popen(Media[CurrentKC][-1],
    stdin=subprocess.PIPE,stdout=lw,stderr=subprocess.STDOUT)
    print ' … running'
    print 'Out of loop!'
    view raw Streamer.py hosted with ❤ by GitHub
  • Monthly Image: Groundhog Burrow

    Our new back-yard groundhog made extensive renovations and improved the landscaping before moving into the unoccupied burrow against the garage wall:

    Groundhog at garage burrow - cobbles
    Groundhog at garage burrow – cobbles

    It seems the same architect designed this project:

    Cobbled walk - 37 Fairmont Ave
    Cobbled walk – 37 Fairmont Ave

    I cannot explain the post in the middle of the walk; perhaps they’ll remove it when everything’s finished.

    The top photo is through three layers of 1950s glass. I cropped the bottom one from a helmet camera image.

  • Forester: TPMS FTW, Sorta-Kinda

    According to the Forester’s manual, the Tire Pressure Monitoring System kicks in after the car reaches 25 mph. It evidently takes a while to figure things out after that, because the TPMS light blinked on a mile from home on the way to Mary’s Vassar Farms garden. I pulled into the next parking lot, measured 20 psi in the left rear tire, then found this staring me in the eye:

    Forester - left rear tire with screw
    Forester – left rear tire with screw

    Well, that certainly simplified the diagnosis!

    I unloaded two bags of shredded leaves and a pile of hoses, swapped in the (limited use, donut-style) spare tire, and continued the mission.

    The TPMS light wasn’t on when I drove to Squidwrench the previous evening. Judging from the wear, that screw appeared during the various errands following our 800 mile road trip, which is good news of a sort, and depressurized the tire over the course of a day or two.

    The receipt from the fix-it folks cautions that a plug is a temporary fix, because “the injury has compromised the integrity of the tire”. On the other paw, the Forester manual tells me “All four tires must be the same in terms of manufacturer, brand (tread pattern), construction, and size. You are advised to replace the tires with new ones that are identical to those fitted as standard equipment” and then provides a checklist:

    When you replacing or installing tire(s), all four tires must be the same for following items.
    (a) Size
    (b) Circumference
    (c) Speed symbol
    (d) Load index
    (e) Construction
    (f) Manufacturer
    (g) Brand (tread pattern)
    (h) Degrees of wear

    There’s absolutely no way to get an identical replacement tire, let alone one with the same tread wear, but I am so unready to replace all four tires after 12 k miles / 2 years.

    We shall see how this works out…

  • Groundhog on High Alert

    Looks like I’m getting the stinkeye:

    Groundhog being suspicious
    Groundhog being suspicious

    The extensive garden armor remains effective, although we know groundhogs can run straight up a chain-link fence when given sufficient motivation. They generally give up after encountering the galvanized chickenwire around the buried concrete blocks; the garden is just to the left of the picture.

    The front-yard groundhog suffered a fatal automobile accident shortly after it finished excavating its burrow against the front foundation. This critter may have moved into the abandoned summer home near the garage at the back of the house.

  • Blender Bearing Repair: Round Three

    After half a dozen years, the bearings in the blender impeller felt pretty bad:

    Defunct blender bearings
    Defunct blender bearings

    I wiped everything clean, found the box containing the box containing the tube of bearings, packed the base with more silicone grease, reassembled everything in reverse order, and it’s all good again.

    The first repair lasted for a year and the second for six, so I think overpacking the base with grease helped a lot. Maybe I’m getting better at ignoring horrible grinding sounds.

    I can do this twice more, although the Jesus clip holding the shaft into the bearing stack definitely needs replacing.