CUPS — the Common Unix Printing System — is an open source print spooling and scheduling system. One of the interesting aspects of CUPS is its filtering system, which can reconstitute print job data between formats. For example, rasterizing PDF graphics for output on non-PostScript printers.

Turns out there’s a CUPS filter for thermal printers similar to those Adafruit offers. With a little setup, it’s possible to have these producing razor-sharp graphics unlike anything I’ve seen from a thermal receipt printer before. Much nicer than our improvised Python library!

This guide is based on a tutorial by Stewart Russell, with some changes and additions to work with Adafruit’s various thermal printers.

Required items:

  • Raspberry Pi computer (any model or version)
  • Adafruit Mini Thermal Receipt Printer (any model, though the “Tiny” model with USB is easiest to interface)
  • Thermal paper roll(s) — the smaller printer models require special smaller rolls — and you may want at least one spare to run through while experimenting with settings
  • 5V power supply (2A or larger)
  • DC jack adapter and perhaps some breadboard jumper wires
  • Latest “full” version of Raspbian or Raspberry Pi OS from Raspberry Pi web site (not the “Lite” version)
  • 8 GB or larger SD/microSD card as appropriate
  • Monitor, keyboard and mouse
  • Network connection — Ethernet or WiFi

Some prior Raspberry Pi experience is assumed — downloading the OS, writing an SD card, basic system and network configuration, etc. You can search the Adafruit Learning System for related guides if any of this is unfamiliar.

If you’re using a TTL printer (not USB), DO NOT connect it to the Raspberry Pi yet! It will spit paper like mad. Some system configuration is required first…

Download the latest full (not “Lite”) version of Raspberry Pi OS or Raspbian from the official Pi downloads page. You’ll also need a keyboard, mouse and monitor attached. Write the OS to an 8GB or larger SD card, insert in the Raspberry Pi and power it up. After a minute or two you’ll see the Pi desktop.

On first boot, the system will prompt for some basic configuration like language and time zone. Once that’s set up to your liking, some next steps involve typing commands in the Terminal. From the “Pi” menu at the top left, select Accessories→Terminal.

In the Terminal window, type:

sudo raspi-config

Use the down arrow key to navigate to the “Interfacing Options” selection, then press Return.

Select “Serial.”

When asked about the login shell, select “No.”

When asked about the serial port hardware, select “Yes(this option might not show up on older Pi models, where it’s always on).

Navigate back to the top menu and select “Finish.” You’ll be asked whether to reboot. Select “Yes” unless there’s other setup you want to do on your own first (such as networking) before rebooting.

Optional but recommended steps include:

  • Change hostname to distinguish it from other Raspberry Pi systems on the network
  • Change password (everyone knows the default)
  • Disable overscan
  • In “Localisation Options,” set up Locale, Timezone, Keyboard, etc.

You can use the graphical interface (PiPreferencesRaspberry Pi Configuration) for these other settings, but the serial hardware options should be done with raspi-config in the terminal.

All the Adafruit thermal printer varieties are bare units; they don’t have a DC barrel jack for power. Use a Female DC Power Adapter to connect to a 5V 2A power supply (our 5V 4A and 10A supplies can also work, if you already have one around, but do not use a 12V adapter).

During initial testing, you can press a couple of breadboard jumper wires into the printer’s JST power plug; the red wire is +, black wire is –. This is not very durable though…once you’re confident everything is working, you can remove the wires, clip off the JST plug, strip the ends and screw them directly to the adapter’s terminals.

For TTL printers (not USB): connect GND, RX and TX to the Raspberry Pi GPIO header in the positions shown. The transmit/receive lines will cross: the TX pin on the printer connects to RXD on the Pi, RX on the printer to TXD on the Pi.

If using the “Mini” Thermal Receipt Printer, the data plug from the printer can fit directly over these pins. Handy!

The “Tiny” and “Nano” printers (and “Printer Guts”) have a single 5-pin connector for power and data. Use something like F-M jumper wires or a Pi Cobbler and breadboard to make these connections. You’ll also need to split GND from the power supply to both the printer and the Pi GND pin.

The “Tiny” printer also features USB — that’s much easier if you have a port available!

It’s perfectly safe to connect the TX/RX directly to the Raspberry Pi GPIO pins. Though these printers are sometimes called “5V TTL,” in reality the controller logic all runs at 3.3 Volts…the inputs are simply 5 Volt tolerant so they can work with Arduino and similar.

If using a TTL printer and it starts printing a bunch of junk, disconnect power immediately! Return to the raspi-config utility and disable the serial console, then reboot.

Your thermal printer may have arrived with a test page in the box or the paper bay. If not, or if you threw that away, you can generate a new one by installing a roll of paper and holding the feed button (on printers that have one) while connecting power, or tapping the button on the back of the “Nano” printer or the “Printer Guts.”

Look for the baud rate that’s printed near the bottom of the page. This is typically either 9600 or 19200 baud. This is important…you’ll need to know the correct value for your printer later.

(Don’t fret if your printer is “only” 9600 baud. This has no impact on the speed of printing! The paper feed rate is really the bottleneck…the baud rate just determines how we’ll get the Pi and printer communicating.)

Once your printer’s connected, powered up and not spouting junk, launch a Terminal window and we’ll enter a few commands…

For a TTL (not USB) printer, type the following (substitute the correct baud rate for your printer):

Download: file
stty -F /dev/serial0 19200
echo -e "This is a test.\\n\\n\\n" > /dev/serial0

(In older versions of Raspbian we’d use /dev/ttyAMA0 instead. If the above commands present a problem, check what’s in the /dev directory and use serial0 or AMA0 as necessary.)

For a USB printer, type:

Download: file
echo -e "This is a test.\\n\\n\\n" > /dev/ttyUSB0

If your printer outputs only gibberish: check the baud rate and try again. Substitute 9600 or 19200 as needed.

Sometimes there’s a little bit of gibberish at the start of the first line printed; some residue in the serial buffer. This won’t happen on subsequent lines or after we’re done fully configuring the printer.

If nothing happens, check the following:

  • Is the printer connected to a 5V power source, 2A or larger? You should see an occasional LED blink from the printer.
  • Is thermal paper installed in the bay?
  • For TTL printers: are the TX/RX pins connected between Pi and printer, and do they cross? (i.e. TX on printer should go to RXD on Pi.) Is GND connected between printer and Pi?
  • For TTL printers: did you disable the serial console option and enable the serial port hardware? Check with raspi-config.

Commence Major Installation!

Once the printer passes the above basic test, it’s time to move on to software.

Make sure the package database is up-to-date, then install several packages…

Download: file
sudo apt-get update
sudo apt-get install libcups2-dev libcupsimage2-dev git build-essential cups system-config-printer

Though not unusually large (about 75 MB total), there are many small pieces to process. This could take about 30 minutes to install, so you might want to go mow the lawn or something.

Once that’s done, download and install this CUPS filter for the thermal printer:

Download: file
git clone
cd zj-58
sudo ./install

Now we’ll switch over to using the GUI for the rest.

From the Pi menu, select Preferences→Print Settings.

If the “Print Settings” menu option is not present, the ZJ-58 CUPS filter didn’t install. Check the above steps and try again.

In the Print Settings dialog, click “Unlock” at the top right and enter the admin password. In earlier versions of Raspbian, the Unlock button is not present and this step is not required.

The “Add” button should be available now. Click that.

For a USB printer: you should see “USB Serial Port #1” in the Devices list. Highlight that item (not Serial Port #1, but “USB Serial Port #1” specifically), select the correct baud rate, then click the “Forward” button.

For TTL printers: If “Serial Port #1” is present, select that and set the baud rate. Otherwise (slightly older Raspbian OS), highlight “Enter URI” in the Devices list. Then in the device URI field, type “serial:/dev/serial0?baud=19200” (or 9600) (and might be “ttyAMA0” instead of “serial0”, depending on commands tested earlier). The device name is case-sensitive, so get this exactly right!

Click the “Forward” button. On the next form, click “Select printer from database,” then scroll to the very bottom of the list and select “Zijiang” — this is a brand of thermal printer that happens to use the same command set.

Click “Forward” again, and on the next form select the “Zijiang ZJ-58” driver:

“Forward” once more, then you’ll have the option of assigning your printer a name…or just stick with the default ZJ-58:

Finally, click “Apply” and you’ll now see the ZJ-58 printer in the Print Settings list:

Click the ZJ-58 icon, then from the “Printer” menu select “Properties”:

Set “Feed distance after print” to “feed 12mm,” then click OK.

Your mileage may vary; if you want more or less paper feed after each print, this is where you’ll find it later.

After clicking “OK,” the printer should now fully work with the Raspberry Pi via the “lp” service.

Let’s give it a try! Back to the Terminal window…

Download: file
echo "This is a test." | lp

This should print the “This is a test.” string again…but unlike the first test, you’ll notice this one isn’t using the printer’s internal font…it’s actually rasterizing the text and we can tweak settings such as size.

You can try a printing bitmap as well:

Download: file
lp -o fit-to-page /usr/share/raspberrypi-artwork/raspberry-pi-logo.png

“-o fit-to-page” is important — most graphics, if printed “actual-size,” would print just a narrow strip cropped from the left side.

Most raster formats are supported, and for vector there’s PDF.

SVG is not directly supported. If you try to print an SVG file, you’ll see a bunch of text instead! You can use the LibreOffice Draw application included with Raspbian to open SVG files and save them as PDF for printing.

The Raspberry Pi logo shown is from a raster PNG file, you can see it’s a bit “dithery.” The dragon was an SVG file converted to PDF…this uses better halftoning and is amazingly sharp.

CUPS is a complex system way beyond the scope of this guide (there are entire books on the subject), but the CUPS web site explains some command-line options to get you started.

Accessing the printer from other systems on the same network requires just a few extra clicks…

From the Pi menu, select Preferences→Print Settings.

Double-click the ZJ-58 printer, or highlight it and select Printer→Properties:

In the “Policies” section, make sure “Shared” is checked, then click OK.

Returning to the main Print Settings form, now select Server→Settings…

Check the “Publish shared printers connected to this system” box, then click OK.

The printer is now accessible to other systems…even non-Linux computers running Windows or Mac OS X.

For example, on a Mac, go to the “Printers and Scanners” system preference form, click the add (+) icon, and you’ll see the networked printer available there:

It’s then possible to print to the thermal printer from any application.

Due to the peculiar media size, this will require some trial and error and experimentation. Though the media size is described as “58mm” wide, the actual imageable area is more like 48mm. If you try to print any “normal size” artwork, or if Media Size is set to “Letter,” you’ll just get a narrow strip from the left edge of the page.

This guide was first published on Apr 19, 2016. It was last updated on Apr 19, 2016.