13 KiB
title | description | date | draft | tags | categories | featured_image | lastmod | telegram_entry_id | type | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Изучаем протокол принтеров Niimbot | Изучаем протокол принтеров Niimbot и печатаем этикетки, отправляя пакеты | 2024-06-29T21:10:28+03:00 | false |
|
|
miniature.jpg | 2024-07-29T22:28:54+03:00 | default |
После того, как поигрались с niimprint, захотелось чего-то большего. У меня появилась идея написать полноценный веб-интерфейс для печати, в котором можно будет и рисовать этикетки, и печатать. Для этого я решил изучить протокол принтеров.
⚠ ПРЕДУПРЕЖДЕНИЕ
Данный проект предназначен только для ознакомительных и образовательных целей. Проект не связан с компанией Niimbot, не поддерживается ей и не предназначен для использования в коммерческих целях без согласия владельца.
Структура пакета
Сейчас в моём владении два принтера D110 и B1.
Вооружившись Wireshark и Android телефоном, снял дампы обмена данными с принтером по bluetooth. Для этого нужно было включить опцию "Bluetooth HCI Snoop Log" в настройках разработчика, а потом на компьютере после печати запустить adb bugreport <filename>
.
Изучив пакеты и сверившись с другими открытыми источниками, получилась такая структура пакета:
- Prefix – префикс
0x03
, присутствующий только при одной команде - Connect. - Head – всегда 2 байта
0x55
0x55
. - Command – ID команды (пакета).
- Data length – количество байтов данных, идущих далее.
- Data – непосредственно данные в количестве Data length.
- Checksum – вычисляется с помощью XOR всех байтов от Command до последнего байта Data.
- Tail – всегда 2 байта
0xAA
0xAA
.
Типы пакетов
На данный момент мне удалось идентифицировать следующие типы пакетов:
ID команды | Наименование | ID ответа |
---|---|---|
0x01 | PrintStart | 0x02 |
0x03 | PageStart | 0x04 |
0x05 | PrinterLog | 0x06 |
0x0b | AntiFake | 0x0c |
0x13 | SetPageSize | 0x14 |
0x15 | PrintQuantity | 0x16 |
0x1a | RfidInfo | 0x1b |
0x1c | RfidInfo2 | 0x1d |
0x20 | PrintClear | 0x30 |
0x21 | SetDensity | 0x31 |
0x23 | SetLabelType | 0x33 |
0x27 | SetAutoShutdownTime | 0x37 |
0x28 | PrinterReset | 0x38 |
0x40 | PrinterInfo | 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e |
0x54 | RfidSuccessTimes | 0x64 |
0x58 | SoundSettings | 0x68 |
0x70 | GetVolumeLevel / WriteRFID | 0x71 |
0x83 | PrintBitmapRowIndexed | ⚠ Без ответа |
0x84 | PrintEmptyRow | ⚠ Без ответа |
0x85 | PrintBitmapRow | ⚠ Без ответа |
0x8e | LabelPositioningCalibration | 0x8f |
0xa3 | PrintStatus | 0xb3 |
0xa5 | PrinterStatusData | 0xb5 |
0xaf | PrinterConfig | 0xbf |
0xc1 | Connect | 0xc2 |
0xda | CancelPrint | 0xd0 |
0xdc | Heartbeat | 0xdd, 0xdf, 0xde, 0xd9 |
0xe3 | PageEnd | 0xe4 |
0xf3 | PrintEnd | 0xf4 |
Далее рассмотрим основные пакеты подобнее.
Простой пакет
Данный тип пакета всегда содержит 0x01 в качестве данных.
| 1 |
| |
V1 |[ 1 ]|
Ответ:
| 1 |
| |
Ok |[ 1 ]|
Error |[ 0 ]|
0x01 PrintStart
Начало последовательности пакетов для печати. Формат данного пакета отличается в разных версиях протоколов. Варианты:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| | | | | | | | |
V1 |[ 1 ]| | | | | | | |
V3 |[ total_pages ]| | | | | | |
V4 |[ total_pages ]|[ 0 ]|[ 0 ]|[ 0 ]|[ 0 ]|[ page_color ]| |
V5 |[ total_pages ]|[ 0 ]|[ 0 ]|[ 0 ]|[ 0 ]|[ page_color ]|[ quality ]|
Значения:
- total_pages – итоговое количество страниц.
- page_color – цвет страницы (назначение неизвестно).
- quality – вероятнее всего, плотность печати.
ID Ответа: 0x02:
| 1 |
| |
Ok |[ 1 ]|
Error |[ 0 ]|
0xf3 PrintEnd
Начало данных страницы. ID Ответа: 0xf4. Простой пакет.
0x03 PageStart
Начало данных страницы. Вызывается между PrintStart и PrintEnd. ID Ответа: 0x04. Простой пакет.
0xe3 PageEnd
Конец данных страницы. Вызывается между PrintStart и PrintEnd. ID Ответа: 0xe4: Простой пакет.
Важно знать
Протокол обмена данным варьируется между разными моделями
В основном это касается набора пакетов при непосредственно печати. Изучив код приложения, можно сделать вывод, что есть пять вариаций протокола + вариации для самих моделей.
Вероятно, протокол обмена данными может варьироваться даже в пределах ревизий одной модели
Это можно увидеть в декомпилированном коде приложения:
Принтер искусственно занижает плотность печати при использовании неправильной или отсутствующей RFID метке
Вот тут довольно интересно. Принтеры и приложения ведут себя по разному.
Метки нет вообще:
- Niimbot Android
- Этикетку создать невозможно в принципе.
- Niimbot Windows
- D110 - не поддерживается, хоть и может печатать по usb.
- B1 - печатать можно, игнорируя предупреждения, плотность очень низкая.
- Сторонние проекты
- D110 - печатает без проблем с нужной плотностью.
- B1 - печатает с низкой плотностью.
Метка есть, но от бумаги размером, который не поддерживается принтером:
- Niimbot Android
- D110 - печатать можно, игнорируя предупреждения, плотность очень низкая (подлость самого приложения).
- B1 - аналогично с D110.
- Niimbot Windows
- D110 - печатать можно, игнорируя предупреждения, плотность очень низкая.
- B1 - печатать можно, игнорируя предупреждения, плотность очень низкая.
- Сторонние проекты
- D110 - печатает без проблем с нужной плотностью.
- B1 - аналогично с D110.
Что касаемо самой метки - считывание происходит при закрытии крышки. Принтер видит метку даже если она снаружи корпуса. Так что можно просто приложить метку снаружи, закрыть корпус и печатать на чём попало.
Источники
-
Тут и тут нашлась библиотека jcprintersdk для Java. Библиотека обфусцирована.
-
kjy00302/niimprint - утилита на Python для печати изображений на принтерах niimbot.
-
AndBondStyle/niimprint - доработанный форк niimprint. Также благодаря автору и человеку с неизвестным мне ником удалось получить актуальную версию jcprintersdk с низкой обфускацией путём декомпиляции Android приложения.
-
ayufan/niimprint-web - малофункциональный, но полезный проект печати через браузер.