HP 7475A Plotter: Hacking Chiplotle For Hardware Handshaking

Chiplotle seems like a good way to drive the HP 7475A plotter, but some preliminary tinkering showed that the plotter pen paused quite regularly while drawing. The plotter wakes up with hardware handshaking enabled, Chiplotle has a config file that lets you specify hardware handshaking, the cable has all the right connections for hardware handshaking, but peering at Der Blinkenlights showed hardware handshaking never happened: the data didn’t overrun, the buffer never filled up, and DTR remained solidly on.

Come to find out that Chiplotle sends data in half-buffer-size chunks (all code from baseplotter.py):

class _BasePlotter(object):
   def __init__(self, serial_port):
      self.type = '_BasePlotter'
      self._logger = get_logger(self.__class__.__name__)
      self._serial_port = serial_port
      self._hpgl = commands
      self._margins = MarginsInterface(self)
      self.maximum_response_wait_time = get_config_value('maximum_response_wait_time')

      #this is so that we don't pause while preparing and sending
      #full buffers to the plotter. By sending 1/2 buffers we assure
      #that the plotter will still have some data to plot while
      #receiving the new data
      self.buffer_size = int(self._buffer_space / 2)
      self.initialize_plotter( )

Every time something goes out to the plotter, this happens:

   def _write_string_to_port(self, data):
      ''' Write data to serial port. data is expected to be a string.'''
      #assert type(data) is str
      if not isinstance(data, basestring):
         raise TypeError('string expected.')
      data = self._filter_unrecognized_commands(data)
      data = self._slice_string_to_buffer_size(data)
      for chunk in data:
         self._sleep_while_buffer_full( )
         self._serial_port.write(chunk)

In order to figure out whether the plotter has enough room, Chiplotle must ask it:

   def _sleep_while_buffer_full(self):
      '''
         sleeps until the buffer has some room in it.
      '''
      if self._buffer_space < self.buffer_size:
         while self._buffer_space < self.buffer_size:
            time.sleep(0.01)

The self._buffer_space method contains the complete handshake:

   def _buffer_space(self):
      self._serial_port.flushInput()
      self._serial_port.write(self._hpgl.B().format)
      bs = self._read_port()
      return int(bs)

Assuming that Python can actually meter out a 0.01 second sleep, that’s a mere 10 ms; call it 10 character times at 9600 b/s. By and large, Chiplotle hammers away at the poor plotter while the buffer drains.

Now, that would be just ducky, except that the HP 7475A plotter dates back to slightly after microcontrollers were invented. The MC6802 trundles along at 1 MHz from a 4 MHz crystal, because it needed a quadrature clock, and takes a while to get things done. Responding to the buffer space request (a three-character sequence: ␛.B) requires the plotter to:

  • Stop plotting 
  • Answer the phone
  • Figure out what to do
  • Compose a reply
  • Drop it in the serial buffer
  • Resume plotting

Which take enough time to produce a distinct hitch in the gitalong. Some crude print debugging showed most of the delay happens between the write() and the read() tucked inside _buffer_space.

Linux handles serial port hardware handshaking far below the Python level, so the simplest fix was to rip out the line that checks for enough buffer space:

   def _write_string_to_port(self, data):
      ''' Write data to serial port. data is expected to be a string.'''
      #assert type(data) is str
      if not isinstance(data, basestring):
         raise TypeError('string expected.')
      data = self._filter_unrecognized_commands(data)
      data = self._slice_string_to_buffer_size(data)
      for chunk in data:
#         self._sleep_while_buffer_full( )
         self._serial_port.write(chunk)

And then the plotter races along without pauses, drawing as fast as it possibly can, with the DTR output blinking like crazy as Chiplotle dumps the character stream into the output buffer and the serial port hardware (*) managing the data flow. Apparently, detecting a buffer-full situation and dropping the DTR output requires only a few 6802 CPU cycles, which is what makes hardware handshaking such a good idea.

There’s a movie about that…

Hooray for Der Blinkenlights!

HP 7475A - serial port adapters - hardcore
HP 7475A – serial port adapters – hardcore

(*) Which is, of course, a USB-to-RS232 converter. I paid extra to get one that reports an FTDI chipset, which may mean the counterfeiters have upped their game since the Windows driver disaster. I actually tried it on the Token Windows box and it still works, so maybe it’s Genuine FTDI.