Pythonでレシートプリンタから印刷してみた(Windows環境)

# やりたかったこと

![20250422_134847.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/b8bab31e-866b-4452-a74f-a367580e5ac0.jpeg)

写真のような、小売店などに設置してあるレシートプリンタを操作してみたいと思ったことありませんか？
たまたまレシートプリンタを使えそうなイベントがあったので、ついでに操作方法をまとめてみました。

なお、本記事ではEPSONのTM-T88Vを使用していますが、EPSONのPOS規格に対応した製品なら概ね同じ手順で操作できるかと思います。

# 1. 準備

## テスト環境
- Windows11
- Python 3.10

## レシートプリンタの入手
有名どころはEPSONのTMシリーズだと思います。現行機種のTM-M88IIIやTM-T88VIIはヤフオクでも数万円以上するので、型落ち品であるTM-T88Vを入手しました。購入当初は送料込みで3000円強でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/e3a8a81e-57d4-4233-b706-cf25411bc112.png)
[EPSON レシートプリンター一覧](https://www.epson.jp/products/receiptprinter/printerlist.htm)

型番のうち、TMに続く「M」シリーズは複数の接続インターフェースに対応するモデル、「T」シリーズは単一の接続インターフェースであるモデルです。~~TM-Mシリーズは小型な上、無線LANやBluetoothなどに対応しているのでお金に余裕があれば買いたかった...。~~

ちなみにTM-T88Vは同型番で複数種類あり、今回使用したのはUSB接続モデルの製品です。ヤフオクなどではシリアル接続・パラレル接続のものも多く出回っているので注意が必要です。

## 公式ドライバーの入手と本体設定(必須ではない)
自動カットの設定などができるので一旦入手することをおすすめします。製品によってソフトウェアが異なるので適宜読み替えてください。

1. EPSON公式ページから[TM-T88V Utility](https://www.epson.jp/dl_soft/readme/27258.htm)を入手
1. インストール
**注意：Windows11の「メモリ整合性」機能と互換性がないので、インストール時にはWindows Defenderから「メモリ整合性」をオフにしておく必要があります。**
1. EPSON TM-T88V Utilityを起動

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/54084ff9-1ad7-4fe8-a8b8-7fe022d6d88e.png)

TM-T88V Utilityでは本体設定や情報の確認を行えます。
- 自動用紙カット
用紙交換後の紙送り・自動カットの設定ができます。とりあえず「カバークローズ時に用紙を自動カットする」にしておけば便利かと。
- 印刷制御の設定
印字濃度や速度の設定ができます。こだわりがなければ最大値で問題ないかと。

ちなみに任意のロゴを登録し、印刷毎に印字することも可能なようですが、今回使用するライブラリを使用する場合は設定が無視されるようなので割愛します。
設定が終わったらソフトはアンインストールしてしまっても問題ありません。

# 2. ドライバーの差し替え

公式ドライバーはlibsubに対応していないので、互換ドライバーに差し替えを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/10aa18a8-7f3a-4f72-ad7a-2c67556e2eff.png)

1. [Zadig](https://zadig.akeo.ie/)をダウンロード
1. ソフトを起動し、上部メニュー`Options`→`List All Devices`にチェック
1. プルダウンメニューで「EPSON TM-T88V」(接続したプリンターに対応したもの)を選択
1. `Driver`欄の矢印右側が`WinUSB~`となっていることを確認し、`Replace Driver`をクリック

ドライバーのインストールには数分ほどかかります。インストールが完了したらソフトは閉じて問題ありません。

## ベンダーIDとプロダクトIDの特定

デバイスマネージャーを開き、`ユニバーサルシリアルバスデバイス`の項目に「EPSON」から始まるデバイスが表示されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/aa072b13-f08c-43bb-94ab-f7533855e664.png)

次に当該デバイスのプロパティを開き、`詳細`タブのプルダウンメニューから`ハードウェアID`を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/fa06410b-0f18-4bba-ab78-6458666fa829.png)

このとき表示される`VID_`、`PID_`それぞれに続く4桁がベンダーID、プロダクトIDとなるので16進数としてメモしておきます(写真の場合はベンダーIDが`0x04b8`、プロダクトIDが`0x0202`)。

# 3. Pythonライブラリの入手

必要なライブラリをインストールします。

```bash
pip install pyusb libusb python-escpos
```

## インターフェース番号の特定(USB接続の場合)

プリンターと接続されているUSBポートの番号を特定するため、先ほど確認したベンダーIDとプロダクトIDを書き換えてから以下コードを実行します。

```Python
import usb.util
import usb.core

VENDOR_ID = 0x04b8
PRODUCT_ID = 0x0202

dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
if dev is None:
    raise ValueError("デバイスが見つかりません")
cfg = dev.get_active_configuration()
for intf in cfg:
    print("Interface Number:", intf.bInterfaceNumber)
```

ここまでの手順まで問題無く完了していれば、コンソール上にインターフェース番号が表示されるはずです。

# 4. テスト印刷

これで設定は完了です！インターフェース番号を書き換えて、早速テスト印刷してみましょう。

```Python
from escpos.printer import Usb

VENDOR_ID = 0x04b8
PRODUCT_ID = 0x0202
INTERFACE_NUM = 0

p = Usb(VENDOR_ID, PRODUCT_ID, INTERFACE_NUM, timeout=1000)
p.text("hogehoge\n")
p.qr("https://example.com")
p.cut()
```

![20250422_134311.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/84be1aec-17b8-4fca-b8d5-bf36e1dd7c5e.jpeg)

用紙に文字とQRコードが印刷されていれば成功です。
`import`部分では`Usb`クラスを利用していますが、ほかに`Network`や`Serial`クラスがあるようです。ご自身のプリンター接続方法に合わせて書き換えてください。

## 日本語テキストについて

今回利用したpython-escposライブラリは日本語印刷には非対応のため、日本語を指定すると文字化けします(一応文字コードは指定できるものの不具合があり正しく印刷できない)。
日本語に対応したライブラリも存在するものの、長期間メンテナンスされておらず動作確認ができなかったため、forkライブラリを修正したものを作成しました。

```bash
pip install git+https://github.com/chansei/python-escpos-jp
```

https://github.com/chansei/python-escpos-jp

当該ライブラリをimportすることで日本語テキストの印刷に対応した`jpText`関数が利用できるようになります。

```Python
from escposjp import Usb

p = Usb(0x04B8, 0x0202, 0, timeout=1000)
p.jpInit() # 利用前に初期化が必要
p.jpText("テスト印刷")
p.qr("https://example.com")
p.cut()
```

![20250422_134423.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447714/d9e21ff8-a164-472f-82ef-375ab867d8b0.jpeg)

標準テキストのほかに、Pillowライブラリのimageとしてフォントを埋め込み、画像として印刷する方法もありますが、詳しく解説されている方が居るので割愛します。

https://qiita.com/kurikurisan/items/15660bed340504f02032

# おわりに

Windows環境のPythonから印刷した例が少なく、ドライバー周りの処理に苦しみました。
一度動いてしまえば何でも印刷できそうな予感がします。