--- title: Изучаем протокол принтеров Niimbot description: Изучаем протокол принтеров Niimbot и печатаем этикетки, отправляя пакеты date: 2024-06-29T21:10:28+03:00 draft: false tags: - bluetooth - niimbot - термопечать - Niimbot B1 - Niimbot B110 - реверс-инжиниринг - javascript - typescript - web categories: - reverse-engineering featured_image: miniature.jpg lastmod: 2024-07-28T19:45:42+03:00 telegram_entry_id: "" type: default --- После того, как поигрались с [niimprint](/niimbot-d110-pc), захотелось чего-то большего. У меня появилась идея написать полноценный веб-интерфейс для печати, в котором можно будет и рисовать этикетки, и печатать. Для этого я решил изучить протокол принтеров. > ⚠ **ПРЕДУПРЕЖДЕНИЕ** > > Данный проект предназначен только для ознакомительных и образовательных целей. > Проект не связан с компанией Niimbot, не поддерживается ей и не предназначен > для использования в коммерческих целях без согласия владельца. ## Структура пакета Сейчас в моём владении два принтера D110 и B1. Вооружившись Wireshark и Android телефоном, снял дампы обмена данными с принтером по bluetooth. Для этого нужно было включить опцию "Bluetooth HCI Snoop Log" в настройках разработчика, а потом на компьютере после печати запустить `adb bugreport `. Изучив пакеты и сверившись с другими открытыми источниками, получилась такая структура пакета: ![niimbot packet](packet.png) * **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 (GetPrinterInfo) | | 0xde | | 0xe3 | PageEnd | | 0xe4 | | 0xf3 | PrintEnd | | 0xf4 | ## Важно знать ### Протокол обмена данным варьируется между разными моделями В основном это касается набора пакетов при непосредственно печати. Изучив код приложения, можно сделать вывод, что есть пять вариаций протокола + вариации для самих моделей. ![proto files](proto_files.png) ### Вероятно, протокол обмена данными может варьироваться даже в пределах ревизий одной модели Это можно увидеть в декомпилированном коде приложения: ![protocol tasks](protocol_tasks.png) ### Принтер искусственно занижает плотность печати при использовании неправильной или отсутствующей RFID метке Вот тут довольно интересно. Принтеры и приложения ведут себя по разному. **Метки нет вообще**: * **Niimbot Android** - Этикетку создать невозможно в принципе. * **Niimbot Windows** - D110 - не поддерживается, хоть и может печатать по usb. - B1 - печатать можно, игнорируя предупреждения, плотность очень низкая. * **Сторонние проекты** - D110 - печатает без проблем с нужной плотностью. - B1 - печатает с низкой плотностью. **Метка есть, но от бумаги размером, который не поддерживается принтером**: * **Niimbot Android** - D110 - печатать можно, игнорируя предупреждения, плотность очень низкая (подлость самого приложения). - B1 - аналогично с D110. * **Niimbot Windows** - D110 - печатать можно, игнорируя предупреждения, плотность очень низкая. - B1 - печатать можно, игнорируя предупреждения, плотность очень низкая. * **Сторонние проекты** - D110 - печатает без проблем с нужной плотностью. - B1 - аналогично с D110. Что касаемо самой метки - считывание происходит при закрытии крышки. Принтер видит метку даже если она снаружи корпуса. Так что можно просто приложить метку снаружи, закрыть корпус и печатать на чём попало. ## Источники * [Тут](https://github.com/DelphiTeacher/OrangeFreeSDK/tree/master/%E7%B2%BE%E8%87%A3%E6%99%BA%E6%85%A7%E6%A0%87%E7%AD%BE%E6%89%93%E5%8D%B0%E6%9C%BAJCPrint/Client/JCPrintSDK) и [тут](https://github.com/dadrum/niimbot_flutter_plugin/tree/main/android/app/libs) нашлась библиотека jcprintersdk для Java. Библиотека обфусцирована. * [kjy00302/niimprint](https://github.com/kjy00302/niimprint) - утилита на Python для печати изображений на принтерах niimbot. * [AndBondStyle/niimprint](https://github.com/AndBondStyle/niimprint) - доработанный форк niimprint. Также благодаря автору и человеку с неизвестным мне ником удалось получить актуальную версию jcprintersdk с низкой обфускацией путём декомпиляции Android приложения. * [ayufan/niimprint-web](https://github.com/ayufan/niimprint-web) - малофункциональный, но полезный проект печати через браузер.