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.

Month: November 2015

  • HP 7475A Plotter: One-Button Demo Madness

    Back in the day, you could install a Genuine HP 09872-60066 Digitizing Sight in your Genuine HP 7475A plotter, maneuver the sight to an interesting point on the paper, press the Enter button, send the point’s coordinates through the serial port to the computer, then do whatever you like with the numbers.

    Here in the future, I twiddled the demo code that draws Superformula patterns to send a digitization command and await the response at the end of each plot. I can then change the paper, press the Enter button, and get the next plot: exactly what I need for the upcoming Poughkeepsie Mini Maker Faire.

    The only gotcha turns out to be that, having hacked the Chiplotle interface to use hardware handshaking, there’s no way to tell when the outgoing buffer has drained. Until that happens, the plotter can’t respond to the digitizing command and, eventually, Chiplotle kvetches about not hearing anything.

    The least awful solution seems to be sleeping for 40 seconds (!) while the plotter trudges through the last line of the legend (!!), then continuing apace:

            print "Waiting for plotter... ignore timeout errors!"
            sleep(40)
            while NoneType is type(plt.status):
                sleep(5)
    
            print "Load more paper, then ..."
            print "  ... Press ENTER on the plotter to continue"
            plt.clear_digitizer()
            plt.digitize_point()
            
            plotstatus = plt.status
            while (NoneType is type(plotstatus)) or (0 == int(plotstatus) & 0x04):
                plotstatus = plt.status
                
            print "Digitized: " + str(plt.digitized_point)
    

    When the interface times out, Chiplotle doesn’t set the status code to anything in particular (which makes sense), so you can’t do anything useful with it. Therefore, the operand order in the last while statement matters: you can’t convert a value of type NoneType into anything else.

    The other change wraps the entire plotting loop with an always-and-forever loop: hit Ctrl-C to break out at the end of the day.

    You can’t change the new plot’s paper size, because the digitizing command preempts the Enter button that’s part of the Enter+Size combination. That makes perfect sense, even in retrospect.

    Testing that gave me the opportunity to run all the pens, refilled and OEM, through their paces:

    HP 7475A - Superformula demo
    HP 7475A – Superformula demo

    The Sakura pens in their adapters continue to work well:

    HP 7475A - Superformula - Sakura pens
    HP 7475A – Superformula – Sakura pens

    They’re such unique snowflakes…

    The complete Python source code:

    from chiplotle import *
    from math import *
    from datetime import *
    from time import *
    from types import *
    import random
    
    
    def superformula_polar(a, b, m, n1, n2, n3, phi):
        ''' Computes the position of the point on a
        superformula curve.
        Superformula has first been proposed by Johan Gielis
        and is a generalization of superellipse.
        see: http://en.wikipedia.org/wiki/Superformula
        Tweaked to return polar coordinates
        '''
    
        t1 = cos(m * phi / 4.0) / a
        t1 = abs(t1)
        t1 = pow(t1, n2)
    
        t2 = sin(m * phi / 4.0) / b
        t2 = abs(t2)
        t2 = pow(t2, n3)
    
        t3 = -1 / float(n1)
        r = pow(t1 + t2, t3)
        if abs(r) == 0:
            return (0, 0)
        else:
          #     return (r * cos(phi), r * sin(phi))
            return (r, phi)
    
    
    def supershape(width, height, m, n1, n2, n3,
                   point_count=10 * 1000, percentage=1.0, a=1.0, b=1.0, travel=None):
        '''Supershape, generated using the superformula first proposed 
        by Johan Gielis.
    
        - `points_count` is the total number of points to compute.
        - `travel` is the length of the outline drawn in radians. 
           3.1416 * 2 is a complete cycle.
        '''
        travel = travel or (10 * 2 * pi)
    
        # compute points...
        phis = [i * travel / point_count
                for i in range(1 + int(point_count * percentage))]
        points = [superformula_polar(a, b, m, n1, n2, n3, x) for x in phis]
    
        # scale and transpose...
        path = []
        for r, a in points:
            x = width * r * cos(a)
            y = height * r * sin(a)
            path.append(Coordinate(x, y))
    
        return Path(path)
    
    
    # RUN DEMO CODE
    
    if __name__ == '__main__':
    
        override = False
    
        plt = instantiate_plotters()[0]
    #   plt.write('IN;')
    
        if plt.margins.soft.width < 11000:               # A=10365 B=16640
            maxplotx = (plt.margins.soft.width / 2) - 100
            maxploty = (plt.margins.soft.height / 2) - 150
            legendx = maxplotx - 2600
            legendy = -(maxploty - 650)
            tscale = 0.45
            numpens = 4
            # prime/10 = number of spikes
            m_values = [n / 10.0 for n in [11, 13, 17, 19, 23]]
            # ring-ness 0.1 to 2.0, higher is larger
            n1_values = [
                n / 100.0 for n in range(55, 75, 2) + range(80, 120, 5) + range(120, 200, 10)]
        else:
            maxplotx = plt.margins.soft.width / 2
            maxploty = plt.margins.soft.height / 2
            legendx = maxplotx - 3000
            legendy = -(maxploty - 700)
            tscale = 0.45
            numpens = 6
            m_values = [n / 10.0 for n in [11, 13, 17, 19, 23, 29, 31,
                                           37, 41, 43, 47, 53, 59]]   # prime/10 = number of spikes
            # ring-ness 0.1 to 2.0, higher is larger
            n1_values = [
                n / 100.0 for n in range(15, 75, 2) + range(80, 120, 5) + range(120, 200, 10)]
    
        print "   Max: ({},{})".format(maxplotx, maxploty)
    
        # spiky-ness 0.1 to 2.0, lower is spiky-er
        n2_values = [
            n / 100.0 for n in range(10, 60, 2) + range(65, 100, 5) + range(110, 200, 10)]
    
        plt.write(chr(27) + '.H200:')   # set hardware handshake block size
        plt.set_origin_center()
        # scale based on B size characters
        plt.write(hpgl.SI(tscale * 0.285, tscale * 0.375))
        # slow speed for those abrupt spikes
        plt.write(hpgl.VS(10))
        
        while True:
            
            # standard loadout has pen 1 = fine black
            plt.write(hpgl.PA([(legendx, legendy)]))
            plt.select_pen(1)
            plt.write(hpgl.LB("Started " + str(datetime.today())))
    
            if override:
                m = 4.1
                n1_list = [1.15, 0.90, 0.25, 0.59, 0.51, 0.23]
                n2_list = [0.70, 0.58, 0.32, 0.28, 0.56, 0.26]
            else:
                m = random.choice(m_values)
                n1_list = random.sample(n1_values, numpens)
                n2_list = random.sample(n2_values, numpens)
    
            pen = 1
            for n1, n2 in zip(n1_list, n2_list):
                n3 = n2
                print "{0} - m: {1:.1f}, n1: {2:.2f}, n2=n3: {3:.2f}".format(pen, m, n1, n2)
                plt.select_pen(pen)
                plt.write(hpgl.PA([(legendx, legendy - 100 * pen)]))
                plt.write(
                    hpgl.LB("Pen {0}: m={1:.1f} n1={2:.2f} n2=n3={3:.2f}".format(pen, m, n1, n2)))
                e = supershape(maxplotx, maxploty, m, n1, n2, n3)
                plt.write(e)
                pen = pen + 1 if (pen % numpens) else 1
    
            plt.select_pen(1)
            plt.write(hpgl.PA([(legendx, legendy - 100 * (numpens + 1))]))
            plt.write(hpgl.LB("Ended   " + str(datetime.today())))
            plt.select_pen(0)
            plt.write(hpgl.PA([(-maxplotx,maxploty)]))
            
            print "Waiting for plotter... ignore timeout errors!"
            sleep(40)
            while NoneType is type(plt.status):
                sleep(5)
    
            print "Load more paper, then ..."
            print "  ... Press ENTER on the plotter to continue"
            plt.clear_digitizer()
            plt.digitize_point()
            
            plotstatus = plt.status
            while (NoneType is type(plotstatus)) or (0 == int(plotstatus) & 0x04):
                plotstatus = plt.status
                
            print "Digitized: " + str(plt.digitized_point)
    
  • Monthly Science: Dehumidification by Rice

    As part of a discussion on the M2 forums about using rice to dehumidify 3D printer filament, I replaced the 500 g bag of silica gel in the basement safe with a bowl containing 200 g of long-grain brown rice from our rice supply and let it sit for a while:

    Basement Safe Humidity - Rice vs. Silica Gel - 2015-10-31
    Basement Safe Humidity – Rice vs. Silica Gel – 2015-10-31

    The abrupt drop in humidity from 52% to the logger’s minimum 15% marks the point where I replaced the rice with a fresh bag of silica gel, with a door opening shortly thereafter. The basement air outside the safe varied between 52% and 54% during that time, so the air inside the safe trended upward toward that goal.

    The rice still weighed exactly 200 g after its stay in the safe, so we can conclude it hadn’t absorbed or released any water.

    Conclusion: nope, rice doesn’t work as a dehumidifier…