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
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)
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
write() and the
read() tucked inside
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.
Hooray for Der Blinkenlights!
(*) 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.