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: Electronics Workbench

Electrical & Electronic gadgets

  • Raspberry Pi Streaming Radio Player: Marginally Viable Product

    The least horrible way to get events from the keypad turned out to be a simple non-blocking poll from Python’s select library, then sucking the event input queue dry; the main loop now does what might be grandiosely overstated as cooperative multitasking. Well, hey, it reads lines from mplayer’s output pipe and processes keypad events and doesn’t stall (for very long) and that’s multi enough for me.

    It extracts the stream title from the ICY Info line, but I still haven’t bothered with a display. It may well turn out that this thing doesn’t need a display. The stream title will be enclosed in single quotes, but it may also contain non-escaped and non-paired single quotes (a.k.a. apostrophes): the obvious parsing strategy doesn’t work. I expect titles can contain non-escaped semicolons, too, which will kill the algorithm I’m using stone cold dead. Some try - except armor may be appropriate.

    This code does not tolerate a crappy WiFi connection very well at all. I eventually replaced a long-antenna WiFi adapter with an actual Ethernet cable and all the mysterious problems at the far end of the house Went Away. Soooo this code won’t tolerate random network stream dropouts very well, either; we’ll see how poorly that plays out in practice.

    The hackery to monitor / kill / restart / clean up after mplayer and its pipes come directly from seeing what failed, then whacking that mole in the least intrusive manner possible. While it would be better to wrap a nice abstract model around what mplayer is (assumed to be) doing, it’s not at all clear to me that I can build a sufficiently durable model to be worth the effort. Basically, trying to automate a program designed to be human-interactive is always a recipe for disaster.

    The option for the Backspace / Del key lets you do remote debugging by editing the code to just bail out of the loop instead of shut down. Unedited, it’s a power switch: the Pi turns off all the peripherals and shuts itself down. The key_hold conditional means you must press-and-hold that button to kill the power, but don’t run this on your desktop PC, OK?

    Autostarting the program requires one line in /etc/rc.local:

    sudo -u pi python /home/pi/Streamer.py &
    

    AFAICT, using cron with an @REBOOT line has timing issues with the network being available, but I can’t point to any solid evidence that hacking rc.local waits until the network is up, either. So far, so good.

    I make no apologies for any of the streams; I needed streams behind all the buttons and picked stuff from Xiph’s listing. The AAC+ streams from the Public Domain Project give mplayer a bad bellyache; I think its codecs can’t handle the “+” part of AAC+.

    All in all, not bad for a bit over a hundred lines of code, methinks…

    More fiddling will happen, but we need some continuous experience for that; let the music roll!

    The Python program as a GitHub Gist:

    from evdev import InputDevice,ecodes,KeyEvent
    import subprocess32
    import select
    import re
    import sys
    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' : ['Dub 1',['mplayer','-playlist','http://dir.xiph.org/listen/2645/listen.m3u'%5D%5D,
    'KEY_KP5' : ['Dub 2',['mplayer','http://streaming207.radionomy.com:80/MiamiClubMusiccom'%5D%5D,
    'KEY_KP6' : ['WAMC',['mplayer','http://pubint.ic.llnwd.net/stream/pubint_wamc'%5D%5D,
    'KEY_KP1' : ['Oldies 1',['mplayer','http://streaming304.radionomy.com:80/keepfree60s'%5D%5D,
    'KEY_KP2' : ['Oldies 2',['mplayer','http://streaming207.radionomy.com:80/1000Oldies'%5D%5D,
    'KEY_KP3' : ['Soft Rock',['mplayer','http://streaming201.radionomy.com:80/SoftRockRadio'%5D%5D,
    'KEY_KP0' : ['Smooth',['mplayer','http://streaming202.radionomy.com:80/The-Smooth-Lounge'%5D%5D
    }
    CurrentKC = 'KEY_KP7'
    Controls = {'KEY_KPSLASH' : '/',
    'KEY_KPASTERISK' : '*',
    'KEY_KPENTER' : ' ',
    'KEY_KPMINUS' : '<',
    'KEY_KPPLUS' : '>'
    }
    # set up event input and polling
    k=InputDevice('/dev/input/keypad')
    kp = select.poll()
    kp.register(k.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
    # Start the default stream
    print 'Starting mplayer on',Media[CurrentKC][0]," -> ",Media[CurrentKC][-1][-1]
    p = subprocess32.Popen(Media[CurrentKC][-1],stdin=subprocess32.PIPE,stdout=lw,stderr=subprocess32.STDOUT)
    print ' … running'
    #— Play the streams
    while True:
    # pluck next line from mplayer and decode it
    text = lr.readline()
    if 'ICY Info: ' in text:
    trkinfo = text.split(';')
    for ln in trkinfo:
    if 'StreamTitle' in ln:
    trkhit = re.search(r"StreamTitle='(.*)'",ln)
    TrackName = trkhit.group(1)
    print 'Track name: ', TrackName
    break
    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 = subprocess32.Popen(Media[CurrentKC][-1],stdin=subprocess32.PIPE,stdout=lw,stderr=subprocess32.STDOUT)
    print ' … running'
    continue
    # accept pending events from keypad
    if [] != kp.poll(0):
    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 = subprocess32.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 = subprocess32.Popen(Media[CurrentKC][-1],stdin=subprocess32.PIPE,stdout=lw,stderr=subprocess32.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 = subprocess32.Popen(Media[CurrentKC][-1],stdin=subprocess32.PIPE,stdout=lw,stderr=subprocess32.STDOUT)
    print ' … running'
    print 'Out of loop!'
    view raw Streamer.py hosted with ❤ by GitHub
  • Vacuum Tube LEDs: Knockoff Neopixel Failure

    The knockoff Neopixel epoxied atop the big incandescent bulb just failed:

    Vacuum Tube LEDs - ersatz heatsink plate cap
    Vacuum Tube LEDs – ersatz heatsink plate cap

    It stopped changing colors and began blinking high-intensity bursts of the RGB LEDs. Which was interesting, I’ll grant you, but didn’t produce the desired, ah, mood.

    Differential diagnosis:

    • Reboot that sucker: fail
    • Shot of circuit cooler: fail
    • Failing LED with known-good Arduino Pro Mini: fail
    • “Failing” Arduino Pro Mini with known-good LED: work

    Looks like a permanent WS2812B controller failure this time around.

    It’s been plugged into a Kill-a-Watt meter that reports it ran for 415 hours and used 150 W·h of energy, for an average 360 mW dissipation. I think the actual power falls well below the meter’s lower limit, so I doubt the accuracy, but it’s a whole bunch less than a nightlight and much more interesting.

    Now, to break that epoxy bond without breaking the bulb …

  • Vacuum Tube LEDs: Fire in the Noval

    Replacing the original Noval socket in the string with the platter-friendly version, bracing the wiring with duct tape, balancing it on my desk, and firing it up:

    Noval socket - red phase
    Noval socket – red phase

    The green phase looks nice, too:

    Noval socket - green phase
    Noval socket – green phase

    Those screws are too big.

    The getter flash covers the entire top of the tube; shining an LED down through the evacuation tip won’t work and even a laser doesn’t do much. That saves me the trouble of trying to create a cap that doesn’t wreck the tube’s good looks.

    I originally planned to use white / natural PETG for the socket, but the more I see of those things, the more I think black is the new white. The sockets should vanish into the background, to let the tubes (and their reflections) carry the show.

    The (yet to be designed) base must vanish under the platter edge, too, which puts a real crimp on its overall height. I’m not sure how to fit an Arduino Pro Mini and an FTDI board beside the existing socket; perhaps this calls for a unified socket-base design held on by those screws, rather than a separate socket inside a base enclosure.

    Even though I know the tubes are inert and cool, I still hesitate before removing them from their sockets with the Neopixels running: you simply do not unplug a hot, powered device!

  • Vacuum Tube Prices, Then and Now

    Quite by coincidence, a Pile o’ Stuff disgorged a 1975 Radio Shack Catalog listing three dense pages of vacuum tubes, including a 21HB5A:

    Radio Shack 1975 Catalog - 21HB5A Tube Listing
    Radio Shack 1975 Catalog – 21HB5A Tube Listing

    These days, you buy New Old Stock 21HB5A tubes from eBay for about the same in current dollars with shipping:

    eBay - 21HB5A Tubes
    eBay – 21HB5A Tubes

    I should stock up and light up!

    Vacuum Tube LEDs - IBM 21HB5A Beam Power Tube - green violet phase
    Vacuum Tube LEDs – IBM 21HB5A Beam Power Tube – green violet phase

     

  • Streaming Player: Wireless Keypad

    Moving the streaming media player control panel across the Sewing Room for E-Z access:

    Wireless Keypad - colored labels
    Wireless Keypad – colored labels

    Stipulated: garish labels that don’t fit the keys well at all.

    I need more than one stream for testing; the only one that matters is Classical.

    The keypad uses the same 2.4 GHz ISM band as the Raspberry Pi’s Wifi radio, which means holding a key down (which should never happen) puts a dent in mplayer’s cache fill level. Even absent that interference, the WiFi link seems more than a little iffy, probably because it’s at the far end of the house and upstairs from the router.

    Other WiFi devices report that 2.4 GHz RF has trouble punching through the intervening fifty feet of hardwood floor (on the diagonal, the joists amount to a lot of wood) and multiple sets of doubled wallboard sheets; the RPi probably needs a better radio with an actual antenna. I did move the WiFi control channel away from the default used by the (relatively distant) neighbors, which seemed to improve its disposition.

  • Raspberry Pi Streaming Radio Player: Minimum Viable Product

    With the numeric keypad producing events, and the USB audio box producing sound, the next steps involve starting mplayer through Python’s subprocess interface and feeding keystrokes into it.

    There’s not much to it:

    As much hardware doc as you need:

    RPi Streaming Player - first lashup
    RPi Streaming Player – first lashup

    The green plug leads off to a set of decent-quality PC speakers with far more bass drive than seems absolutely necessary in this context. The usual eBay vendor bungled an order for the adapter between the RCA line-out jacks and the 3.5 mm plug that will avoid driving the speakers from the UCA202’s headphone monitor output; I doubt that will make any audible difference. If you need an adapter with XLR female to 1/4 inch mono, let me know…

    The keypad labels provide all the UI documentation there is:

    Numeric Keypad - stream labels
    Numeric Keypad – stream labels

    The Python source code as a GitHub Gist:

    from evdev import InputDevice,ecodes,KeyEvent
    import subprocess32
    Media = {'KEY_KP7' : ['mplayer','http://relay.publicdomainproject.org:80/classical.aac'%5D,
    'KEY_KP8' : ['mplayer','http://relay.publicdomainproject.org:80/jazz_swing.aac'%5D,
    'KEY_KP9' : ['mplayer','http://live.str3am.com:2070/wmht1'%5D,
    'KEY_KP6' : ['mplayer','http://pubint.ic.llnwd.net/stream/pubint_wamc'%5D,
    'KEY_KP1' : ['mplayer','-playlist','http://dir.xiph.org/listen/5423257/listen.m3u'%5D,
    'KEY_KP2' : ['mplayer','-playlist','http://dir.xiph.org/listen/5197460/listen.m3u'%5D,
    'KEY_KP3' : ['mplayer','-playlist','http://dir.xiph.org/listen/5372471/listen.m3u'%5D,
    'KEY_KP0' : ['mplayer','-playlist','http://dir.xiph.org/listen/5420157/listen.m3u'%5D
    }
    Controls = {'KEY_KPSLASH' : '/',
    'KEY_KPASTERISK' : '*',
    'KEY_KPDOT' : ' '
    }
    k=InputDevice('/dev/input/keypad')
    print 'Starting mplayer'
    p = subprocess32.Popen(Media['KEY_KP7'],stdin=subprocess32.PIPE)
    print ' … running'
    for e in k.read_loop():
    if (e.type == ecodes.EV_KEY) and (KeyEvent(e).keystate == 1):
    kc = KeyEvent(e).keycode
    if kc == 'KEY_NUMLOCK':
    continue
    print "Got: ",kc
    if kc == 'KEY_BACKSPACE':
    print 'Backspace = shutdown!'
    p = subprocess32.call(['sudo','halt'])
    break
    if kc in Controls:
    print 'Control:', kc
    p.stdin.write(Controls[kc])
    if kc in Media:
    print 'Switching stream to ',Media[kc]
    print ' … halting'
    p.communicate(input='q')
    print ' … restarting'
    p = subprocess32.Popen(Media[kc],stdin=subprocess32.PIPE)
    print ' … running'
    print "Out of loop!"

    The Media dictionary relates keycodes with the command line parameters required to fire mplayer at the streaming stations. With that running, the Controls dictionary turns keycodes into mplayer keyboard controls.

    There’s no display: you have no idea what’s going on. I must start the program manually through an ssh session and can watch mplayer‘s console output.

    Poking the Halt button forcibly halts the RPi, after which you squeeze the Reset button to reboot the thing. There’s no indication that it’s running, other than sound coming out of the speakers, and no way to tell it fell of the rails other than through the ssh session.

    The loop blocks on events, so it can’t also extract stream titles from the (not yet implemented) mplayer stdout pipe / file and paste them on the (missing) display; that’s gotta go.

    There’s a lot not to like about all that, of course, but it’s in the tradition of getting something working to discover how it fails and, in this case, how it sounds, which is even more important.

  • Makerspace Starter Kit Available

    For a variety of reasons that aren’t relevant here, I must dramatically reduce the amount of stuff in the Basement Laboratory / Machine Shop / Warehouse.

    If you (or someone you know) has / is starting / will start a makerspace or similar organization, here’s an opportunity to go from zero to hero with a huge infusion of tools / instruments / make-froms / raw material / gadgets / surplus gear.

    Think of it as a Makerspace Starter Kit: everything you need in one acquisition.

    You’ve seen much of the stuff in these blog posts during the past five years, although I tightly crop the photos for reasons that should be obvious when you consider the backgrounds.

    A few glimpses, carefully chosen to make the situation look much tidier than it really is:

    This slideshow requires JavaScript.

    I’m not a hoarder, but I can look right over the fence into that territory…

    I want to donate the whole collection to an organization that can figure out how to value it and let me write it off. Failing that, I’m willing to sell the whole collection to someone who will move it out and enjoy it / put it to good use / part it out / hoard it.

    We can quibble over the value, which surely lies between scrap metal and filet mignon.

    As nearly as I can estimate from our last two moves, I have 6±2 short tons of stuff:

    • Metal shop: old South Bend lathe / vertical mill-drill / bandsaw / hand tools / arbor press
    • Cabinets / shelves loaded with cutters / tools / micrometers / calipers / whatever
    • Gas & electric welding equipment, gas foundry furnace
    • Walls / bins / drawers of fasteners / wire nuts / plumbing fittings / pipe clamps / you-name-its
    • Bookshelves of references / magazines / databooks; I’ll keep at most one set of the magazines with my columns
    • Ham radio equipment / antennas / cables
    • Radial saw, blades, clamps, tooling, and a lumber / plywood stockpile
    • Labeled boxes of make-froms on steel shelving; you get the shelves, the boxes, and their contents.
    • Solvents, chemicals, metals, minerals, elements, etc.
    • Electronic / optical / mechanical surplus & doodads
    • Stockpiles of metal rods / pipes / beams / flanges / sheets / scrap parts
    • Tools & toys & treasures beyond your wildest imagination

    When we left Raleigh, the moving company estimator observed “This will be like moving a Home Depot!”

    You must take everything, which means you must have the ability & equipment to handle 6±2 tons of stuff in relatively small, rather heavy, not easily stackable lumps. You’ll need 1000+ square feet of space with at least a seven-foot ceiling on your end to unload the truck(s) and create a solid block of stuff with skinny aisles between the shelves. This is not a quick afternoon trip for you, your BFF, a pickup truck, and a storage unit.

    I plan to keep the Sherline, the M2 3D printer, various small tools, some hardware / parts / stock, most of the electronic instruments (antique-ish, at best) and components, plus odds and ends. I’ll extract or clearly mark those items, leaving your team to move everything else without (too many) on-the-fly decisions.

    I can provide photos and descriptions, but, realistically, you should evaluate the situation in person.

    Although we’re not planning to move in the near future, if you’re thinking of moving into the Mid Hudson Valley and always wanted a house with a ready-to-run Basement Shop, we can probably work something out. Note: all of the online real estate descriptions, including Zillow, seem confused, as the two houses on our two-acre property contain the total square footage / rooms / baths / whatever. Contact us for the Ground Truth after you’ve contemplated the satellite view.

    As the saying goes, “Serious inquiries only.”