Приветствую всех!

Не так давно я уже рассказывал про то, какими были принтеры начала эпохи термопечати. И в той статье я упомянул, что подобные устройства использовались совместно с платёжными терминалами VeriFone.

Увы, найти именно такой принтер у меня не вышло, так что сегодня поговорим о его не менее популярном в данной области матричном собрате, который тоже оказался крайне интересным.



Итак, в сегодняшней статье поговорим о том, как устроен и работает такой принтер. Разберёмся, как подключить его к обычному компьютеру. Традиционно будет много интересного.

О чём я?


Давным-давно, где-то в середине восьмидесятых, не было простых и дешёвых термопринтеров. Поэтому чеки печатались на матричных экземплярах. Один из таких принтеров (в составе терминала Tranz 460) я уже мимоходом демонстрировал в одной из своих статей. Экземпляр вполне себе печатал (а также радовал ни с чем не сравнимыми звуками), но мне хотелось именно отдельно стоящий, причём желательно в таком же исполнении (принтеры подкладной печати являются по сути уменьшенными аналогами «больших» аналогов, но об этом несколько позже).

И вот ко мне в руки попал именно такой аппарат, так что можно и попробовать его подключить и запустить.

Обзор оборудования




А вот и сам экземпляр, VeriFone Printer 250. Называется он так, правда, только в собственной инструкции и в торговых каталогах, во всех остальных местах его именуют просто P250.


Выпускался с начала девяностых и является преемником более старого P200, использовался совместно с большинством старых терминалов VeriFone. Устройства типа Tranz (на фото), Omni (на КДПВ) были рассчитаны на работу с ним «из коробки», даже была соответствующая библиотека в составе SDK. Даже в более поздних Omni-3350, Omni-3750, оснащённых термопринтерами, поддержку внешних матричных до конца не выкинули.
Сам принтер очень похож на современный и практически ничем не выдаёт обывателю матричную натуру. Сверху у него две крышки — одна для моточка с лентой, под другой находится картридж. Два индикатора, кнопка протяжки бумаги, тумблер питания.



Снизу ничего интересного, за исключением P/N. Выпускалось множество модификаций такого принтера, которые хоть и были одинаковы на вид, совершенно отличались по функциям.



Сзади два разъёма miniDIN — питание и RS-232.



Принтер со снятыми крышками.



А вот окошко датчика наличия бумаги. Сделан он весьма своеобразно — контролирует он тут не конец ленты, а просто то, что рулон близок к окончанию. Если бумага кончится, ничто не будет мешать механизму отбить весь текст по пустому месту. Также этот датчик не работает, если крышка снята — из-за попадающего света принтер начинает думать, что бумага есть всегда.



Блок питания. Очень суровый, трансформаторный. На нём же указана распиновка разъёма.

Внутренности




Разумеется, принтер я разобрал. Ничего сложного в этом нет, просто берём широкую плоскую отвёртку и вставляем её в две прорези спереди, а затем осторожно снимаем крышку. К ней идут провода от тумблера и очень короткий шлейфик на плату индикации, который тоже надо аккуратно отсоединить. Вот и всё.



Снимем механизм. Принтер построен на микроконтроллере Z8. В отличие от того, что установлен в некогда упомянутом PrintPak, здешний чип с УФ-стиранием, что даже в теории даёт возможность смены шрифтов. Механизм посажен на четыре самореза через резиновые прокладки, аналогичные тем, что используются для гашения вибраций в компьютерных оптических приводах.

Расходники


Разберёмся с бумагой и картриджем.
В качестве ленты используется Epson ERC-23BR. Можно, конечно, вставить и другую (ERC-23P, ERC-23B), но тогда пропадёт возможность цветной печати.



VeriFone продавала и брендированные картриджи. Не думаю, что она выпускала их сама, скорее всего, это какой-то OEM. Лента красно-чёрная, что даёт возможность печатать двумя цветами.



Моточек бумаги. В эпоху термопринтеров его ширина выглядит нестандартной — 76 мм. Восьмидесятимиллиметровый рулон в принтер не лезет.



Сама бумага двухслойная, причём нижний слой чувствителен к ударам (на фото другой рулон, где я провёл ногтем по верхнему слою, что отпечаталось на нижнем). Это позволяло печатать сразу две копии чека за раз, при этом одна оставалась у продавца, другая выдавалась клиенту.

Механизм


Этот принтер по принципу действия сильно отличается от привычных нам матричников.



Вот, например, принтер подкладной печати, Star SP298.



Несмотря на то, что это чековый принтер, внутри него точно такая же конструкция с двумя шаговыми двигателями, механизмом протяжки бумаги, подвижной головкой с девятью иглами… Аналогичный механизм стоял в первых отечественных электронных кассовых аппаратах.



Здесь же устройство совершенно иное: иглы в головке расположены строго горизонтально. Управляет всем простой коллекторный двигатель, который крутит как валик протяжки бумаги, так и червячный вал, перемещающий головку. Редуктор рассчитан на то, что за один проход головки бумага сдвигается на расстояние одного пикселя.



Сама конструкция называется «shuttle impact printer» (линейно-матричный принтер), про неё даже была коротенькая статья тут. Принтеры большого формата тоже выпускались в подобном исполнении, обычно это большие и суровые аппараты для огромных объёмов печати. А вот немного про обслуживание таких монстров. По сравнению с традиционной конструкцией, у них намного выше скорость печати (конкретно у моего экземпляра она приближается к старым термопринтерам). Тем не менее, высокие даже по меркам матричных принтеров цена и шум сильно ограничивают их применение.





Цветная печать реализована просто — картридж просто наклоняется электромагнитом, отчего головка бьёт по другой половине ленты. На фото показаны оба положения.

К слову, в некоторых десктопных принтерах (типа Epson LX-300+) была возможность печати сразу четырьмя цветами (после добавления специального комплекта для модернизации под названием Color Kit). Принцип был аналогичным — шаговый мотор, синхронизированный с остальным механизмом, наклонял картридж, подставляя под головку нужную часть ленты. Тем не менее, полноценной цветной печатью назвать это было нельзя, при проходе несколькими цветами по одному месту лента пачкалась ещё не до конца высохшей краской и следующие листы становились грязными. Впрочем, для печати цветного текста или, например, цветных графиков, аппарат подходил идеально.

Первый запуск


Итак, с устройством разобрались. Можно пробовать включать.



Заправляем бумагу и включаем снова с зажатой кнопкой протяжки. Принтер начнёт печатать ASCII-таблицу.
Работает.

Подключение к компьютеру


Вообще, эти принтеры предназначались для работы с терминалами. Но, понятное дело, ничто не мешает нам подсоединить его к ПК.



Распиновка оказалась вот такая. К принтеру подошёл некогда спаянный мною кабель для прошивки терминала Tranz 460.
Кстати, вот как бывает: в мануале к Tranz'у распиновка скромно опущена (тогда мне пришлось прозванивать всё самому), зато здесь она дана.



В недрах принтера рядом с механизмом находятся DIP-переключатели. Помним об этом и не забываем выставить нужные параметры. Я оставил значения по умолчанию: 9600 бод, 7 бит, чёт.


Принтер можно подрубить к любому компьютеру с MS-DOS/Win9x, для чего надо установить на этот COM-порт драйвер принтера «Generic/Text Only» и в настройках включить рулонную подачу. Точно так же можно печатать из любого текстового редактора под DOS, где есть поддержка непрерывной печати и настройки ограничения числа символов в строке. Например, можно использовать всем известный «Лексикон».

Но, разумеется, если бы всё ограничивалось подключением принтера к старому компьютеру, то никакой статьи не было бы. На самом деле этот девайс умеет куда больше. Ну а как это использовать — будем разбираться.

Протокол принтера



Экземпляр, как и многие другие принтеры, управляется ESC-последовательностями. Тем не менее, это не ESC/P и не ESC/POS, обычно применяющиеся в таких принтерах. Сам протокол достаточно подробно описан в неком «VeriFone Printer 250 Reference and programmer's manual».



Для проверки пробуем отправить какую-то строку, после чего символ LF (0x0A). Принтер немедленно напечатает её.

При печати получаются огромные вертикальные поля, что тоже следует учитывать. Для того, чтобы автоматически промотать бумагу до конца, необходимо отправить символ FF (0x0C).



Избавиться от этого не выйдет даже при большом желании, такова конструкция. На фото видно, насколько большой путь проходит бумага от головки до отрывной зубчатой планки.

Разумеется, просто писать команды в терминал не так интересно, поэтому даже написал прогу для этого.
#include <iostream>
#include <string>
#include <windows.h>

using namespace std;

HANDLE hSerial;

uint8_t openPort(int portNumber) {
    char sPortName[10];
    sprintf (sPortName, "COM%i", portNumber);
    hSerial = ::CreateFile(sPortName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if(!GetCommState(hSerial, &dcbSerialParams)){
        cout << "getting state error\n";
        exit(0);
    }
    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 7;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = EVENPARITY;
    if(!SetCommState(hSerial, &dcbSerialParams)){
        cout << "error setting serial port state\n";
        exit(0);
    }

    cout << "Opened port: " << sPortName << endl;
    return 1;
}


uint8_t ReadCOM(uint8_t & sReceivedByte) {
    DWORD iSize;
     ReadFile(hSerial, &sReceivedByte, 1, &iSize, 0);  // получаем 1 байт
    return iSize;
    }

DWORD WriteCOM(uint8_t data) {
    DWORD dwBytesWritten;    // тут будет количество собственно переданных байт
    DWORD dwSize = 1;
    WriteFile(hSerial, &data, dwSize, &dwBytesWritten, NULL);
    return dwBytesWritten;
}

void print(string data) {
int len = data.length();
if(len > 40) len = 40;
for(int i = 0; i < len; i++) WriteCOM(char(data[i]));
WriteCOM(0x0A);
}

int main()
{
    SetConsoleCP(866);
    SetConsoleOutputCP(866);
    openPort(4);
    string s;
    cin >> s;
    print(s);
    return 0;
}


Теперь, убедившись, что принтер печатает, можно пробовать и другие команды.

Размер и цвет текста


Принтер позволяет печатать с двойной шириной или двойной высотой. И если ширина переключается одним-единственным байтом, то для изменения высоты требуется отдельная ESC-последовательность.

Функции для задания этих параметров получились вот такие:

void setHeight(int height) {
WriteCOM(0x1B);
WriteCOM(0x66);
if(height == 0) {
    WriteCOM(0x30);
    WriteCOM(0x30);
}
else if(height == 1) {
    WriteCOM(0x31);
    WriteCOM(0x31);
}
WriteCOM(0x3B);
}

void setWidth(int width) {
if(width == 0) {
    WriteCOM(0x1F);
}
else if(width == 1) {
    WriteCOM(0x1E);
}
}

В обоих случаях 0 — обычный размер, 2 — увеличенный.



Вот для примера печать с обычной и удвоенной шириной.

Русский шрифт




У принтера есть отдельная команда выставления кодировки. Но русской там нет.



С русским шрифтом всё куда интереснее, для него используется отдельная кодовая таблица. Причём она не соответствует ни 866, ни 1251, ни какой-либо ещё. Это просто все кириллические символы, которых нет в латинице (типа Э, Ю, Я, Ы, И, П, Ц...), записанные по порядку.
Соответственно, для печати строки на русском необходимо загнать так все символы, похожие на латинские, и с предварительным выбором кодировки те, которых в ASCII нет.



Осложняет ситуацию то, что буквы раскиданы как попало, то есть банальным смещением кода символа в таблице не обойтись. К слову говоря, в этом списке указаны адреса символов в ROM принтера, при печати же они лежат 20h:40h. И да, это всё, на что способен этот принтер, печать строчных букв не поддерживается.
Ничего, кстати, не смущает? Да, букв «З» и «Ч» нет, зато вместо них зачем-то есть такие истинно кириллические символы как «J», «U», "/", а также дублирующиеся из латиницы А, Е, Т и другие. Ну да ладно.

Итак, вот такая функция получилась в итоге:

void printCyrillic(string data) {
int len = data.length();
if(len > 40) len = 40;
for(int i = 0; i < len; i++) {
    if(uint8_t(data[i]) >= 0xA0) {
            WriteCOM(' ');
    }
    else if(uint8_t(data[i]) >= 0x80 && uint8_t(data[i]) <= 0x9F) {
            loadCyrillic(uint8_t(data[i]));
    }
    else {
            WriteCOM(uint8_t(data[i]));
    }
}
WriteCOM(0x0A);
}

Принцип очень простой: если символ из ASCII, грузим его так, если это кириллица, то вызываем отдельную функцию, иначе просто отправляем вместо этого символа пробел (да, печатать стиль SCP Foundation символ █ принтер тоже не умеет).
Я долго думал, как сделать саму загрузку кириллицы в буфер принтера, но так и не придумал чего-то лучше, нежели вручную прописать коды всех символов:

void loadCyrillic(uint8_t symbol) {
switch(symbol) {
case 0x80:
    WriteCOM('A'); //А
    break;
case 0x81:
    WriteCOM(0x03);
    WriteCOM(0x21); //Б
    WriteCOM(0x0F);
    break;
case 0x82:
    WriteCOM('B'); //В
    break;
case 0x83:
    WriteCOM(0x03);
    WriteCOM(0x37); //Г
    WriteCOM(0x0F);
    break;
case 0x84:
    WriteCOM(0x03);
    WriteCOM(0x2F); //Д
    WriteCOM(0x0F);
    break;
case 0x85:
    WriteCOM('E'); //Е
    break;
case 0x86:
    WriteCOM(0x03);
    WriteCOM(0x2F); //Ж
    WriteCOM(0x0F);
    break;
case 0x87:
    WriteCOM('3'); //З
    break;
case 0x88:
    WriteCOM(0x03);
    WriteCOM(0x25); //И
    WriteCOM(0x0F);
    break;
case 0x89:
    WriteCOM(0x03);
    WriteCOM(0x33); //Й
    WriteCOM(0x0F);
    break;
case 0x8A:
    WriteCOM('K'); //К
    break;
case 0x8B:
    WriteCOM(0x03);
    WriteCOM(0x2E); //Л
    WriteCOM(0x0F);
    break;
case 0x8C:
    WriteCOM('M'); //М
    break;
case 0x8D:
    WriteCOM('H'); //Н
    break;
case 0x8E:
    WriteCOM('O'); //О
    break;
case 0x8F:
    WriteCOM(0x03);
    WriteCOM(0x2A); //П
    WriteCOM(0x0F);
    break;
case 0x90:
    WriteCOM('Р'); //Р
    break;
case 0x91:
    WriteCOM('C'); //С
    break;
case 0x92:
    WriteCOM('T'); //Т
    break;
case 0x93:
    WriteCOM(0x03);
    WriteCOM(0x28); //У
    WriteCOM(0x0F);
    break;
case 0x94:
    WriteCOM(0x03);
    WriteCOM(0x24); //Ф
    WriteCOM(0x0F);
    break;
case 0x95:
    WriteCOM(0x03);
    WriteCOM(0x3D); //Х
    WriteCOM(0x0F);
    break;
case 0x96:
    WriteCOM(0x03);
    WriteCOM(0x39); //Ц
    WriteCOM(0x0F);
    break;
case 0x97:
    WriteCOM(0x03);
    WriteCOM(0xB9); //Ч
    WriteCOM(0x0F);
    break;
case 0x98:
    WriteCOM(0x03);
    WriteCOM(0x2C); //Ш
    WriteCOM(0x0F);
    break;
case 0x99:
    WriteCOM(0x03);
    WriteCOM(0x32); //Щ
    WriteCOM(0x0F);
    break;
case 0x9A:
    WriteCOM(0x03);
    WriteCOM(0x3F); //Ъ
    WriteCOM(0x0F);
    break;
case 0x9B:
    WriteCOM(0x03);
    WriteCOM(0x35); //Ы
    WriteCOM(0x0F);
    break;
case 0x9C:
    WriteCOM(0x03);
    WriteCOM(0x30); //Ь
    WriteCOM(0x0F);
    break;
case 0x9D:
    WriteCOM(0x03);
    WriteCOM(0x20); //Э
    WriteCOM(0x0F);
    break;
case 0x9E:
    WriteCOM(0x03);
    WriteCOM(0x22); //Ю
    WriteCOM(0x0F);
    break;
case 0x9F:
    WriteCOM(0x03);
    WriteCOM(0x3C); //Я
    WriteCOM(0x0F);
    break;
}
}

Проверяем, и вот результат:



Тестовые команды




Помимо непосредственно управления печатью есть несколько служебных последовательностей, в частности, запрос статуса или информации о принтере. Увы, актуальны они лишь для некоторых ревизий, мой же принтер на них не отвечает ничего.

Вот как-то так


Итак, принтер оказался определённо крайне интересным устройством, причём не только в плане конструкции, но и в плане управления (так как аппарат был создан задолго до массового распространения ESC/POS). Увы, постоянно его использовать не выйдет ввиду теперь уже малого распространения расходников для него. Тот же новый картридж пока что не вышло достать вообще нигде.
В более новых моделях терминалов VeriFone перешла на всем известную термопечать с неподвижной головкой, после чего внешние матричные и термопринтеры у неё окончательно ушли в историю.
Такие дела.

Ссылки






Возможно, захочется почитать и это:



Комментарии (14)


  1. dlinyj
    27.08.2023 08:23
    +4

    Какой прикольный аппарат. Матричные принтеры просто обожаю, достоинство печати чеков, что они не выцветают со временем. Интересно, он умеет в графику?


    1. MaFrance351 Автор
      27.08.2023 08:23
      +4

      В графику он умеет, причём печать достаточно необычная — вначале пробиваются чётные пиксели, потом нечётные. Там отдельный протокол для её печати, где вначале надо включить графический режим, а затем отправлять строки байт, где один бит — чётность, ещё два — константы, остальные — пиксели.
      И потом идёт «терминатор», который даёт команду выбить эту строку.

      Вот что об этом говорит мануал:


      Терминатор:


      Но это справедливо лишь для некоторых P/N, к которым мой принтер, увы, не относится. Он эту последовательность, по-видимому, игнорирует, так как печатает просто строку закорючек.


      1. dlinyj
        27.08.2023 08:23
        +1

        Я когда ковырялся с кассовыми принтерами, там было весьма непростой протокол для графики.


        1. MaFrance351 Автор
          27.08.2023 08:23

          Я когда-то разбирался с фискальным регистратором ("Феликс-РК" это был, к слову, возможно, про него даже будет отдельный пост). Тоже думал печатать графику. У него даже была тестовая прога (к слову, поддерживает ещё целую кучу кассовых аппаратов, ставится вместе с драйвером ККМ от Атол), и там можно было печатать графику, просто загружая BMPшку. Вообще, Феликс-РК графику изначально не поддерживает, но её можно было включить, если потыкать что-то в настройках (такая вот недокументированная возможность). Но прикол в том, что печаталось оно при обратном ходе ленты (то есть он её затягивал обратно в себя), и это надо было учитывать. Потом наигрался и таки раздобыл обычный принтер с протоколом ESC/POS, который вообще не вызвал никаких проблем и используется у меня и поныне (список покупок там напечатать или заметки какие-то).


          К слову, в известных мне прошивках для этого OMNI-395 печать графики не использовалась. Как мне объяснили — для большей совместимости, чтобы было легче одну прошивку портировать на разные модели терминалов.


    1. salnicoff
      27.08.2023 08:23
      +1

      Как правило, такие аппараты умели. Надо же логотип магазина печатать. :-) Правда, когда программисты смотрели документацию на команды для печати графики, волосы у них вставали дыбом. И не только на голове. :-) Подход к пересылке битов уж очень нестандартный, даже в сравнении с эпсоновским.


      1. MaFrance351 Автор
        27.08.2023 08:23
        +1

        Некоторые (вроде упомянутого мною "Феликса") умели, но не в произвольную графику, а в предварительно загруженные в память изображения.
        То есть можно было загрузить этот логотип и потом вызывать его печать. Но обычная отправка строки байт и печать как битмапа реализована в нормальном виде не была.


        Но, к счастью, большинство ESC/POS-принтеров в печать графики вполне умеют. И даже подход более-менее стандартный, не надо заниматься ерундой типа переключения режимов и отправки байт специального формата.


        1. salnicoff
          27.08.2023 08:23
          +1

          Ну, еще бы ESC/POS не умели... Это же стандарт обычных «Эпсонов», загнанный в маленький формат бумаги...


          1. MaFrance351 Автор
            27.08.2023 08:23
            +1

            Ну это понятно. Я про то, что с появлением таких принтеров печать графики перестала быть чем-то крайне сложным.


            1. salnicoff
              27.08.2023 08:23

              Дык первой версии эпсоновского стандарта уже лет 40, если не больше...


              1. MaFrance351 Автор
                27.08.2023 08:23

                Вот только старые кассовые принтеры вроде показанного в посте этому стандарту частенько не соответствовали.


  1. Markscheider
    27.08.2023 08:23
    +2

    Похожие, но более миниатюрные давным-давно встраивались в т.н. бухгалтерские калькуляторы (эпсон, ситизен). И там тоже была двухцветная лента!


    1. MaFrance351 Автор
      27.08.2023 08:23
      +1

      В бухгалтерских калькуляторах обычно были принтеры барабанного типа (как в АЦПУ), которые ввиду габаритов ничего кроме цифр и пары спецсимволов печатать не могли. И там не лента была обычно, а поролоновый валик, пропитанный краской.
      Вот парочка таких для примера:

      Этот же принтер матричный, с лентой и иглами. И по громкости он, кстати, уделывает даже некоторые «большие» матричники (это и есть одна из причин, почему в десктопных принтерах обычно были головки с традиционным расположением игл).

      Кстати, калькуляторы такие до сих пор продают (новые и по весьма негуманным ценам), и принтер там всё такой же. Просто потому что двумя цветами дешёвые и массовые термопринтеры печатать пока не научились.


      1. tormozedison
        27.08.2023 08:23
        +1

        Барабанный тоже интересно погонять отдельно от калькулятора.


        1. MaFrance351 Автор
          27.08.2023 08:23

          Есть такое в планах.