今回実装したもの
以下のリンクより遊べます。エプソン製の TM シリーズのプリンタであれば大体動くとは思いますが、動作確認は TM-T90 でしかとれていません。横幅がプリンタの印字可能ドット数を超える画像を印刷しようとすると、数メートルのゴミを印刷してしまうので注意してください。 USB ケーブルかプリンタの電源ケーブルを抜くことで中断できます。また、事前に udev ルールの適切な設定が必要です。
プリンタ購入〜印字テスト
ふとレシートを作りたくなったので、ヤフオクで TM-T90 というのを買ってみました。
USB 接続モデルだったので USB 2.0 Type-B (要は、現代的なプリンタ用の USB ケーブル)でサクっと PC に繋ぎます。そのままでは権限の問題で一般ユーザはアクセスできないので、 udev ルールを書いて対応します(Windows や macOS でのやりかたについてはよくわかりませんでした)。
/etc/udev/rules.d/99-escpos.rules
に以下の設定を書きました。
SUBSYSTEM=="usb",
ATTRS{idVendor}=="04b8",
ATTRS{idProduct}=="0202",
MODE="0664",
GROUP="wheel"
ざっくり説明すると、この VID/PID を持つデバイスには wheel
グループに属するユーザがアクセスできるよ、という設定です。
このようなプリンタは ESC/POS というコマンドを用いて制御するのが一般的なようです。PyPI にパッケージ があったので、まずはこれでテストしてみます。
from escpos.printer import Usb
p = Usb(idVendor=0x04b8, idProduct=0x0202)
text = "Lorem ipsum dolor (中略) id est laborum."
p.text(text)
p.cut()
めちゃくちゃ簡単に印刷できました…が、このライブラリは日本語が出せなかったり、紙幅の設定がいじれなかったりと、できないことがわりと多いです。結局、プリンタに実装されている機能をフルに使うには、本家の EPSON が公開している ESC/POS リファレンスを読みながら生の制御コマンドを読み書きしていくことになります。これがなかなか曲者で、たとえば、日本語を出力するには
FS C
コマンドで文字コードを Shift-JIS に設定ESC R
コマンドで国際文字セットを日本語に設定FS &
コマンドで漢字モードを有効化ESC M
コマンドで内蔵フォントを選択- テキストを Shift-JIS でエンコードして送信
という難解なステップを経る必要があります(そして、これらの制御は機種・ベンダー依存です)。この辺を踏まえつつ、JavaScript & WebUSB に乗せていきます。
WebUSB で実装
WebUSB において、USB デバイスとの通信は以下のような手順で行われます。
navigator.usb.requestDevice()
で USB デバイスのリストを取得- ペアリング済のデバイスが存在する場合は
navigator.usb.gerDevice()
でも可
- ペアリング済のデバイスが存在する場合は
open()
でデバイスを開くselectConfiguration()
で構成を選択claimInterface()
で排他ロックを要求selectAlternateInterface()
でインターフェースを選択transferOut()
およびtransferIn()
でデバイスと通信releaseInterface()
で排他ロックを解除close()
でデバイスを閉じる
また、claimInterface()
で DOMException: Unable to claim interface
というエラーが生じることがあります。これは、Linux カーネルが usblp
や usbfs
などのドライバを勝手にロードし、デバイスがビジーになってしまうことが原因です。libusb には libusb_detach_kernel_driver
という便利なメソッドがあり、これでカーネルが自動でロードしたドライバを unbind
することができるのですが、Linux でしか使えないので WebUSB には実装されていないようです。参考:Chromium の Issue
残念ながら JavaScript からはどうにもならないようなので、それらのドライバを自動で unbind
するスクリプトを udev ルールに追記します。
RUN+="/bin/sh -c 'echo -n $id:1.0 > /sys/bus/usb/drivers/usblp/unbind && echo -n $id:1.0 > /sys/bus/usb/drivers/usbfs/unbind'"
今回作ったデモページ WebUSB ESC/POS demo では、(日本語を含む)テキストの印刷に加え、画像の印刷と紙幅の変更を行えるようにしています。
できていないことメモ
- プリンタからの情報取得
- 紙幅やフォントなど、内蔵メモリの情報を USB で送ってくれるはずなんだけど受信できない
- 印刷濃度の設定