2020年12月28日月曜日

ReactNativeのAndroidアプリでESCPOS(レシートプリンタ)をUSB接続で使う


背景

ESCPOS規格のレシートプリンタ(USB接続)をReactNativeで動かそうとしたら対応するライブラリが見当たらなかったので、Android向けのESCPOSライブラリを利用してReactNativeのライブラリを作ってみました。
OTGケーブルが必要だったり、無線デバッグが必要だったり、詰まりどころがあるので、使い方を共有します。

使ったもの

  • OTGケーブル
    とても重要な装置です。
    Android端末を親機(ホストデバイス)として動かすために必要です。
    Raspberry Pi Zeroに同梱されているAメス<->micorBケーブルは、OTGケーブルではないので、形状は一緒なのですが動きません。
    OTGケーブルを準備してください。
    amazon: USB TypeC OTGケーブル
    amazon: USB microB OTGケーブル
  • ReactNativeのAndroidを開発出来るPC
    環境構築手順に従い、React Native CLIを利用するためnpmとAndroid Studioをインストールし、Android Studioで必要なSDKをインストールし、ANDROID_HOMEという環境変数を設定したPCです。
  • Android端末
    下記の2種類の端末で動くことを確認しました。
    Android6.1が入ったNexus5
    Android8.0が入ったHuawei P20 Lite
    Android11が入ったHuaweiのタブレット
  • ESCPOS対応のプリンタ
    EPSONのTM-T88IVというレシートプリンタを利用しました。

USBポートをプリンタに繋ぐので、ReactNativeの無線デバッグモードを利用

Android端末にプリンタをUSB接続しても開発を進められるように、ReactNativeの無線デバッグモードを利用します。
使い方を説明します。

Android端末とPCを同じWiFiに接続します。


ReactNativeのログ出力用ターミナルで「d」を入力するか、ReactNativeのアプリを動かしている端末を振って開発メニューを表示し、Settingsを選びます。


Debug server host & port for deviceを選びます。


PCのIPアドレス + 8081ポート を入力します。
例: 192.168.0.16:8081


これで、無線でデバッグできるようなります。

作ったライブラリ: react-native-escpos-android

Android向けに作られているESCPOS-ThermalPrinter-Androidというライブラリをreact-nativeのbridgeを利用してreact-nativeから機能の一部を呼べるようにしたライブラリです。

https://github.com/asukiaaa/react-native-escpos-android

ライブラリの作成にはreact-native-builder-bobを利用しました。
npmやyarnでインストールできます。
npm install react-native-escpos-android --save

Androidのintentによる起動を設定しておくと、USBでプリンターが接続された際にアプリに対してUSBデバイスの利用を許可できるので、場合によっては便利です。
必須でないのでここでは説明しませんが、気になる方はライブラリのREADMEをご覧ください。

OTGケーブルを通してレシートプリンタとAndroid端末を接続した状態で下記の処理を実行すると、プリンタに情報を書き込めます。
import EscposAndroid from 'react-native-escpos-android';

let raw = [
0x1b, 0x40, // initialize
0x1c, 0x43, 0x01, // set shift-jis
0x82, 0xA0, 0x82, 0xA2, 0x82, 0xA4, // あいう in shift-jis
0x0a, // change line
0x8a, 0xbf, 0x8e, 0x9a, // 漢字 in shift-jis
0x1b, 0x64, 0x05, // feed
0x1D, 0x56, 0x01, // cut
]
await EscposAndroid.write({ raw })

escposのparserライブラリや文字コード変換ライブラリと合わせると便利

escposのコマンドを生成するライブラリや、shift-jisにエンコードするライブラリを併用すると、日本語のレシートを生成しやすくなって便利です。

コマンド生成: EscPosEncoder
文字コード変換: iconv-lite

それらを利用すると、このように記述できます。
import EscposAndroid from 'react-native-escpos-android'
import EscPosEncoder from 'esc-pos-encoder'
import iconv from 'iconv-lite'

const commandBytes = encoder
.initialize()
.raw([
0x1c, 0x43, 0x01, // set shift-jis
0x82, 0xA0, 0x82, 0xA2, 0x82, 0xA4, // あいう in shift-jis
0x0a, // change line
0x8a, 0xbf, 0x8e, 0x9a, // 漢字 in shift-jis
0x0a, // change line
])
.raw(iconv.encode('試行錯誤な日々', 'SHIFT_JIS'))
.raw([
0x1b, 0x64, 0x05, // feed
0x1D, 0x56, 0x01, // cut
])
.encode()
const raw = [...commandBytes] // convert ArrayBuffer to array of number
await EscposAndroid.write({ raw })

出力したレシートがこちらです。


余談: シリアル通信ライブラリは、ポートを開く時の処理が異なるのか、escposは開けなかった

escpos用のライブラリを利用する前はreact-native-usbserial-cvkなどを利用して書き込みを試みていました。
このライブラリを利用するとM5Stackに対しては書き込めたのですが、レシートプリンタに向けて書き込みを試みると下記のエラーが発生して使えませんでした。
[Fri Dec 25 2020 12:00:20.868]  LOG      {"devices": [{"deviceId": 1009, "deviceName": "/dev/bus/usb/001/009", "productId": 514, "productName": "EPSON UB-U02III", "serialNumber": "20101029084402093M03C", "vendorId": 1208}]}
[Fri Dec 25 2020 12:00:22.941] WARN Possible Unhandled Promise Rejection (id: 0):
Error: No control endpoint
promiseMethodWrapper@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:2242:45
openDeviceAsync@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:135886:47
writeSerial$@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:97338:68
tryCatch@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:24938:23
invoke@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:25111:32
tryCatch@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:24938:23
invoke@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:25011:30
http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:25021:21
tryCallOne@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:27020:16
http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:27121:27
_callTimer@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:30560:17
_callImmediatesPass@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:30599:17
callImmediates@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:30816:33
__callImmediates@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:2736:35
http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:2522:34
__guard@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:2719:15
flushedQueue@http://192.168.3.159:8081/index.bundle?platform=android&dev=true&minify=false:2521:21
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]

レシートプリンタも扱えるシリアル通信ライブラリをご存知でしたら、コメントなどで共有していただけると嬉しいです。

まとめ

Android向けに作られているescposのライブラリを利用してReactNative用のライブラリを作り、escposのコマンド生成ライブラリや文字コード変換ライブラリと組み合わせてレシートに日本語を出力できました。

参考

nyampass/halake-raspi/src/receipt-printer.py
linux上でpythonでescposを操作するプログラムです。

Wireless Debugging — React Native
無線デバッグ機能の使い方を説明しているブログです。

0 件のコメント :