From 75f8d9801237b885e7f8e83a802cda5cc92d3a3d Mon Sep 17 00:00:00 2001 From: "Andriy Kushnir (Orhideous)" Date: Tue, 30 Jan 2024 21:39:42 +0200 Subject: [PATCH 1/5] Add scaffold for docs --- .github/workflows/docs.yml | 45 ++++++++++++++ docs/faq.md | 5 ++ docs/hardware.md | 5 ++ docs/index.md | 8 +++ docs/requirements.txt | 5 ++ mkdocs.yml | 119 +++++++++++++++++++++++++++++++++++++ 6 files changed, 187 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/faq.md create mode 100644 docs/hardware.md create mode 100644 docs/index.md create mode 100644 docs/requirements.txt create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..1d19ed3 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,45 @@ +name: Build documentation +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install python + uses: actions/setup-python@v4 + with: + python-version: 3.x + - name: Setup cache + uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - name: Setup dependencies + run: pip install -r docs/requirements.txt + - name: Build docs + run: mkdocs build --strict + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: ./site + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..85e85ea --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,5 @@ +--- +hide: + - navigation +--- + diff --git a/docs/hardware.md b/docs/hardware.md new file mode 100644 index 0000000..85e85ea --- /dev/null +++ b/docs/hardware.md @@ -0,0 +1,5 @@ +--- +hide: + - navigation +--- + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..93a5cbf --- /dev/null +++ b/docs/index.md @@ -0,0 +1,8 @@ +--- +hide: + - navigation + - toc +--- + +# niimprint + diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..32bd08c --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +cairosvg~=2.7.1 +mkdocs-material~=9.4.14 +mkdocs-minify-plugin~=0.7.1 +pillow~=10.1.0 + diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..8d43cd0 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,119 @@ +site_name: niimprint +site_url: https://orhideous.github.io/niimprint +edit_uri: blob/main/docs/ # Change the master branch to main as we are using main as a main branch +site_author: niimprint authors +site_description: >- + Python app for Niimbot label printers. + +# Repository +repo_name: orhideous/niimprint +repo_url: https://github.com/orhideous/niimprint + +# Copyright +copyright: Copyright © 2024 niimprint authors + +# Configuration +theme: + name: material + features: + - announce.dismiss + - content.action.edit + - content.action.view + - content.code.annotate + - content.code.copy + # - content.tabs.link + - content.tooltips + # - header.autohide + # - navigation.expand + - navigation.footer + - navigation.indexes + # - navigation.instant + # - navigation.prune + - navigation.sections + - navigation.tabs + # - navigation.tabs.sticky + - navigation.top + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + # - toc.integrate + palette: + - scheme: default + primary: white + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: Roboto + code: Roboto Mono + favicon: assets/favicon.png + +# Plugins +plugins: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + - minify: + minify_html: true + - social: {} + +# Customization +extra: + annotate: + json: [.s2] + social: + - icon: fontawesome/brands/github + link: https://github.com/orhideous/niimprint + +# Extensions +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:material.extensions.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: squidfunk + repo: mkdocs-material + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +# Page tree +nav: + - Home: index.md + - FAQ: faq.md + - Printers & Tapes: hardware.md From 17167fbcb2ece5f4090ad57912a7627f6c6cbf63 Mon Sep 17 00:00:00 2001 From: "Andriy Kushnir (Orhideous)" Date: Tue, 30 Jan 2024 22:29:01 +0200 Subject: [PATCH 2/5] Moved documentation to separate directory --- README.md | 20 +++++ docs/faq.md | 17 ++++ docs/hardware.md | 9 +++ {examples => docs/img}/B21_30x15_result.png | Bin {examples => docs/img}/B21_80x50_result.png | Bin {examples => docs/img}/image_orientation.png | Bin docs/index.md | 50 +++++++++++- readme.md | 78 ------------------- 8 files changed, 95 insertions(+), 79 deletions(-) create mode 100644 README.md rename {examples => docs/img}/B21_30x15_result.png (100%) rename {examples => docs/img}/B21_80x50_result.png (100%) rename {examples => docs/img}/image_orientation.png (100%) delete mode 100644 readme.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2062db2 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# niimprint +An open-source library and client for Niimbot printers + +### Fork changelog & differences from original version + +- Tested on Niimbot B1, B18, B21, D11, D110 and Python 3.11 +- Added transport abstraction: switch between bluetooth and USB (serial) +- Disabled checksum calculation for image encoding (works fine without it so far) +- Switched to [click](https://click.palletsprojects.com/) CLI library instead of argparse +- Integrated [pyproject.toml](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) and [poetry](https://python-poetry.org) +- Integrated [pre-commit](https://pre-commit.com/) and [ruff](https://docs.astral.sh/ruff/), re-formatted all files +- Miscellaneous refactoring / file renaming / etc. + +## Installation and usage + +Check out [**documentation**](https://andbondstyle.github.io/niimprint/). + +## Licence + +[MIT](https://choosealicense.com/licenses/mit/). Originally developed by [kjy00302](https://github.com/kjy00302), forked & enhanced by [AndBondStyle](https://github.com/AndBondStyle) diff --git a/docs/faq.md b/docs/faq.md index 85e85ea..28c5b78 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -3,3 +3,20 @@ hide: - navigation --- +## Image orientation + +Generally, the image comes out of the printer with the same orientation you see it on your screen. +You can have your input image rotated as you like, but adjust its orientation by passing `-r <...>` flag. + +See the image below for clarification. + +[![](./img/image_orientation.png)]() + + + +## Image resolution + +As far as we've tested, Niimbot printers have **8 pixels per mm** (~203 dpi) resolution. The CLI prints the image you provided as-is, without any checks of the actual label size, so be careful. However the script will check if the image width is too big for selected printer. The maximum width in pixels is usually slightly less than specified maximum width in mm: + +- **B21, B1, B18**: max 384 pixels (almost equal to 50 mm * 8 px/mm = 400) +- **D11**: max 96 pixels (almost equal to 15 mm * 8 px/mm = 120) diff --git a/docs/hardware.md b/docs/hardware.md index 85e85ea..6734f1a 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -3,3 +3,12 @@ hide: - navigation --- +# Supported printers + +- D11 +- D110 +- B21 +- B1 +- B18 + +# Tapes diff --git a/examples/B21_30x15_result.png b/docs/img/B21_30x15_result.png similarity index 100% rename from examples/B21_30x15_result.png rename to docs/img/B21_30x15_result.png diff --git a/examples/B21_80x50_result.png b/docs/img/B21_80x50_result.png similarity index 100% rename from examples/B21_80x50_result.png rename to docs/img/B21_80x50_result.png diff --git a/examples/image_orientation.png b/docs/img/image_orientation.png similarity index 100% rename from examples/image_orientation.png rename to docs/img/image_orientation.png diff --git a/docs/index.md b/docs/index.md index 93a5cbf..d7fb391 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,5 +4,53 @@ hide: - toc --- -# niimprint +# Niimbot Printer Client +## Installation + +Recommended method is to use [poetry](https://python-poetry.org) and install with `poetry install`. +However `requirements.txt` is also provided for convenience. + +Project is tested on Python 3.11, but should work on other versions. + +## Usage + +Just run `niimprint --help` + +## Examples + +### B21, USB connection, 30x15 mm (240x120 px) label + +``` +python niimprint -c usb -a /dev/ttyACM0 -r 90 -i examples/B21_30x15mm_240x120px.png +``` + +![](./img/B21_30x15_result.png) + +### B21, Bluetooth connection, 80x50 mm (640x384 px) label + +``` +python niimprint -c bluetooth -a "E2:E1:08:03:09:87" -r 90 -i examples/B21_80x50mm_640x384px.png +``` + +![](./img/B21_80x50_result.png) + + +## USB connection + +For USB connection, you can omit the `--addr` argument and let the script auto-detect the serial port. +However, it will fail if there're multiple available ports. + +On linux, serial ports can be found at `/dev/ttyUSB*`, `/dev/ttyACM*` or `/dev/serial/*`. +On windows, they will be named like `COM1`, `COM2` etc. Check the device manager to choose the correct one. + +## Bluetooth connection + +It seems like B21 (and maybe other models?) has two bluetooth addresses. +For me, they start with `C2:E1` and `E2:E1` respectively. + +!!! warning "Double-check bluetooth address" + + Connection works only if you disconnect from `C2:E1` and connect to `E2:E1`. + +Also after connecting to `E2:E1` via bluetoothctl I always get `org.bluez.Error.NotAvailable br-connection-profile-unavailable` error, but printing works fine regardless. diff --git a/readme.md b/readme.md deleted file mode 100644 index a6b2c34..0000000 --- a/readme.md +++ /dev/null @@ -1,78 +0,0 @@ -# `niimprint` — Niimbot Printer Client - -**Fork changelog & differences from original version:** - -- Tested on Niimbot B1, B18, B21, D11, D110 and Python 3.11 -- Added transport abstraction: switch between bluetooth and USB (serial) -- Disabled checksum calculation for image encoding (works fine without it so far) -- Switched to [click](https://click.palletsprojects.com/) CLI library instead of argparse -- Integrated [pyproject.toml](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) and [poetry](https://python-poetry.org) -- Integrated [pre-commit](https://pre-commit.com/) and [ruff](https://docs.astral.sh/ruff/), re-formatted all files -- Miscellaneous refactoring / file renaming / etc. - -## Installation - -Recommended method is to use [poetry](https://python-poetry.org) and install with `poetry install`. However `requirements.txt` is also provided for convenience. Project is tested on Python 3.11, but should work on other versions. - -## Usage - -``` -$ python niimprint --help - -Usage: niimprint [OPTIONS] - -Options: - -m, --model [b1|b18|b21|d11|d110] Niimbot printer model [default: b21] - -c, --conn [usb|bluetooth] Connection type [default: usb] - -a, --addr TEXT Bluetooth MAC address OR serial device path - -d, --density INTEGER RANGE Print density [default: 5; 1<=x<=5] - -r, --rotate [0|90|180|270] Image rotation (clockwise) [default: 0] - -i, --image PATH Image path [required] - -v, --verbose Enable verbose logging - --help Show this message and exit. -``` - -### Image orientation: - -Generally, the image comes out of the printer with the same orientation you see it on your screen. You can have your input image rotated as you like, but adjust its orientation by passing `-r <...>` flag. See the image below for clarification. - -[![](examples/image_orientation.png)]() - - - -### Image resolution: - -As far as we've tested, Niimbot printers have **8 pixels per mm** (~203 dpi) resolution. The CLI prints the image you provided as-is, without any checks of the actual label size, so be careful. However the script will check if the image width is too big for selected printer. The maximum width in pixels is usually slightly less than specified maximum width in mm: - -- **B21, B1, B18**: max 384 pixels (almost equal to 50 mm * 8 px/mm = 400) -- **D11**: max 96 pixels (almost equal to 15 mm * 8 px/mm = 120) - -### USB connection: - -For USB connection, you can omit the `--addr` argument and let the script auto-detect the serial port. However, it will fail if there're multiple available ports. On linux, serial ports can be found at `/dev/ttyUSB*`, `/dev/ttyACM*` or `/dev/serial/*`. On windows, they will be named like `COM1`, `COM2` etc. Check the device manager to choose the correct one. - -### Bluetooth connection: - -It seems like B21 (and maybe other models?) has two bluetooth adresses. For me, they start with `C2:E1` and `E2:E1` respectively. Connection works only if you disconnect from `C2:E1` and connect to `E2:E1`. Also after connecting to `E2:E1` via bluetoothctl I always get `org.bluez.Error.NotAvailable br-connection-profile-unavailable` error, but printing works fine regardless. - -## Examples - -**B21, USB connection, 30x15 mm (240x120 px) label** - -``` -python niimprint -c usb -a /dev/ttyACM0 -r 90 -i examples/B21_30x15mm_240x120px.png -``` - -[![](examples/B21_30x15_result.png)]() - -**B21, Bluetooth connection, 80x50 mm (640x384 px) label** - -``` -python niimprint -c bluetooth -a "E2:E1:08:03:09:87" -r 90 -i examples/B21_80x50mm_640x384px.png -``` - -[![](examples/B21_80x50_result.png)]() - -## Licence - -[MIT](https://choosealicense.com/licenses/mit/). Originally developed by [kjy00302](https://github.com/kjy00302), forked & enhanced by [AndBondStyle](https://github.com/AndBondStyle) From 755dc516ffc2aef6ae240e9777252435f4516365 Mon Sep 17 00:00:00 2001 From: "Andriy Kushnir (Orhideous)" Date: Tue, 30 Jan 2024 22:32:27 +0200 Subject: [PATCH 3/5] Add notes about printing --- docs/index.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/index.md b/docs/index.md index d7fb391..e52920f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,6 +17,16 @@ Project is tested on Python 3.11, but should work on other versions. Just run `niimprint --help` +### Print and tape notes + +* `TT14*50` is prone to overfeeding. Roll it for 1.5mm back after each printed label. +* «Bubble B» stickers have variable dimensions and colors (green, blue, teal, pink, yellow — in that order). + Be careful with layouts. +* Almost all stickers have rounded corners, count it when designing layouts. +* Note that different print densities will vary the pixel size, so count at least 2-5 pixels from the edges + to avoid clipping. +* **Always** use good dithering (Floyd-Steinberg, Sierra, Burkes) for grayscale images. + ## Examples ### B21, USB connection, 30x15 mm (240x120 px) label From 7ecbc27c573b109c68c666490714e6d2c96d1f54 Mon Sep 17 00:00:00 2001 From: "Andriy Kushnir (Orhideous)" Date: Wed, 31 Jan 2024 00:32:46 +0200 Subject: [PATCH 4/5] Add notes about tapes for D11/D110 --- docs/hardware.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/hardware.md b/docs/hardware.md index 6734f1a..7fb83f6 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -12,3 +12,13 @@ hide: - B18 # Tapes + +=== "D11 / D110" + + | Tape type | Printable dimensions (mm) | Pixels | Notes | + |-----------|---------------------------|--------|-------------| + | T14*22 | 13.5×21.5 | 96*172 | | + | T12*22 | 11.5×21.5 | 94*168 | | + | T12*30 | 11.7×29.8 | 94*228 | | + | TT14*50 | 13.9×49.9 | 96*380 | Transparent | + | T14*40 | 12x32 | 96*228 | «Bubble B» | From 9132b01361fdc929cf6df421b741b8051e830deb Mon Sep 17 00:00:00 2001 From: "Andriy Kushnir (Orhideous)" Date: Tue, 13 Feb 2024 15:03:20 +0200 Subject: [PATCH 5/5] Imported wiki with information about protocol Credits: kjy00302 --- docs/protocol.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 69 insertions(+) create mode 100644 docs/protocol.md diff --git a/docs/protocol.md b/docs/protocol.md new file mode 100644 index 0000000..bf37436 --- /dev/null +++ b/docs/protocol.md @@ -0,0 +1,68 @@ +--- +hide: + - navigation +--- + +## Packet structure + +Niimbot uses simple TLV packet with preamble, postamble and checksum. + +Every field of packet uses big endian. + + |5555|c2|01|02|c1|aaaa| + │ │ │ │ │ └ Postamble (constant) + │ │ │ │ └─── Checksum (xor sum of type, length and value) + │ │ │ └────── Value + │ │ └───────── Length (of data) + │ └──────────── Type + └───────────────── Preamble (constant) + +## Line encoding packets +Niimbot uses three types of packet to print image. + +All three packet type has repeat count. So, repetition of same data can be reduced to one packet. + +Currently niimprint's encoder only uses line packet without optimization. + +_(Common packet fields are omitted for clarity.)_ + +### Blank packet +Blank packet (packet type 0x84) encodes blank line. + + |5555|84|03|0000|0a|8d|aaaa| + │ └ Repeat count + └───── Line count + +This packet will print 10 blank lines. + +### Line packet +line packet (packet type 0x85) encodes line as bitmap. + + |5555|85|12|000a|00|01|0f|02|0000000000000001fffe0000|91|aaaa + │ │ │ │ │ └ Bitmap data + │ │ │ │ └─── Repeat count + │ │ │ └────── Pixel count (right) (bit count of 0x00000000) + │ │ └───────── Pixel count (middle) (bit count of 0x00000001) + │ └──────────── Pixel count (left) (bit count of 0xfffe0000) + └───────────────── Line count + +This packet will print 2 bitmap lines. + +### Points packet +points packet (packet type 0x83) encodes line as array of 1D points. + + 5555|83|0e|000c|00|01|03|07|003f0040004d004e|f8|aaaa| + │ │ │ │ │ └ Point data (0x003f, 0x0040, 0x004d, 0x004e) + │ │ │ │ └─── Repeat count + │ │ │ └────── Pixel count (right) + │ │ └───────── Pixel count (middle) + │ └──────────── Pixel count (left) + └───────────────── Line count + +This packet will print 7 lines with points. + +### Todo + +!!! question + + How does B21 encodes more then 96 pixels? diff --git a/mkdocs.yml b/mkdocs.yml index 8d43cd0..c57f712 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -117,3 +117,4 @@ nav: - Home: index.md - FAQ: faq.md - Printers & Tapes: hardware.md + - Protocol: protocol.md