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

  • Cast Iron Pan Seasoning: Low-woo Results

    The original cast-iron seasoning recipe, after half a dozen iterations of flax seed oil & high-temperature baking, produced disappointing results:

    Wagner cast iron skillet - washed - top
    Wagner cast iron skillet – washed – top

    The key point of seasoning seems to require heating the oil enough to polymerize its molecular thingies, with (IMO) pretty nearly everything else boiling down to woo.

    Since that rusting incident, I’ve done this after every use:

    • Wipe the pan clean with the same hot soapy water I use for everything else
    • Remove crud with the same Scotchbrite / sponge pad I use for everything else
    • Rinse and wipe dry with the sponge side of the pad
    • Set stove timer for 3 minutes
    • Put pan on simmer burner, set to high flame
    • Continue cleanup until timer sounds
    • Set stove timer for 3 minutes
    • Wipe half a dozen drops of flax seed oil around pan with cotton cloth scrap
    • Continue cleanup until timer sounds
    • Turn off simmer burner
    • Wipe pan with that oily cotton scrap

    The pan reaches about 300 °F after 3 minutes. The “opening the pores” thing is woo, but a completely dry pan doesn’t spit back and that’s a major plus.

    The pan tops out at a bit over 400 °F after a total of 6 minutes. There’s no smoke, no excitement, just a hot pan on the back burner.

    Given that I’m washing the pan anyway, the whole “seasoning” operation adds maybe two minutes to the process. By now, it’s entirely automatic.

    Nota Bene: Set the timer before turning on the burner and before adding the oil, because you will become distracted and will not remember the pan quietly heating on the back burner. You have been warned.

    After two months of doing that about once a day:

    Wagner Cast Iron Skillet - Low Woo Seasoning
    Wagner Cast Iron Skillet – Low Woo Seasoning

    Granted, it looks about the same as the previous results, but this uniform dull black coating repels water, doesn’t rust, loves oil, wipes clean without scouring, and the daily omelet doesn’t stick hardly at all. Obviously, the key difference is that I’ve polymerized a gazillion coats of oil, rather than half a dozen.

    Although I have no idea whether I’m exposing us to lethal free radicals created by the polymerization process, I doubt anybody else knows anything on that subject with regard to their own seasoning technique, so we’re pretty much even. As with most such worries, It Doesn’t Matter.

    Next, I’ll just wipe the pan and let it dry in the rack. That coating should eventually wear off, at least in the high-traffic areas; let’s see how little maintenance it requires.

     

  • Storm Door Brace: Now, With Inserts!

    The mid-1950s wood doors on our house have wood storm doors with interchangeable wood-framed glass and screen panels. Twice a year, the diligent homeowner will swap the panels to match the season; during the last 60+ years, the glass panels remain undropped.

    The back door has a diagonal tension brace to hold the door in shape; the door may be slightly distorted or the frame slightly out of square. In any event, the brace obstructs the panel, so the semiannual ritual includes loosening the brace and removing four screws. During the last 60+ years, the screw holes have required repair / filling several times; about five years ago, I plugged them with epoxy putty and drilled them to fit the screws.

    That repair having aged out, I was about to renew the epoxy when I realized that I now have brass inserts that would work even better, if I replaced the original wood screws with 10-32 machine screws.

    I cut the screws to the exact length using the brace and brass insert as a fixture:

    Storm door - screw cutting
    Storm door – screw cutting

    The vacuum cleaner nozzle to the lower right inhales the debris from the Dremel cutoff wheel that would otherwise fill the shop; I used up the last half of a wheel on four stainless steel screws.

    Because each end of the brace has two screws, I knew that I couldn’t just drill out the four holes, plant four inserts, and be done with the job: the first insert on each end could go pretty nearly anywhere, but the second insert must match the brace hole spacing. The only way I know how to do that is to epoxy the first two inserts in place and let them cure, drill the other two holes slightly oversize, mount those inserts on the brace, butter them with epoxy, put the brace in place, tighten the first two screws, snug the brace, and hope I didn’t epoxy the brace to the door or the screws to the inserts.

    Slips of waxed paper between the brace and the door prevented the first problem and oiling the screws prevented the second. It’s not the best-looking job I’ve ever done, but nobody will ever see the inserts behind the brace:

    Storm door - inserts
    Storm door – inserts

    Now, we’re ready for winter and I’m ready for spring!

    Most likely, the new owners (whoever and whenever they may be) will never use these inserts, as they’ll replace all the windows & doors, plus sand & refinish the hardwood floors, before moving in …

  • Kitchen Spatula Search

    A long long time ago, we bought a kitchen spatula that’s served us well ever since:

    Spatula Search - original
    Spatula Search – original

    To give you an idea of how old that poor thing is, the back of the handle bears a Japan stamp. I’ve re-set the rivets several times, the blade has rusted as badly as you think, and we recently, very reluctantly, decided it has passed its best-used-by date.

    The 3 x 4.5 inch blade is 19 mil = 0.45 mm plated carbon steel, stiff enough to remain flat and springy enough to bend a little, with a 9 inch = 230 mm steel handle ending in a plastic overmold.

    These days, it’s essential to the cutting, flipping, and serving of the morning’s omelet-like substance, made of eggs, bacon, veggies, green leafy things, plus this-and-that, in the cast-iron pan. Mary chops the disk into quarters with the reasonably sharp edge, maneuvers the reasonably bendy blade under each quarter, flips them over, tops with bacon & cheese, pauses for consolidation & melting, then pops them onto plates. Yum!

    Omelet in cast-iron pan
    Omelet in cast-iron pan

    So we set out to buy a replacement.

    Here’s what we’ve tried and rejected so far:

    Spatula Search - overview
    Spatula Search – overview

    I’ve used this one for many years to flip pancakes on a succession of non-stick griddles, a service at which it excels. The edge isn’t sharp enough to cut the green-and-leafy and the completely inflexible blade cannot be maneuvered under the omelet quarters:

    Spatula Search - heavy solid plastic
    Spatula Search – heavy solid plastic

    This one gets deployed for burgers and their ilk, also in the cast-iron pan. The blade, although sharp enough, is completely rigid:

    Spatula Search - heavy slotted metal
    Spatula Search – heavy slotted metal

    On the other paw, a slightly concave 7 mil = 0.18 mm spring steel blade is much too thin and, well, springy. Although very sharp, you cannot apply enough cutting force without suddenly bending the blade and, if the omelet quarter isn’t positioned exactly right, the blade will bend underneath it and dump breakfast on the stovetop. The alert reader will notice a missing weld between the blade and the bottom wire handle:

    Spatula Search - thin spring steel
    Spatula Search – thin spring steel

    This very thin plastic blade has similar problems with poor cut-ability and excessive flexibility:

    Spatula Search - thin springy plastic
    Spatula Search – thin springy plastic

    This one looked really promising and worked almost perfectly. Regrettably, its nylon blade bears a 400 °F rating and the bottom of the omelet reaches nearly 450 °F. You can see what happens to the reasonably sharp edge as it scrapes across the pan:

    Spatula Search - heavy slotted nylon
    Spatula Search – heavy slotted nylon

    The omelet cooks at the temperature it cooks at, which part of the specifications is not subject to further discussion.

    So, we’re stumped. Having trawled the usual online and big-box stores, we’ve been unable to find a replacement. Simple steel blades aren’t available. Trendy silicone-bonded stainless steel blades combine the worst of all worlds: won’t cut and won’t flip. Pretty nearly anything you don’t see above seems obviously unsuitable for our simple needs: too big, too small, or too melty.

    We’ll consider all recommendations and suggestions! Thanks …

  • Monthly Science: CR2032 Lithium Cell Life

    One of the Hobo dataloggers asked for a new battery during its most recent data dump. The old battery dates back to January 2015:

    Maxell CR2032 lithium cell - 22 month life
    Maxell CR2032 lithium cell – 22 month life

    That was when a batch of Energizer cells failed in quick succession: it wasn’t the datalogger’s fault. I’ve been handling the cells a bit more carefully, too, although that certainly doesn’t account for the much longer life.

    With batteries, particularly from eBay, you definitely can’t tell what you’re going to get or how long it’ll last; that’s true of many things in life.

  • Inside Another 9 V Battery

    A long time ago, I discovered some quasi-AAAA cells inside 9 V batteries:

    Inside a batteries.com 9V battery
    Inside a batteries.com 9V battery

    It occurred to me that I should dismantle a defunct Rayovac Maximum 9 V alkaline battery from the most recent batch (*) to see what it looked like:

    Rayovac Maximum 9V battery - interior
    Rayovac Maximum 9V battery – interior

    Surprise!

    A closer look at those pancake cells:

    Rayovac Maximum 9V battery - detail
    Rayovac Maximum 9V battery – detail

    They look like separate cells bonded into a stack, although there’s no easy way to probe the inter-cell contacts; the leftmost cell probably died first.

    (*) Which has apparently outlived the Rayovac Maximum brand, as they don’t appear on the Rayovac site.

  • Hand Sprayer Hose Kink Prevention

    Mary’s new half-gallon sprayer arrived with a kink in the hose just below the handle, which is about what you’d expect from a non-reinforced plastic tube jammed into the smallest possible box containing both the sprayer and its wand. Fortunately, the Box o’ Springs had one that just fit the hose and jammed firmly into the handle:

    Sprayer hose with kink-resisting spring
    Sprayer hose with kink-resisting spring

    The kink slowly worked its way out after being surrounded by the spring and shouldn’t come back.

    That was easy…

  • Raspberry Pi Streaming Radio Player: Improved Pipe Handling

    My Raspberry Pi-based streaming radio player generally worked fine, except sometimes the keypad / volume control knob would stop responding after switching streams. This being an erratic thing, the error had to be a timing problem in otherwise correct code and, after spending Quality Time with the Python subprocess and select doc, I decided I was abusing mplayer’s stdin and stdout pipes.

    This iteration registers mplayer’s stdout pipe as Yet Another select.poll() Polling Object, so that the main loop can respond whenever a complete line arrives. Starting mplayer in quiet mode reduces the tonnage of stdout text, at the cost of losing the streaming status that I really couldn’t do anything with, and eliminates the occasional stalls when mplayer (apparently) dies in the middle of a line.

    The code kills and restarts mplayer whenever it detects an EOF or stream cutoff. That works most of the time, but a persistent server or network failure can still send the code into a sulk. Manually selecting a different stream (after we eventually notice the silence) generally sets things right, mainly by whacking mplayer upside the head; it’s good enough.

    It seems I inadvertently invented streaming ad suppression by muting (most of) the tracks that produced weird audio effects. Given that the “radio stations” still get paid for sending ads to me, I’m not actually cheating anybody out of their revenue: I’ve just automated our trips to the volume control knob. The audio goes silent for a few seconds (or, sheesh, a few minutes) , blatting a second or two of ad noise around the gap to remind us of what we’re missing; given the prevalence of National Forest Service PSAs, the audio ad market must be a horrific wasteland.

    The Python source code as a GitHub Gist:

    from evdev import InputDevice,ecodes,KeyEvent
    import subprocess32 as subp
    import select
    import re
    import sys
    import time
    import logging
    Media = {'KEY_KP7' : ['Classical',False,['mplayer','–quiet','-playlist','http://stream2137.init7.net/listen.pls'%5D%5D,
    'KEY_KP8' : ['Jazz',False,['mplayer','–quiet','-playlist','http://stream2138.init7.net/listen.pls'%5D%5D,
    'KEY_KP9' : ['WMHT',False,['mplayer','–quiet','http://live.str3am.com:2070/wmht1'%5D%5D,
    'KEY_KP4' : ['Classic 1000',True,['mplayer','–quiet','-playlist','http://listen.radionomy.com/1000classicalhits.m3u'%5D%5D,
    'KEY_KP5' : ['DCNY 911',False,['mplayer','–quiet','-playlist','http://www.broadcastify.com/scripts/playlists/1/12305/-5857889408.m3u'%5D%5D,
    'KEY_KP6' : ['WAMC',False,['mplayer','–quiet','http://pubint.ic.llnwd.net/stream/pubint_wamc'%5D%5D,
    'KEY_KP1' : ['60s',True,['mplayer','–quiet','-playlist','http://listen.radionomy.com/all60sallthetime-keepfreemusiccom.m3u'%5D%5D,
    'KEY_KP2' : ['50-70s',True,['mplayer','–quiet','-playlist','http://listen.radionomy.com/golden-50-70s-hits.m3u'%5D%5D,
    'KEY_KP3' : ['Soft Rock',True,['mplayer','–quiet','-playlist','http://listen.radionomy.com/softrockradio.m3u'%5D%5D,
    'KEY_KP0' : ['Zen',True,['mplayer','–quiet','-playlist','http://listen.radionomy.com/zen-for-you.m3u'%5D%5D
    }
    CurrentKC = 'KEY_KP3'
    Controls = {'KEY_KPSLASH' : '//////',
    'KEY_KPASTERISK' : '******',
    'KEY_KPENTER' : ' ',
    'KEY_KPMINUS' : '<',
    'KEY_KPPLUS' : '>',
    'KEY_VOLUMEUP' : '*',
    'KEY_VOLUMEDOWN' : '/'
    }
    MuteStrings = ["TargetSpot","[Unknown]","Advert:","+++","—","SRR","Srr","ZEN FOR"]
    MuteDelay = 8.0 # delay before non-music track; varies with buffering
    UnMuteDelay = 7.5 # delay after non-music track
    Muted = False # keep track of muted state
    MixerChannel = 'PCM' # which amixer thing to use
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',filename='/tmp/Streamer.log',level=logging.INFO)
    logger = logging.getLogger()
    # 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 file for output tracing
    lw = open('/tmp/mp.log','w') # mplayer piped output
    # set the mixer output low enough that the initial stream won't wake the dead
    subp.call(['amixer','sset',MixerChannel,'10'])
    # Start the player with the default stream, set up for polling
    print 'Starting mplayer on',Media[CurrentKC][0],' -> ',Media[CurrentKC][-1][-1]
    logging.info('Starting mplayer on %s -> %s',Media[CurrentKC][0],Media[CurrentKC][-1][-1])
    p = subp.Popen(Media[CurrentKC][-1],
    stdin=subp.PIPE,stdout=subp.PIPE,stderr=subp.STDOUT)
    pp = select.poll() # this may be valid for other invocations, but is not pretty
    pp.register(p.stdout.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    print ' … running'
    #——————–
    #— Play the streams
    while True:
    # pluck next line from mplayer and decode it
    if [] != pp.poll(10):
    text = p.stdout.readline()
    if 'ICY Info: ' in text: # these lines may contain track names
    lw.write(text)
    lw.flush()
    trkinfo = text.split(';') # also splits at semicolon embedded in track name
    # logging.info('Raw split line: %s', trkinfo)
    for ln in trkinfo:
    if 'StreamTitle' in ln: # this part contains a track name
    NeedMute = False # assume a listenable track
    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
    logging.info('Track name: [%s]', TrackName)
    if Media[CurrentKC][1] and ( (len(TrackName) == 0) or any(m in TrackName for m in MuteStrings) ) :
    NeedMute = True
    else:
    print ' … semicolon in track name: ', ln
    logging.info('Semicolon in track name: [' + ln + ']')
    else:
    print ' … quotes in track name: ', ln
    logging.info('Quotes in track name: [' + ln + ']')
    if NeedMute:
    print ' … muting:',
    if Media[CurrentKC][1] and not Muted:
    time.sleep(MuteDelay) # brute-force assumption about buffer leadtime
    subp.call(['amixer','-q','sset',MixerChannel,'mute'])
    Muted = True
    print 'done'
    logging.info('Track muted')
    else:
    print ' … unmuting:',
    if Muted:
    if Media[CurrentKC][1]:
    time.sleep(UnMuteDelay) # another brute-force timing assumption
    Muted = False
    subp.call(['amixer','-q','sset',MixerChannel,'unmute'])
    print 'done'
    logging.info('Track unmuted')
    elif 'Exiting.' in text: # mplayer just imploded
    lw.write(text)
    lw.flush()
    print 'Got EOF / stream cutoff'
    logging.info('EOF or stream cutoff')
    print ' … killing dead mplayer'
    pp.unregister(p.stdout.fileno())
    p.terminate() # p.kill()
    p.wait()
    # print ' … flushing pipes'
    # lw.truncate(0)
    print ' … discarding keys'
    while [] != kp.poll(0):
    kev = k.read
    print ' … restarting mplayer: ',Media[CurrentKC][0]
    logging.info('Restarting mplayer')
    p = subp.Popen(Media[CurrentKC][-1],
    stdin=subp.PIPE,stdout=subp.PIPE,stderr=subp.STDOUT)
    pp.register(p.stdout.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    print ' … running'
    logging.info(' … running')
    # accept pending events from volume control knob
    if [] != vp.poll(10):
    vev = v.read()
    lw.write('Volume')
    lw.flush()
    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]
    logging.info('Error sending volume, restarting player')
    pp.unregister(p.stdout.fileno())
    p = subp.Popen(Media[CurrentKC][-1],
    stdin=subp.PIPE,stdout=subp.PIPE,stderr=subp.STDOUT)
    pp.register(p.stdout.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    print ' … running'
    logging.info(' … running')
    # accept pending events from keypad
    if [] != kp.poll(10):
    kev = k.read()
    lw.write("Keypad")
    lw.flush()
    for e in kev:
    if e.type == ecodes.EV_KEY:
    kc = KeyEvent(e).keycode
    if kc == 'KEY_NUMLOCK': # discard these, as we don't care
    continue
    # print 'Got: ',kc
    if (kc == 'KEY_BACKSPACE') and (KeyEvent(e).keystate == KeyEvent.key_hold):
    if True:
    print 'Backspace = shutdown!'
    p.kill()
    logging.shutdown()
    q = subp.call(['sudo','shutdown','-P','now'])
    q.wait()
    time.sleep(5)
    print "Oddly, we did not die…"
    else:
    print 'BS = bail from main!'
    logging.shutdown()
    sys.exit(0)
    break
    if KeyEvent(e).keystate != KeyEvent.key_down: # discard key up & other rubbish
    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]
    logging.info('Error sending controls, restarting player')
    pp.unregister(p.stdout.fileno())
    p.terminate() # p.kill()
    p.wait()
    p = subp.Popen(Media[CurrentKC][-1],
    stdin=subp.PIPE,stdout=subp.PIPE,stderr=subp.STDOUT)
    pp.register(p.stdout.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    print ' … running'
    logging.info(' … running')
    if kc in Media:
    print 'Switching stream to ',Media[kc][0],' -> ',Media[kc][-1][-1]
    logging.info('Switching stream: ' + 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'
    pp.unregister(p.stdout.fileno())
    p.terminate() # p.kill()
    p.wait()
    # print ' … flushing pipes'
    # lw.truncate(0)
    print ' … restarting player: ',Media[CurrentKC][0]
    p = subp.Popen(Media[CurrentKC][-1],
    stdin=subp.PIPE,stdout=subp.PIPE,stderr=subp.STDOUT)
    pp.register(p.stdout.fileno(),select.POLLIN + select.POLLPRI + select.POLLERR)
    print ' … running'
    logging.info(' … running')
    print 'Out of loop!'
    logging.shutdown()
    view raw Streamer.py hosted with ❤ by GitHub