niimbot-proto: черновик
All checks were successful
Build and deploy mmote.ru / Build-And-Deploy (push) Successful in 34s

This commit is contained in:
MultiMote 2024-07-25 21:06:25 +03:00
parent e004a8fbcc
commit 14cf65bbf0
5 changed files with 480 additions and 0 deletions

View File

@ -0,0 +1,4 @@
---
title: "Реверс-инжиниринг"
description: "Изобретаем заново"
---

View File

@ -0,0 +1,40 @@
---
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-25T21:04:37+03:00
telegram_entry_id: ""
type: default
---
После того, как поигрались с [niimprint](/niimbot-d110-pc), захотелось чего-то большего. У меня появилась идея написать полноценный веб-интерфейс для печати, в котором можно будет и рисовать этикетки, и печатать. Для этого я решил изучить протокол принтеров.
<!--more-->
Вооружившись Wireshark и android телефоном, снял дампы обмена данными с принтером по bluetooth. Для этого нужно было включить опцию "Bluetooth HCI Snoop Log" в настройках разработчика, а потом на компьютере после печати запустить `adb bugreport <filename>`.
Изучив пакеты и сверившись с другими открытыми данными, получилась такая структура пакета:
![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`.

BIN
content/posts/niimbot-proto/miniature.jpg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -0,0 +1,433 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="279mm"
height="122mm"
viewBox="0 0 279 122"
version="1.1"
id="svg1"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="packet.svg"
inkscape:export-filename="packet.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="0.96297715"
inkscape:cx="469.3777"
inkscape:cy="120.45976"
inkscape:window-width="1920"
inkscape:window-height="1027"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="0.26458334"
spacingy="0.26458333"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
<sodipodi:guide
position="103.18749,71.729165"
orientation="0,-1"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="71.437524,74.375002"
orientation="0,-1"
id="guide4"
inkscape:locked="false" />
</sodipodi:namedview>
<defs
id="defs1">
<rect
x="150"
y="79.999999"
width="130"
height="39.999999"
id="rect457" />
</defs>
<g
inkscape:label="Слой 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#d7f4e3;stroke:#000000;stroke-width:0.765;stroke-dasharray:1.53, 0.765;stroke-dashoffset:0;stroke-opacity:1"
id="rect2-21-5-8-6-4"
width="22.489586"
height="22.489584"
x="148.16666"
y="55.5625" />
<rect
style="fill:#ffaaaa;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-0-2"
width="22.489586"
height="22.489584"
x="238.125"
y="55.5625" />
<rect
style="fill:#ffaaaa;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-9"
width="22.489586"
height="22.489584"
x="215.63541"
y="55.5625" />
<rect
style="fill:#ffaaaa;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-0"
width="22.489586"
height="22.489584"
x="58.208347"
y="55.5625" />
<rect
style="fill:none;stroke:#cccccc;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5-8-6-9-9"
width="22.489586"
height="22.489584"
x="13.229167"
y="55.5625" />
<rect
style="fill:#ffaaaa;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21"
width="22.489586"
height="22.489584"
x="35.718746"
y="55.5625" />
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="46.915318"
y="71.775108"
id="text2-2"><tspan
sodipodi:role="line"
id="tspan2-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="46.915318"
y="71.775108">55</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="24.446386"
y="71.854347"
id="text2-2-1"><tspan
sodipodi:role="line"
id="tspan2-6-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.465"
x="24.446386"
y="71.854347">03</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="69.404907"
y="71.775108"
id="text2-2-4"><tspan
sodipodi:role="line"
id="tspan2-6-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="69.404907"
y="71.775108">55</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="226.88708"
y="71.864677"
id="text2-2-49"><tspan
sodipodi:role="line"
id="tspan2-6-9"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="226.88708"
y="71.864677">AA</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="249.37668"
y="71.864677"
id="text2-2-4-2"><tspan
sodipodi:role="line"
id="tspan2-6-2-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="249.37668"
y="71.864677">AA</tspan></text>
<rect
style="fill:#e5ff80;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5"
width="22.489586"
height="22.489584"
x="80.697922"
y="55.5625" />
<rect
style="fill:#87deaa;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5-8"
width="22.489586"
height="22.489584"
x="103.18751"
y="55.5625" />
<rect
style="fill:#d7f4e3;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5-8-6"
width="22.489586"
height="22.489584"
x="125.67709"
y="55.5625" />
<rect
style="fill:#d7f4e3;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5-8-6-9"
width="22.489586"
height="22.489584"
x="170.65625"
y="55.5625" />
<rect
style="fill:#eeaaff;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
id="rect2-21-5-8-6-9-1"
width="22.489586"
height="22.489584"
x="193.14581"
y="55.5625" />
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="114.58388"
y="71.854347"
id="text2-2-4-22"><tspan
sodipodi:role="line"
id="tspan2-6-2-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="114.58388"
y="71.854347">02</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="137.07344"
y="71.854347"
id="text2-2-4-22-6"><tspan
sodipodi:role="line"
id="tspan2-6-2-3-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="137.07344"
y="71.854347">01</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="159.41145"
y="67.513535"
id="text2-2-4-22-6-8"><tspan
sodipodi:role="line"
id="tspan2-6-2-3-4-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="159.41145"
y="67.513535">..</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="182.0526"
y="71.854347"
id="text2-2-4-22-6-9"><tspan
sodipodi:role="line"
id="tspan2-6-2-3-4-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="182.0526"
y="71.854347">10</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="204.07709"
y="71.916359"
id="text2-2-4-22-6-9-7"><tspan
sodipodi:role="line"
id="tspan2-6-2-3-4-0-8"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="204.07709"
y="71.916359">12</tspan></text>
<text
xml:space="preserve"
style="font-size:14.1111px;font-family:Calibri;-inkscape-font-specification:Calibri;text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.465;stroke-dasharray:none;stroke-opacity:1"
x="91.994385"
y="71.854347"
id="text2-2-4-22-6-9-0"><tspan
sodipodi:role="line"
id="tspan2-6-2-3-4-0-9"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.465"
x="91.994385"
y="71.854347">01</tspan></text>
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="58.20834"
y="44.979168"
id="text2"><tspan
sodipodi:role="line"
id="tspan2"
style="font-size:11.2889px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765"
x="58.20834"
y="44.979168">Head</tspan></text>
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:none;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="118.98223"
y="22.205931"
id="text2-8"><tspan
sodipodi:role="line"
id="tspan2-1"
style="font-size:11.2889px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765"
x="118.98223"
y="22.205931">Command</tspan></text>
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="237.42249"
y="44.81382"
id="text3"><tspan
sodipodi:role="line"
id="tspan3"
style="font-size:11.2889px;stroke-width:0.765"
x="237.42249"
y="44.81382">Tail</tspan></text>
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="159.3205"
y="44.868919"
id="text3-1"><tspan
sodipodi:role="line"
id="tspan3-8"
style="font-size:11.2889px;stroke-width:0.765"
x="159.3205"
y="44.868919">Data</tspan></text>
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="147.07803"
y="105.67899"
id="text3-1-5"><tspan
sodipodi:role="line"
id="tspan3-8-2"
style="font-size:11.2889px;stroke-width:0.765"
x="147.07803"
y="105.67899">Data length</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
d="m 35.71875,52.916664 v -5.291667 h 44.979166 v 5.291667"
id="path3" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
d="m 215.63541,52.916676 v -5.291667 h 44.97917 v 5.291667"
id="path3-9" />
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="105.83333"
y="29.104166"
id="text4"><tspan
sodipodi:role="line"
id="tspan4"
style="stroke-width:0.765"
x="105.83333"
y="29.104166" /></text>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
d="m 91.942715,52.916664 v -27.95329 l 53.578125,0.172043"
id="path4"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 125.67709,52.916664 v -5.291667 h 67.46875 v 5.291667"
id="path5" />
<g
id="g1"
transform="translate(56.664966,58.472918)">
<text
xml:space="preserve"
style="font-size:11.2889px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="177.84134"
y="47.514751"
id="text3-1-6"><tspan
sodipodi:role="line"
id="tspan3-8-0"
style="font-size:11.2889px;stroke-width:0.765"
x="177.84134"
y="47.514751">Checksum</tspan></text>
</g>
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 204.39062,81.510086 v 28.291994 h 57.28229"
id="path6"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.765;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 113.52562,81.510086 V 109.5375 h 64.80355"
id="path7"
sodipodi:nodetypes="ccc" />
<text
xml:space="preserve"
style="font-size:11.2889px;line-height:0.7;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';text-align:center;word-spacing:0px;text-anchor:middle;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.765;stroke-dasharray:none;stroke-opacity:1"
x="58.20834"
y="97.895821"
id="text3-1-5-1"><tspan
sodipodi:role="line"
id="tspan3-8-2-1"
style="font-size:11.2889px;line-height:0.7;word-spacing:0px;fill:#cccccc;fill-opacity:1;stroke-width:0.765"
x="58.20834"
y="97.895821"
rotate="0 0 0 0 0 0 0">Prefix</tspan><tspan
sodipodi:role="line"
style="font-size:6.35px;line-height:0.7;word-spacing:0px;fill:#cccccc;fill-opacity:1;stroke-width:0.765"
x="58.20834"
y="105.79805"
id="tspan7"
rotate="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0.7;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';fill:#cccccc;fill-opacity:1"
id="tspan8">Connect</tspan> packet only</tspan></text>
<path
style="fill:none;fill-opacity:1;stroke:#cccccc;stroke-width:0.765;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 23.8125,80.697915 V 108.72534 H 92.604165"
id="path7-3"
sodipodi:nodetypes="ccc" />
<text
xml:space="preserve"
transform="scale(0.26458333)"
id="text455"
style="fill:#cccccc;-inkscape-font-specification:'Calibri Bold';font-family:Calibri;font-weight:bold;font-size:37.33333333px;stroke-width:0.37795276;stroke-linecap:round;stroke:#cccccc;white-space:pre;shape-inside:url(#rect457)" />
</g>
<metadata
id="metadata9">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:creator>
<cc:Agent>
<dc:title>MultiMote</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB