Getting a USB receipt printer working on Linux

In this post, I’ll step through how to get a thermal receipt printer with USB interface appearing on Linux. The aim of this is to be able to point a driver such as escpos-php at the device. The printer used here is an Epson TM-T20, which is very common in point-of-sale environments.

I have previously written quite a bit about how to use thermal receipt printer protocols, but the previous printer I covered had only a network interface, not USB like this one:

2015-03-printer-back
2015-03-printer-top

The directions below are for Debian, but could be adapted for any other Linux.

Find the device file

Plug in your printer, and check that usblp sees it:

dmesg
[12724.994550] usb 8-4: new full-speed USB device number 5 using ohci-pci
[12725.168956] usb 8-4: New USB device found, idVendor=04b8, idProduct=0e03
[12725.168963] usb 8-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[12725.168968] usb 8-4: Product: TM-T20
[12725.168971] usb 8-4: Manufacturer: EPSON
[12725.168975] usb 8-4: SerialNumber: ....
[12725.175114] usblp 8-4:1.0: usblp1: USB Bidirectional printer dev 5 if 0 alt 0 proto 2 vid 0x04B8 pid 0x0E03

This kernel module makes your printer visible as a device file, so that it can be accessed in the old-fashioned way. Find the new device file under /dev/usb:

ls /dev/usb

In my case, this was /dev/usb/lp1. The next step is to see if you can write to it:

echo "Hello" >> /dev/usb/lp1

Chances are, you will get a permission denied error at this point, so find out what group the printer is in:

stat /dev/usb/lp1

Which will show output something like:

File: ‘/dev/usb/lp1’
  Size: 0         	Blocks: 0          IO Block: 4096   character special file
Device: 5h/5d	Inode: 220997      Links: 1     Device type: b4,1
Access: (0660/crw-rw----)  Uid: (    0/    root)   Gid: (    7/      lp)
...

This file is owned by group lp (“line printer”). If your username was bob, you would add yourself to this group using:

sudo usermod -a -G lp bob

If you plan to build a web-based point-of-sale system with this, then also add the www-data user to that group.

Now log out and back in, and the previous test should now be working:

echo "Hello" >> /dev/usb/lp1

Troubleshooting: Check usblp

If these steps don’t work, then your computer ether doesn’t have, or isn’t using usblp You’ll need to check a few things:

  • Install a different linux-image if the driver is not on your computer at all.
  • modprobe or insmod usblp
  • blacklist a vendor driver which has claimed the interface.
    • run lsusb -v and usb-devices (look for driver=)

Printing something useful

As a duplicated section from my earlier post, the printer uses ESC/POS, which means it accepts plaintext with some special commands for formatting.

A simple receipt-generator, foo.php, might look like this:

<?php
/* ASCII constants */
const ESC = "\x1b";
const GS="\x1d";
const NUL="\x00";

/* Output an example receipt */
echo ESC."@"; // Reset to defaults
echo ESC."E".chr(1); // Bold
echo "FOO CORP Ltd.\n"; // Company
echo ESC."E".chr(0); // Not Bold
echo ESC."d".chr(1); // Blank line
echo "Receipt for whatever\n"; // Print text
echo ESC."d".chr(4); // 4 Blank lines

/* Bar-code at the end */
echo ESC."a".chr(1); // Centered printing
echo GS."k".chr(4)."987654321".NUL; // Print barcode
echo ESC."d".chr(1); // Blank line
echo "987654321\n"; // Print number
echo GS."V\x41".chr(3); // Cut
exit(0);

And you would send it to the printer like this:

php foo.php > /dev/usb/lp1

Scaling this up

The codes are quite tricky to work with manually, which is why I put together the escpos-php driver. You can find it at:

The above example would be written using escpos-php as:

<?php
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
$connector = new FilePrintConnector("/dev/usb/lp1");
$printer = new Printer($connector);

/* Print some bold text */
$printer -> setEmphasis(true);
$printer -> text("FOO CORP Ltd.\n");
$printer -> setEmphasis(false);
$printer -> feed();
$printer -> text("Receipt for whatever\n");
$printer -> feed(4);

/* Bar-code at the end */
$printer -> setJustification(Printer::JUSTIFY_CENTER);
$printer -> barcode("987654321");
$printer -> cut();
?>

This would be sent to the printer by loading it from the web, or running the script on the command-line:

php foo2.php

20 thoughts on “Getting a USB receipt printer working on Linux”

  1. Hi Mike,

    Heard that usblp is no longer and its depreciated. I have updated my version and now this is not working. How can this be resolved?

    Regards,
    Sharanga.

  2. Hi, according Tod Euro sign issue 39 I habe used your custom charset custom850!

    It Sprints only two of the for currency symbols of your test, the Euro sign Saint part oft it …

  3. Thanks for your work on this. It works a treat on my CBM 1000II which has a built in USB interface. Only thing that would make it complete would be a dithered ‘greyscale’ image print rather than the one in place currently.

  4. Hello !, I’m working on PHP with my printer EPSON, I need to make a cut but can not. I can not implement ESC / POS business rules, but I would like to know how they can cut where I form by sending the ticket. Thank you very much!

  5. Worked fine for me.

    Currently forming part of a staff in/out list, using two cheap thermal printers, CUPS remote printing and your instruction / scripts above.

  6. Hallo Mike,
    Many thanks for that. I do, however, get an error with my ZJ-5890C: Barcodes and graphics (tried with your github code) just won’t work. Barcodes are just blank (or, its contents are printed as characters), and logos will print a few pixels, then stop and print “0p01L02” as text.
    Any hint would be greatly appreciated.
    Thanks.

  7. You can create a simple script to be able to print TXT files or dump text to the printer using copy&paste:

    unix2dos | iconv -c -f UTF-8 -t 437 > /dev/usb/lp0

    I named my script “thermo” and deposited it into /usr/bin.

    Now you can just copy your text and then type “thermo” into a console and press “Shift+Ins” and/or type whatever you want to print. Then press “Enter” and “Strg+d” to stop editing and start printing.

    unix2dos makes sure the printer receives CR+LF at every new line.
    iconv simply translates UTF8 into the common printer default codepage 437. The -c option makes sure that characters which can’t be printed are ignored rather than cause an error and stop. You might want to create a “thermotest” script without -c and actual printer output to make sure all characters can be printed before you attempt to print.

    You might also want to reformat a text file to match the short lines of the printer. I have called mine “thermof” (thermoFormated):

    fmt -cu -w32 –goal 30 | unix2dos | iconv -c -f UTF-8 -t 437 > /dev/usb/lp0

    fmt will eliminate line wraps which don’t match with your printer width and add new ones at the correct positions. You need to adjust the -w parameter to the width of your own printer (mine is obviously 32 characters wide). You can use –goal to make the print more dense to safe paper rather than more easy to read. A goal of 2 characters less than the printer width gives you maximum density

    Cowsay can accomplish the same as fmt but not as good. But it prints a nice ASCII-Art box around your text. I use this script I call “thermotux” to print short messages to coworkers:

    cowsay -W28 -f tux | unix2dos | iconv -f UTF-8 -t 437 -c > /dev/usb/lp0

    The problem is that cowsay removes all blank lines and combines lines even separated by multiple CR so while it is nice to print out adresses, phone numbers and brief memos, it can’t be used to print proper lists or even tables!

  8. On the site: “https://github.com/mike42/escpos-php”, your link for “Getting a USB receipt printer working on Linux” doesn’t work.

    The used link is: “http://s//mike42.me/blog/2015-03-getting-a-usb-receipt-printer-working-on-linux”, it should be “http://mike42.me/blog/2015-03-getting-a-usb-receipt-printer-working-on-linux”.

  9. Hi mike…. help me please… 🙂

    Mike, i have write foo.php and located it at /var/www/html/foo.php

    I have execute foo.php using console like this command :
    php foo.php > /dev/usb/lp0
    and result is SUCCES. My Epson Printer TM220 can do printing and cut paper automatically.

    Then… i try printing by loading foo.php using browser.
    This is the problem; Printer is not printing the receipt, but in the screen appear information like this
    @EFOOCORP Ltd. EdReceipt for whatever dak987654321d987654321VA

    Mike… how can i do printing foo.php by loading it by browser.

    Thanks for u’r help

  10. Hi Mike,
    I am using TSC TTP 345 printer to print barcode. And I am unable to print.
    What is the possible problems ?
    Any help ………..

  11. I have android app which doesn’t have option for usb printer… but it can easily print by ip address… my printer is working fine on usb or Ethernet but is there any way that I can place some usb address at place of ip address to make it work for usb only.

  12. I tested this with an XP-58III with usb interface. Works well, now i just need a good POS software. Thank you for this article! Helped me a lot

  13. Hi Mike,
    I have a question. I downloaded the files at GitHub address. However, when I try to interpret the file “demo.php” on Linux (php demo.php) I obtain a series of errors. In particular the list is the following:

    PHP Fatal error: Uncaught Error: Call to undefined function Mike42\Escpos\PrintBuffers\mb_detect_encoding() in /var/www/html/Print/src/Mike42/Escpos/PrintBuffers/EscposPrintBuffer.php:95
    Stack trace:
    #0 /var/www/html/Print/src/Mike42/Escpos/Printer.php(979): Mike42\Escpos\PrintBuffers\EscposPrintBuffer->writeText(‘Hello world\n’)
    #1 /var/www/html/Print/example/demo.php(23): Mike42\Escpos\Printer->text(‘Hello world\n’)
    #2 {main}
    thrown in /var/www/html/Print/src/Mike42/Escpos/PrintBuffers/EscposPrintBuffer.php on line 95

    Can you please help me?
    Regards, Gianluca

  14. @Amir- Good question, I’ve got no idea. Things that come to mind include writing your own app using an Android USB API, or use a raspberry pi as a networked print server.

Leave a Reply

Your email address will not be published. Required fields are marked *