2024-07-25 21:06:25 +03:00
---
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
2024-07-29 22:31:06 +03:00
lastmod: 2024-07-29T22:30:57+03:00
2024-07-25 21:06:25 +03:00
telegram_entry_id: ""
type: default
---
После того, как поигрались с [niimprint ](/niimbot-d110-pc ), захотелось чего-то большего. У меня появилась идея написать полноценный веб-интерфейс для печати, в котором можно будет и рисовать этикетки, и печатать. Для этого я решил изучить протокол принтеров.
<!-- more -->
2024-07-28 19:46:20 +03:00
> ⚠ **ПРЕДУПРЕЖДЕНИЕ**
>
> Данный проект предназначен только для ознакомительных и образовательных целей.
> Проект не связан с компанией Niimbot, не поддерживается ей и не предназначен
> для использования в коммерческих целях без согласия владельца.
2024-07-25 21:35:25 +03:00
## Структура пакета
Сейчас в моём владении два принтера D110 и B1.
2024-07-26 11:59:01 +03:00
Вооружившись Wireshark и Android телефоном, снял дампы обмена данными с принтером по bluetooth. Для этого нужно было включить опцию "Bluetooth HCI Snoop Log" в настройках разработчика, а потом на компьютере после печати запустить `adb bugreport <filename>` .
2024-07-25 21:06:25 +03:00
2024-07-26 07:56:18 +03:00
Изучив пакеты и сверившись с другими открытыми источниками, получилась такая структура пакета:
2024-07-25 21:06:25 +03:00
![niimbot packet ](packet.png )
* **Prefix** – префикс `0x03` , присутствующий только при одной команде - **Connect** .
* **Head** – всегда 2 байта `0x55` `0x55` .
* **Command** – ID команды (пакета).
* **Data length** – количество байтов данных, идущих далее.
* **Data** – непосредственно данные в количестве **Data length** .
* **Checksum** – вычисляется с помощью XOR всех байтов от **Command** до последнего байта **Data** .
2024-07-25 21:35:25 +03:00
* **Tail** – всегда 2 байта `0xAA` `0xAA` .
2024-07-28 13:24:34 +03:00
## Типы пакетов
Н а данный момент мне удалось идентифицировать следующие типы пакетов:
2024-07-28 23:10:28 +03:00
| 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 |
2024-07-28 13:24:34 +03:00
2024-07-28 23:10:28 +03:00
Далее рассмотрим основные пакеты подобнее.
2024-07-29 22:29:05 +03:00
### Простой пакет
Данный тип пакета всегда содержит **0x01** в качестве данных.
```
| 1 |
| |
V1 |[ 1 ]|
```
Ответ:
```
| 1 |
| |
Ok |[ 1 ]|
Error |[ 0 ]|
```
2024-07-28 23:10:28 +03:00
### 0x01 PrintStart
Начало последовательности пакетов для печати. Формат данного пакета отличается в разных версиях протоколов. Варианты:
2024-07-29 22:14:37 +03:00
```
| 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** – цвет страницы (назначение неизвестно).
2024-07-29 22:29:05 +03:00
* **quality** – вероятнее всего, плотность печати.
ID Ответа: **0x02** :
```
| 1 |
| |
Ok |[ 1 ]|
Error |[ 0 ]|
```
### 0xf3 PrintEnd
Начало данных страницы.
2024-07-29 22:31:06 +03:00
2024-07-29 22:29:05 +03:00
ID Ответа: **0xf4** .
2024-07-29 22:31:06 +03:00
2024-07-29 22:29:05 +03:00
[Простой пакет ](#простой-пакет ).
### 0x03 PageStart
Начало данных страницы. Вызывается между **PrintStart** и **PrintEnd** .
2024-07-29 22:31:06 +03:00
2024-07-29 22:29:05 +03:00
ID Ответа: **0x04** .
2024-07-29 22:31:06 +03:00
2024-07-29 22:29:05 +03:00
[Простой пакет ](#простой-пакет ).
### 0xe3 PageEnd
2024-07-29 22:14:37 +03:00
2024-07-29 22:29:05 +03:00
Конец данных страницы. Вызывается между **PrintStart** и **PrintEnd** .
2024-07-29 22:31:06 +03:00
ID Ответа: **0xe4** .
2024-07-29 22:29:05 +03:00
[Простой пакет ](#простой-пакет ).
2024-07-29 22:14:37 +03:00
2024-07-28 13:24:34 +03:00
2024-07-25 21:35:25 +03:00
## Важно знать
### Протокол обмена данным варьируется между разными моделями
2024-07-25 21:40:53 +03:00
В основном это касается набора пакетов при непосредственно печати. Изучив код приложения, можно сделать вывод, что есть пять вариаций протокола + вариации для самих моделей.
![proto files ](proto_files.png )
2024-07-25 21:35:25 +03:00
2024-07-26 07:56:18 +03:00
### Вероятно, протокол обмена данными может варьироваться даже в пределах ревизий одной модели
2024-07-25 21:35:25 +03:00
Это можно увидеть в декомпилированном коде приложения:
2024-07-25 21:40:53 +03:00
![protocol tasks ](protocol_tasks.png )
2024-07-25 21:35:25 +03:00
### Принтер искусственно занижает плотность печати при использовании неправильной или отсутствующей RFID метке
Вот тут довольно интересно. Принтеры и приложения ведут себя по разному.
**Метки нет вообще**:
* **Niimbot Android**
- Этикетку создать невозможно в принципе.
* **Niimbot Windows**
- D110 - не поддерживается, хоть и может печатать по usb.
- B1 - печатать можно, игнорируя предупреждения, плотность очень низкая.
* **Сторонние проекты**
- D110 - печатает без проблем с нужной плотностью.
- B1 - печатает с низкой плотностью.
**Метка есть, но от бумаги размером, который не поддерживается принтером**:
2024-07-26 07:56:18 +03:00
* **Niimbot Android**
2024-07-25 21:35:25 +03:00
- D110 - печатать можно, игнорируя предупреждения, плотность очень низкая (подлость самого приложения).
- B1 - аналогично с D110.
* **Niimbot Windows**
- D110 - печатать можно, игнорируя предупреждения, плотность очень низкая.
- B1 - печатать можно, игнорируя предупреждения, плотность очень низкая.
* **Сторонние проекты**
- D110 - печатает без проблем с нужной плотностью.
- B1 - аналогично с D110.
2024-07-25 21:40:53 +03:00
Что касаемо самой метки - считывание происходит при закрытии крышки.
2024-07-26 11:59:01 +03:00
Принтер видит метку даже если она снаружи корпуса. Так что можно просто приложить метку снаружи, закрыть корпус и печатать на чём попало.
## Источники
2024-07-28 19:34:41 +03:00
* [Тут ](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. Библиотека обфусцирована.
2024-07-26 11:59:01 +03:00
* [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 ) - малофункциональный, но полезный проект печати через браузер.
2024-07-31 15:55:58 +03:00
* [Отсюда ](https://oss-print.niimbot.com/public_resources/static_resources/devices.json ) можно получить актуальный список устройств с их свойствами в JSON формате.