178 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | |
| layout: post
 | |
| title: "Resurrecting an old plotter"
 | |
| date: 2019-02-23
 | |
| comments: true
 | |
| tags: plotter, art, python, hardware, recurse
 | |
| ---
 | |
| 
 | |
| I finally decided to give in to FOMO on
 | |
| [#plottertwitter](https://twitter.com/hashtag/plottertwitter?lang=en) and bought
 | |
| an old plotter off *ebay*. Being in batch at the [Recurse
 | |
| Center](https://recurse.com) helped a lot: the old hardware laying around inspired the purchase, and a successful
 | |
| [nerdsnipe](https://xkcd.com/356/) of [Alex](https://github.com/wildconceits) gave me a willing
 | |
| collaborator. All of the work below was done with him.
 | |
| 
 | |
| ## What is a plotter anyway?
 | |
| Plotters are graphics devices that can transfer vector images onto a physical
 | |
| medium. The core mechanism of a plotter is an arm that can move a pen in 2
 | |
| axes (w.r.t the medium) and the ability to pick up or place down the pen to draw.
 | |
| Versions of plotters exist where paper is replaced with other flat materials
 | |
| like vinyl or pen with a knife to make it a cutting plotter.
 | |
| 
 | |
| I like to think of plotters as the naive solution to the problem that computers
 | |
| should be able to draw. Smaller, expensive memory chips (or [magnetic
 | |
| cores](https://en.wikipedia.org/wiki/Magnetic-core_memory)) in earlier computers
 | |
| made working with raster images hard, and plotters didn't need much operating
 | |
| memory.
 | |
| 
 | |
| ## HP7440A
 | |
| 
 | |
| HP7440A _"ColorPro"_ was an affordable plotter manufactured by HP, it can hold
 | |
| and switch between 8 pens simultaneously, and draw on surfaces as large as A4.
 | |
| [HP Museum has a longer post about this
 | |
| plotter](http://hpmuseum.net/display_item.php?hw=80)
 | |
| 
 | |
| Ours came pretty unscathed, with original manuals!
 | |
| 
 | |
| First thing we did was to open and clean it. I was quite surprised by how
 | |
| easy it is to open, take that 2018 tech!
 | |
| 
 | |
| { width=600px }
 | |
| 
 | |
| The internal mechanism is pretty simple. There are two servos. One for moving
 | |
| the paper back and forward, and the other for moving the pen left and right.
 | |
| There is also a solenoid-based lever to move pens to and from the paper.
 | |
| 
 | |
| ## Talk To Me
 | |
| 
 | |
| { width=600px }
 | |
| 
 | |
| Our plotter didn't come with any cables to either power it or to send it
 | |
| commands.
 | |
| 
 | |
| Puzzling out the power supply was the biggest mystery. After digging through the manuals and
 | |
| [hand drawn schematics from the HP
 | |
| Museum](http://hpmuseum.net/exhibit.php?hwdoc=80), we managed to identify it as
 | |
| a `10-0-10` AC to AC. Luckily someone was selling one on *ebay*.
 | |
| 
 | |
| Communication turned out to be done using standard serial over a
 | |
| `DB-22` adapter. We solved this by chaining a `DB-22` to `DB-9` adapter to a `DB-9` to
 | |
| `usb` adapter.
 | |
| 
 | |
| The final step was writing in the only language the plotter can understand,
 | |
| [`HP-GL`](https://en.wikipedia.org/wiki/HP-GL) or `HP Graphics Language`. Lucky
 | |
| for us, HP was on top of the plotter game when plotters were popular, so `HP-GL`
 | |
| has become a de facto standard for talking to plotters.
 | |
| 
 | |
| ...and finally our plotter moves!
 | |
| 
 | |
| <video width="600" height="450" controls>
 | |
|   <source src="/images/7440a_printing.mp4" type="video/mp4">
 | |
| </video>
 | |
| 
 | |
| ## Goooooooo faster.
 | |
| 
 | |
| HP7440A has a limited amount of buffer space (about `60 bytes`), so if we send a
 | |
| longer command list to the interface, it will drop all bits after 60 and crash.
 | |
| 
 | |
| Our first solution was to add `1s` sleep between sending subsequent
 | |
| commands, however this made drawings really slow, and led to artifacts from
 | |
| ink bleeding while the plotter is waiting for the next command.
 | |
| 
 | |
| Another Recurser, Francis, pointed us to a clever hack in the [wait function]
 | |
| from [hpgl.js]. This function uses the HPGL command `OA;` to block execution
 | |
| until the plotter is finished with the current instruction. When the plotter
 | |
| executes `OA;` it sends the current pen position, but it first needs to
 | |
| wait until the pen has stopped moving. Thus we can batch a bunch of commands
 | |
| and append `OA;`; once we read the position from the serial,
 | |
| we know that the previous batch is consumed and we can send the next batch.
 | |
| 
 | |
| [wait function]: https://djipco.github.io/hpgl/hpgl.js.html#line1535
 | |
| [hpgl.js]: https://github.com/djipco/hpgl
 | |
| 
 | |
| The code for this trick looks like:
 | |
| 
 | |
| ```python
 | |
| import serial
 | |
| 
 | |
| # combine command together with maxlen buflen and expose as an iterator
 | |
| def stitch(body, buflen=40):
 | |
|     start = ["IN;PU;", "SP1;"]
 | |
|     end = ["SP0;"]
 | |
|     final = start + body + end
 | |
| 
 | |
|     ## read in 20 bytes at a time or boundary
 | |
|     count = 0
 | |
|     buf = []
 | |
|     for ins in final:
 | |
|         if count + len(ins) >= buflen:
 | |
|             yield "".join(buf)
 | |
|             buf = []
 | |
|             count = len(ins)
 | |
|         else:
 | |
|             count += len(ins)
 | |
|         buf.append(ins)
 | |
| 
 | |
|     # send rest of the code
 | |
|     yield "".join(buf)
 | |
| 
 | |
| # cmds is a list with semicolon attached to the command
 | |
| def exec_hpgl(cmds):
 | |
|     port = "/dev/cuaU0"
 | |
|     speed = 9600
 | |
| 
 | |
|     body = stitch(cmds)
 | |
|     with serial.Serial(port, speed, timeout=None) as plt:
 | |
|         for ins in body:
 | |
|             # size (Esc-B) returns bufferlen
 | |
|             plt.write(ins)
 | |
|             # For block sent, end with OA, which reports back current
 | |
|             # position on the pen
 | |
|             plt.write("OA;")
 | |
|             c = ""
 | |
|             data = ""
 | |
|             while c != '\r':
 | |
|                 c = plt.read()
 | |
|                 data += c
 | |
|                 print("read: {}".format(map(ord, c)))
 | |
|             print("OA return: {}".format(data))
 | |
|             # We got data, mean OA got executed, so the instruction buffer
 | |
|             # is all consumed, ready to sent more.
 | |
| ```
 | |
| 
 | |
| This made the plotter super fast!!!
 | |
| 
 | |
| ## Everything's a line anyway!
 | |
| After initial success we quickly realized our plotter does not respond to any
 | |
| commands that involved HP-GL instructions to draw basic geometry, like `CI` for
 | |
| circle.
 | |
| 
 | |
| A re-reading of the manual made us realize that HP sold this functionality as a
 | |
| [hardware adapter board that plugs into the
 | |
| bottom](https://support.hp.com/us-en/document/bpp01354). Our plotter did not arrive with this board.
 | |
| 
 | |
| But everything in computer graphics is a line anyway, right? Using some [root of
 | |
| unity](http://mathworld.wolfram.com/RootofUnity.html) math, we came up with a circle
 | |
| drawing routine.
 | |
| 
 | |
| <video width="600" height="450" controls>
 | |
|   <source src="/images/7440a_circle.mp4" type="video/mp4">
 | |
| </video>
 | |
| 
 | |
| ## This is only the beginning
 | |
| 
 | |
| Hardware wise, the only thing left to fix is our pens, which expired in
 | |
| 1996! We are yet to come up with a strategy to refill/replace them.
 | |
| 
 | |
| { width=600px }
 | |
| 
 | |
| I am also going to leave this plotter at RC (so that other Recursers can
 | |
| continue hacking on it), and buy another one for me to actually do some
 | |
| generative art. :-)
 | |
| 
 | |
| ## Notes
 | |
| 1. Big thanks to Amy and Francis and Alex S for their help at various points.
 | |
| 2. All of our python code is here: [https://github.com/dbalan/plotter-scripts](https://github.com/dbalan/plotter-scripts)
 | |
| 3. Thanks to all who proof read this post.
 | 
