A Python interface to a large-format pen plotter

Lead Image © Daniel Villeneuve, 123RF.com

Pi Plot

Getting a large-format plotter operational presented a personal challenge. A Raspberry Pi with a USB-to-serial dongle was the easiest way to start plotting on my home network.

Shortly after the dot matrix printer arrived on the scene, engineers created the pen plotter [1]. These output devices function more like modern CNC machines [2] than their dot matrix and laser cousins. The advantage that these machines offered was color output and potentially very large output size.

Instead of scanning pixel by pixel, a plotter at its most basic uses a set of move-draw commands. Ink comes from pens that trace out the lines one complete segment at a time, rather than dot by dot in a big grid (Figure 1).

Figure 1: The pen carriage of the plotter, or its equivalent of an ink cartridge. Here, individual pens can be swapped to change color and line thickness.

Other functions help draw squares, triangles, arcs, text, and more complex shapes. In the end, though, it's all a series of lines (Figure 2).

Figure 2: Output from a traditional printer versus a plotter. The printer will approximate lines by filling in the nearest pixel. The plotter draws a line directly from one point to the next.

The plotter manual included sample code written in Basic for an HP computer and a picture of what it should produce. I translated the program into Python, got the plotter talking via serial, cut and loaded a sheet of paper, and ran the program. I was thrilled when the finished plot matched the picture in the book.

That program was the inspiration for this software. Current printers have built-in menus for setup and configuration. The plotter does have manual controls, but they focus purely on paper alignment, paper size, and pen selection. Thus, I needed to create my own interface to the plotter.

Inkscape [3] is the easiest Linux package I've found to create graphics to plot. It will generate an HPGL file (HPGL commands in the proper order for output [4]), but it doesn't have any mechanism actually to transmit them serially to the plotter. See the Accommodating Inkscape" box for more information.

Accommodating Inkscape

This software was written specifically to accommodate the HPGL files generated by Inkscape. For example, Inkscape only places a select pen command at the top of the file before any drawing commands. Although it's perfectly legal to change pens in the middle of a drawing, Inkscape doesn't do this. Thus, it's assumed that once a select pen command is found, there won't be another one.

Similarly, it is sometimes necessary to flip or shift the drawing to get it aligned properly on the paper. That's why these functions are included but things like rotation or scaling are not. If I need to do anything more drastic than these basic operations, I go back to Inkscape.

Talking to a Plotter

The plotter is a serial device. The same protocol that you might have used to dial up your ISP in the days of the modem is used here to send commands to the plotter. Your modem probably reached a blazing 56Kbps, whereas the plotter still prefers 9600 baud (no "K" involved!). Even at that speed, however, it's possible to send data faster than the plotter can handle it.

As soon as a command is received, the plotter will attempt to carry it out. However, it takes time physically to go pick up a pen from the carriage, move the paper around, and get in position to draw a line. Even this slow serial speed is capable of sending 80 commands a second, or a lot of lines to draw!

The plotter has a small buffer, enough for 1,024 characters. When that fills up, the plotter will simply ignore any additional commands until the buffer has room again. This translates to lost drawing commands. Luckily, methods are available to handle flow control. Two methods are a part of the serial protocol and the third is implemented solely by the plotter.

Hardware flow control relies on additional data lines in the serial port. RTS (request to send) and CTS (clear to send) are physical signals that get passed down the serial cable on dedicated wires. RTS is connected on the computer end and CTS is connected on the receiving end. When the computer is ready to send, it raises RTS to a logical high voltage level (12V in an RS232 system). The computer then sends data until the receiving device drives CTS low. The computer sees this low voltage level and stops sending.

Software flow control sends control characters inline with the serial data stream. This is convenient because RTS and CTS aren't always available in all serial drivers. However, it does not allow raw binary data to be transmitted without special considerations. Binary data may contain the STOP or START control characters, which will dutifully stop the data flow, even if they are simply in the middle of an otherwise legal data packet.

Plotter "command mode" is the mode that I use. It doesn't require changing any settings on the plotter or filtering the plot data. Although the manual says this is the least efficient method, it is the easiest to implement. Computers have also advanced significantly, such that I'm not worried about the extra processor cycles.

The plotter offers several commands accessed by sending an escape (0x1B) character followed by a command (usually two more characters). One of the commands the plotter offers is "buffer size remaining." After sending each plotter command, the program asks "how much space is left?" If the buffer size remaining is less than 64 characters, then the program loops slowly without sending data until the buffer space goes above 64 characters.

The Curses Library

Python added support for the curses library in version 1.6. The purpose of the curses library is to provide screen-handling functions for character cell displays (terminals). Today, the terminal is probably an xterm running on your desktop or maybe a virtual terminal accessed by Ctrl+Alt+F1.

Looking back a little bit – after teletypes and before desktop PCs – terminals used to be the primary interface to a computer (e.g., a mainframe). Terminals were produced in many varieties, and each had its own set of capabilities, screen sizes, function keys, and support for color (or lack thereof).

The curses library was created to make all of these differences disappear. When a curses program is initiated, it detects all of the terminal's capabilities. The programmer can then query what functions are available, such as color, character blinking, underline, and so forth. If a function is not available, curses will roll to the next closest option, or the programmer can actively specify a different display attribute. Thus, curses takes care of how to tell a terminal to go to row 20, column 5 and print "Hello, World!" and lets programmers do this regardless of which terminal they are currently using.

When programming with the curses library, you will notice that the coordinates come in y, x order instead of x,y. No one is really sure why it was done this way, but it's been that way for the life of the library so it's too late to change it now.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content