Cat-Printer


"[Cat-Printer](https://github.com/NaitLee/Cat-Printer)" は AliExpress などで販売している安価な Bluetooth 接続サーマルプリンタを各種プラットフォームから Web UI で操作するプロジェクトです。

ここで記載するのは、この Cat-Printer について Linux ホストでカメラ撮影して印刷する機能を追加する quick hack です。UVC カメラで uvccapture を使って撮影し一旦ファイルに落としたものを取り込みます。撮影プログラムが標準出力に出したものを使えばファイル保存を回避できるでしょう。Raspberry pi では uvccapture ではなく raspistill を使うよう変更することも簡単です。

ついでに自分の購入したプリンタのモデル名 "MX09" を追加しています。これがなくても "Test Unknown Device" で選択可能です。

~~~diff_python
diff --git a/printer_lib/models.py b/printer_lib/models.py
index 989ad62..077eb14 100644
--- a/printer_lib/models.py
+++ b/printer_lib/models.py
@@ -19,7 +19,7 @@ class Model():
 Models = {}
 
 # all known supported models
-for name in '_ZZ00 GB01 GB02 GB03 GT01 MX05 MX06 MX08 YT01'.split(' '):
+for name in '_ZZ00 GB01 GB02 GB03 GT01 MX05 MX06 MX08 MX09 YT01'.split(' '):
     Models[name] = Model()
 
 # that can receive compressed data
@@ -27,5 +27,5 @@ for name in 'GB03'.split(' '):
     Models[name].is_new_kind = True
 
 # that have problem giving feed command
-for name in 'MX05 MX06 MX08'.split(' '):
+for name in 'MX05 MX06 MX08 MX09'.split(' '):
     Models[name].problem_feeding = True
diff --git a/server.py b/server.py
index fa2d06c..3cd2e88 100644
--- a/server.py
+++ b/server.py
@@ -14,6 +14,7 @@ import sys
 import json
 import warnings
 import webbrowser
+import subprocess
 
 # For now we can't use `ThreadingHTTPServer`
 from http.server import HTTPServer, BaseHTTPRequestHandler
@@ -49,6 +50,7 @@ mime_type = {
     'txt': 'text/plain;charset=utf-8',
     'json': 'application/json;charset=utf-8',
     'png': 'image/png',
+    'jpg': 'image/jpg',
     'svg': 'image/svg+xml;charset=utf-8',
     'wasm': 'application/wasm',
     'octet-stream': 'application/octet-stream'
@@ -247,6 +249,11 @@ class PrinterServerHandler(BaseHTTPRequestHandler):
             name, address = data['device'].split(',')
             self.printer.connect(name, address)
             self.api_success()
+        if api == 'take':
+            # cmd = "/usr/bin/raspistill -n -o www/hoge.jpg" # raspberry pi
+            cmd = "/usr/bin/uvccapture -q100 -owww/hoge.jpg" # linux UVC camera
+            subprocess.call(cmd, shell=True)
+            self.api_success()
         if api == 'exit':
             self.api_success()
             self.exit()
diff --git a/www/index.html b/www/index.html
index 86cfcf5..3287103 100644
--- a/www/index.html
+++ b/www/index.html
@@ -129,6 +129,7 @@
             <div id="control-overlay">
                 <div>
                     <button id="insert-picture" data-i18n="insert-picture" data-key="Enter">Insert Picture</button>
+                    <button id="take-picture" data-i18n="take-picture" data-key="P">Take Picture</button>
                     <button id="insert-text" data-i18n="insert-text" data-key="\">Insert Text</button>
                     <p data-i18n="or-drag-file-to-below" class="hide-on-android">Or drag file to below</p>
                 </div>
diff --git a/www/main.js b/www/main.js
index 372e971..e474eaa 100644
--- a/www/main.js
+++ b/www/main.js
@@ -380,6 +380,7 @@ class CanvasController {
 
         Ev.put('[name="algo"]'  , 'change', (event) => this.useAlgorithm(event.currentTarget.value), this);
         Ev.put('#insert-picture', 'click' , () => this.useFiles(), this);
+        Ev.put('#take-picture', 'click' , () => this.takePicture(), this);
         Ev.put('#insert-text'   , 'click' , () => Dialog.alert("#text-input", () => this.insertText(this.textArea.value)));
         Ev.put('#text-size'     , 'change', () => this.textArea.style["font-size"] = this.textSize.value + "px"); 
         Ev.put('#text-font'     , 'change', () => this.textArea.style["font-family"] = this.textFont.value); 
@@ -504,6 +505,13 @@ class CanvasController {
             hint('#button-print');
         }, { once: true });
     }
+    async takePicture() {
+        await callApi('/take');
+        let url = 'hoge.jpg?' + new Date().toLocaleTimeString(); // avoid cache
+        this.imageUrl = url;
+        this.putImage(url);
+        this.controls.classList.add('hidden');
+    }
     useFiles(files) {
         const use_files = (files) => {
             let file = files[0];
~~~

新たに追加した "Take Picture" のボタンを押すと撮影がおこなわれてプレビューキャンバスに表示され、印刷できます。
![Screenshot from 2023-06-20 20-36-42.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45347/31cd8b32-93a8-48cf-0549-cebc349ff483.jpeg)
通称 Cat Printer はこんな Bluetooth 接続のサーマルプリンタで同等のものが各種あります。AliExpress 等で 2000 円程度で購入できます。スマートフォン用の専用アプリケーションが用意されています。
![catprinter.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45347/f0e25c39-6385-f77a-0fa0-37aeaac9235c.jpeg)

この記事は「[この記事誰得？ 私しか得しないニッチな技術で記事投稿！](https://qiita.com/official-events/5d4f04cf2ba0cdbc8821)」にエントリしているのですが、ニッチ過ぎて誰の目にも止まらないかもしれません。