Дешёвая и быстрая печать на чековом термопринтере
У нас, айтишников, часто возникает задача напечатать что-то простое и одноразовое — например, логин и пароль новому сотруднику, тикет для монтажника или что-то в этом роде. Тратить бумагу и картридж на подобное очень дорого. При этом есть принтеры с ультрадешёвой скоростной печатью, про которые редко кто вспоминает, хотя видим мы их все каждый день по много раз. Речь про чековые термопринтеры.
Начну, пожалуй, с демки, чтобы вы могли оценить скорость печати — 20 см. в секунду:
(На видео принтер замедляется пару раз в секунду из-за того, что мой скрипт не успевает достаточно быстро засылать команды. Спасибо Тому Скотту за вдохновение:))
Этот принтер я добыл на Авито за полторы тысячи рублей. Модель Citizen CT-S2000, умеет печатать на лентах разной стандартной ширины — от 58 мм до 80 мм. Принтер имел на корпусе следы долгой работы на, кажется, ресторанной кухне, которые удалось оттереть мылом и спиртом. Впрочем, этот принтер фиг убьешь: если термоголовка и отрезной нож исправны, он вам ещё послужит, ломаться там больше особо нечему (а он еще и водостойкий!).
Рулон ленты шириной 80 мм и длиной до ста метров обойдётся вам меньше чем в сто рублей. Ленты 58 мм стоят ещё меньше. Также, т.к. этот принтер печатает нагревая бумагу в определенных местах, ему не нужны картриджи.
Термопринтеры обычно работают по COM-интерфейсу (нативному или эмуляцией через USB), дорогие варианты имеют на борту Ethernet, WiFi или даже Bluetooth, но они слишком дорогие.
На сайте Citizen был богатый набор вариантов подключения — от нативного драйвера под Windows, PPD для CUPS, и до библиотек для Java. Нам с вами это всё не понадобится, потому что для максимальной простоты и скорости мы не будем рендерить задания на печать на сервере, а лишь будем слать команды принтеру на стандартном языке команд «ESC/POS».
Но сначала нам нужно один раз провести настройку принтера. Citizen предлагает настроить принтер через меню, которое он вам печатает (!) — экрана-то у него нет, или воспользоваться утилитой под Windows.
У моего принтера имеются интерфейсы USB и COM на 25 pin, под который не удалось быстро найти переходник с 9 pin — поэтому пользуемся USB. Драйвер под Windows установил виртуальный COM-порт, который мы выбираем в утилите настройки.
Внимательно просмотрите все параметры — нужно проверить тип бумаги (наклейки или обычная) и указать ширину заправленной в принтер ленты. Остальные настройки, типа яркости печати, тона встроенной пищалки и прочего — на ваш вкус.
Чтобы не забивать себе голову синтаксисом языка ESC/POS, воспользуемся Python-библиотекой python-escpos. В моём случае принтером управляет одноплатный компьютер Orange PI PC с Debian, но настройки в любой ОС будут идентичны.
Командой lsusb узнаём ID принтера, в моём случае это был 2730:0fff:
# lsusb
Bus 006 Device 002: ID 2730:0fff Citizen
Также нам нужно выяснить USB endpoints, для этого подставляем ID в команду:
# lsusb -v -d 2730:0fff | grep bEndpointAddress
bEndpointAddress 0x81 EP 1 IN
bEndpointAddress 0x02 EP 2 OUT
Запоминаем полученные значения и создаём тестовый скрипт на Python:
from escpos import Usb
p = printer.Usb(0x2730, 0x0fff, 0, 0x81, 0x02)
Если ваш принтер есть в списке явно поддерживаемых библиотекой, например, Epson TM-T88III, то можно применить его профиль, где авторы явно прописали, что из возможностей поддерживается, а что — нет. Почитать документацию по подключению принтеров можно тут.
Пользование библиотекой — тривиально, она полностью избавляет вас от изучения ESC/POS:
""" Выбор встроенного шрифта, выравнивания, размера """
p.set(font='a', align=u'left', height=3)
""" Печать картинки (помните про ширину ленты и DPI!) """
p.image('/home/test/example.png')
""" Вывод текста (кириллицей-только если поддерживает принтер,
иначе лучше напечатать текст на картинке и печатать ее) """
p.text("Hello, world!\n")
""" Генерация QR-кода """
p.qr("https://gbougakov.dev", size=5, center=True)
""" Генерация бар-кода (читайте Википедию, чтобы
правильно вычислить checksum) """
p.barcode('1324354657687','EAN13')
p.barcode('123456', 'CODE39')
""" Программная генерация бар-кода – если ваш принтер
не умеет генерировать нужный тип аппаратно """
p.soft_barcode('code39', '123456')
""" Надрезать ленту: """
p.cut(mode='PART')
""" или отрубить напечатанный кусок целиком: """
p.cut()
""" послать напряжение на разъём для денежного ящика кассы
(обычно под них выведено два контакта в разъёме RJ-25): """
p.cashdraw(2)
p.cashdraw(5)
Про последнюю команду — чуть поподробнее. На всех чековых принтерах выведен разъём, который отвечает за то, что сразу после выбивания чека у кассира сам открывается ящик, куда он складывает полученные от вас деньги. Стандартное напряжение — 24 вольта, ток — до одного ампера.
Не пытайтесь от него запитать Raspberry Pi или что-то подобное — в момент печати принтер обесточивает разъём. Так что для того, чтобы запитать, например, стрелку из светодиодов «возьми чек тут!» — сойдёт, но не более.
Напоследок — пример применения. В прошедшие выходные моя школа праздновала свой очередной день рождения (аж 143-й), и по традиции старшеклассники устраивали разные конкурсы и развлечения для младших классов и выпускников. Я отвечал за «Бинго» — мой принтер по нажатию кнопки печатал билеты для игроков, а на доску выводились цифры. QR-код на билете позволял проверить, правильно ли игрок вычеркнул цифры в процессе игры:
Для вандалоустойчивости Orange Pi был упрятан в фанерный подиум, накрепко прикрученный к принтеру, а команда на печать билетов была выведена на кнопки, подключённые к GPIO. Для подстраховки был добавлен I2C-экран, на который выводился IP-адрес Orange Pi, чтобы в случае чего знать, как зайти на него по SSH.