ESP/POS プリンタを python-esppos で

# サーマルプリンタを使いたい

ということで、ヤフオクで安いものを入手。

スター精密株式会社の TSP650。

[]( scale=0.5 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/7ba89189-4fe1-f5b8-94a6-93acc3fa13a3.jpeg)

インターフェースは LAN/USB/パラレル/RS-232C のそれぞれのバージョンがあって、LANやUSBが使いでがいいのだけれど、今回入手できたのは RS-232C のもの。

安くカンタンに入手できたので、まあいいか。

2017/11/2追記: 後でパラレルのものも買い足しました。パラレル接続のアレコレについては後述。

制御コードにスター独自コマンドのスターラインモードと、Epson 機互換の ESC/POS モードがある。ESC/POSなら何とかなるだろうと思って以下のようにチャレンジしてみた。

# ESC/POS

これはシチズンの資料だけれど。

http://www.citizen-systems.co.jp/support/download/printer/cmp20_30data/J/CMP-2030ComMan_ESCPOS_V103J.pdf

スター精密のものは海外の資料がありました。

http://www.starmicronics.com/support/mannualfolder/escpos_cm_en.pdf

# python-esppos

本家はここ。

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

ドキュメント。

http://python-escpos.readthedocs.io/en/latest/user/usage.html


PDFのマニュアル。

https://media.readthedocs.org/pdf/python-escpos/latest/python-escpos.pdf

また、フォークして日本語対応したのはここ。

https://github.com/lrks/python-escpos


## 設定と準備

DIPスイッチを変更して、ESP/POSモードにする。

[]( scale=0.2 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/7fca8724-3a17-7797-12e5-9fba8d4ea7c8.jpeg)
写真で1つだけOFFになっているところがそれです。

印字用紙はAmazonで安く買える互換品。80mm幅。危うく60mm幅を注文しそうになりました。

インターフェース。昔懐かしのPC-98的な形のRS-232C。

[]( scale=0.2 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/71ab372e-b60f-ea39-9e14-a57ec1e525fd.jpeg)

血糊がついているのは、イベント用にデコったものです。

RS-232Cは今時のPCには滅多についてないですね。マイコンなどではよくUART だとかシリアルポートだとかがあります。これらはRS-232Cと論理的には同じですが、電気的には異なります。マイコンチップから直接出ているシリアルポートはTTLレベルで、Hi-3.3V / Lo-0V ですが、RS-232C は Hi-12V / LO--12V で、この信号をつなげるとマイコンが壊れてしまいます。

今回は USB-RS232C アダプタ
「ＦＴ２３２　ＵＳＢ－シリアル変換ケーブル　ＶＥ４８８」
http://akizukidenshi.com/catalog/g/gM-08343/

を使って、電気的なところはカンタンにクリアすることにしました。

このコネクタはDP9P Male。

TSP650 は RS-232C DP25P FEMALE。



信号はTSP650 のハードウェアマニュアルを参照。

http://sp-support.star-m.jp/Mannualfolder/tsp650_hm_jp.pdf

[]( scale=0.4 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/4036b9b9-3029-e20d-ad40-6b552faf6efa.png)


信号的にはヌルモデム状の接続（クロス接続）が必要になります。

ヌルモデムとはいっても、結線は細かいバリエーションがあります。大体は最大公約数的な配線で動くのですが、時々ハードウェアによっては細かい違いで動かない時があったりするので、今回はハードウェアマニュアル通りの結線変換アダプタを作りました。

[]( scale=0.1 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/77fd8176-2358-1383-5511-baf373e07f34.jpeg)

なお、後述するように最初は結線を間違えて何も出力されずに悩みました。
写真は結線を直した後のものです。

# インストール

## python-escposを使う

### Ubuntu 16.04

まずはpipでインストール。

```

$ pip3 install python-escpos
Collecting python-escpos
  Downloading python_escpos-2.2.0-py2.py3-none-any.whl
Collecting argparse (from python-escpos)
  Downloading argparse-1.4.0-py2.py3-none-any.whl
Collecting argcomplete (from python-escpos)
  Downloading argcomplete-1.9.2-py2.py3-none-any.whl
Collecting appdirs (from python-escpos)
  Downloading appdirs-1.4.3-py2.py3-none-any.whl
Collecting pyusb>=1.0.0 (from python-escpos)
  Downloading PyUSB-1.0.0.tar.gz (52kB)
    100% |████████████████████████████████| 61kB 521kB/s 
Collecting Pillow>=2.0 (from python-escpos)
  Downloading Pillow-4.2.1-cp35-cp35m-manylinux1_x86_64.whl (5.8MB)
    100% |████████████████████████████████| 5.8MB 215kB/s 
Collecting pyserial (from python-escpos)
  Using cached pyserial-3.4-py2.py3-none-any.whl
Collecting qrcode>=4.0 (from python-escpos)
  Downloading qrcode-5.3-py2.py3-none-any.whl
Collecting six (from python-escpos)
  Downloading six-1.11.0-py2.py3-none-any.whl
Collecting pyyaml (from python-escpos)
  Downloading PyYAML-3.12.tar.gz (253kB)
    100% |████████████████████████████████| 256kB 345kB/s 
Collecting olefile (from Pillow>=2.0->python-escpos)
  Downloading olefile-0.44.zip (74kB)
    100% |████████████████████████████████| 81kB 843kB/s 
Building wheels for collected packages: pyusb, pyyaml, olefile
  Running setup.py bdist_wheel for pyusb ... done
  Stored in directory: /home/nanbuwks/.cache/pip/wheels/18/b0/b8/83d67cc7a3b50866f908ca1104b553f7807e1e767ddf373427
  Running setup.py bdist_wheel for pyyaml ... done
  Stored in directory: /home/nanbuwks/.cache/pip/wheels/2c/f7/79/13f3a12cd723892437c0cfbde1230ab4d82947ff7b3839a4fc
  Running setup.py bdist_wheel for olefile ... done
  Stored in directory: /home/nanbuwks/.cache/pip/wheels/20/58/49/cc7bd00345397059149a10b0259ef38b867935ea2ecff99a9b
Successfully built pyusb pyyaml olefile
Installing collected packages: argparse, argcomplete, appdirs, pyusb, olefile, Pillow, pyserial, six, qrcode, pyyaml, python-escpos
Successfully installed Pillow-4.2.1 appdirs-1.4.3 argcomplete-1.9.2 argparse-1.4.0 olefile-0.44 pyserial-3.2.1 python-escpos-2.2.0 pyusb-1.0.0 pyyaml-3.12 qrcode-5.3 six-1.11.0
You are using pip version 8.1.1, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

```
さて、以下のような印字サンプルプログラムがレポジトリのreadme.mdに書いてあったのでそれをRS-232C用に直してtestesppos.pyとして作りました。


```

from escpos import *
""" Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """
Epson = printer.Serial("/dev/ttyUSB0")
# Print text
Epson.text("Hello World\n")
# Print image
Epson.image("logo.gif")
# Print QR Code
Epson.qr("You can readme from your smartphone")
# Print barcode
Epson.barcode('1324354657687','EAN13',64,2,'','')
# Cut paper
Epson.cut()
```
何も出力されません。

あれれー？と思ったら、アダプタの配線がRXTXが間違えてました。直したらうまく印刷できました。

### Raspberry pi

```

$ uname -a
Linux raspberrypi 4.9.24-v7+ #993 SMP Wed Apr 26 18:01:23 BST 2017 armv7l GNU/Linux

```

ちょっと前のRaspbianです。

```

$ sudo pip3 install python-escpos
Downloading/unpacking python-escpos
  Downloading python_escpos-2.2.0-py2.py3-none-any.whl
Downloading/unpacking argcomplete (from python-escpos)
  Downloading argcomplete-1.9.2-py2.py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): pyyaml in /usr/local/lib/python3.4/dist-packages (from python-escpos)
Requirement already satisfied (use --upgrade to upgrade): Pillow>=2.0 in /usr/lib/python3/dist-packages (from python-escpos)
Downloading/unpacking pyserial (from python-escpos)
  Downloading pyserial-3.4-py2.py3-none-any.whl (193kB): 193kB downloaded
Downloading/unpacking qrcode>=4.0 (from python-escpos)
  Downloading qrcode-5.3-py2.py3-none-any.whl
Downloading/unpacking argparse (from python-escpos)
  Downloading argparse-1.4.0-py2.py3-none-any.whl
Downloading/unpacking pyusb>=1.0.0 (from python-escpos)
  Downloading PyUSB-1.0.0.tar.gz (52kB): 52kB downloaded
  Running setup.py (path:/tmp/pip-build-ou4gckj8/pyusb/setup.py) egg_info for package pyusb
    
Downloading/unpacking appdirs (from python-escpos)
  Downloading appdirs-1.4.3-py2.py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): six in /usr/local/lib/python3.4/dist-packages (from python-escpos)
Installing collected packages: python-escpos, argcomplete, pyserial, qrcode, argparse, pyusb, appdirs
  Running setup.py install for pyusb
    
Successfully installed python-escpos argcomplete pyserial qrcode argparse pyusb appdirs
Cleaning up...
```

```

$ python3 testesppos.py 
Serial printer enabled
Traceback (most recent call last):
  File "testesppos.py", line 7, in <module>
    Epson.image("logo.gif")
  File "/usr/local/lib/python3.4/dist-packages/escpos/escpos.py", line 83, in image
    im = EscposImage(img_source)
  File "/usr/local/lib/python3.4/dist-packages/escpos/image.py", line 37, in __init__
    img_original = Image.open(img_source)
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2251, in open
    fp = builtins.open(fp, "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'logo.gif'
```
何も出ない・・・と思ったら、サンプルプログラムから呼び出している logo.gif が無いだけでした。サンプルプログラムを書きなおして


```

pi@raspberrypi:~ $ vim testesppos.py
pi@raspberrypi:~ $ python3 testesppos.py 
Serial printer enabled
```

うまく動きました。


# NVRAM へイメージを登録して使う

## NVRAM へイメージ登録　ユーティリティー編

やっぱりimageを印刷したりしようとすると時間がかかります。
事前にイメージをプリンタに登録しておき、それを呼び出す方法だと早くなります。

ということで、NVRAMに登録しました。

登録する機能はpython-escposに無いので、WindowsPCを使ってインストールしました。
「Star Micronics プリンタユーティリティ」

[]( scale=0.5 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/d61b4654-245e-f9dd-bb03-c350b12d99ba.png)

## NVRAM へ登録したイメージを使う

上のサンプルプログラムに以下を書き足すと、登録ロゴ1番が印刷されます。

```

Epson._raw(b'\x1c'+b'\x70'+b'\x01'+b'\x00') # NV image 1 print
```
ESPPOSのコマンド「FS q」を使用しています。

# パラレルインターフェース


やっぱりRS-232Cはimageとか使うと遅くなってしまいます。
調子に乗って、パラレル接続のタイプをヤフオクで入手しました。

とはいってもいまどきパラレルインターフェースがついているものなんかはないのでパラレルUSB変換ケーブルを使いました。

[]( scale=0.2 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/3bd78d19-8a43-6683-f4e5-e378937e73e8.jpeg)


```

[19933.347037] usb 1-1.2: new full-speed USB device number 5 using ehci-pci
[19933.456682] usb 1-1.2: New USB device found, idVendor=1a86, idProduct=7584
[19933.456687] usb 1-1.2: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[19933.456690] usb 1-1.2: Product: USB2.0-Print 
[19933.459045] usblp 1-1.2:1.0: usblp0: USB Bidirectional printer dev 5 if 0 alt 1 proto 2 vid 0x1A86 pid 0x7584
```

と出たので

```

# //Epson = printer.Serial("/dev/ttyUSB0",9600,8,1)
Epson = printer.Usb(0x1A86,0x7584)
```

とするとうまくいきました

# パラレルインターフェースでNVRAMに画像を保存する

## Windowsではアレな件

さて、先の「Star Micronics プリンタユーティリティ」を使ってNVRAMに登録しようとしたら・・・

LPTのデバイスしか選べません。
マジかよ「Star Micronics プリンタユーティリティ」最低だな。


[]( scale=0.5 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/febf8fc0-7e41-d292-8aae-d8f5fae5ced3.png)

Windowsでは先のUSBプリンタアダプタはUSB001となります。

[]( scale=0.5 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/db38ccd3-66e5-75f4-2663-17ceed7a6c88.png)

試しにUSB001でWindowsのプリンタとして登録しましたが、オフラインのままで印刷できませんでした。
マジかよWindows10最低だな。

[]( scale=0.5 )![](https://qiita-image-store.s3.amazonaws.com/0/139524/d6074bea-d35a-af91-9920-1c9dca4471a3.png)

## Linuxでアレコレした件

結局以下のpythonプログラムを作ってイメージを登録しました。

```

import sys
from escpos import *
import math
from escpos.constants import GS
import six
import numpy
import time
from PIL import Image

argvs = sys.argv
argc = len(argvs)

if (argc != 2):
    print( 'Usage: # python %s imagefilename' % argvs[0])
    quit()

#im=Image.open("test.png")
im=Image.open(argvs[1])
wb_im = im.convert("1")
size=wb_im.size
imagebuffer = []
imgArray=numpy.asarray(wb_im,numpy.bool8)
print(imgArray)
print( size[0],"dot x ",size[1],"dot")

for x in range(size[0]):
  multiple=256
  byte=0
  for y in range(size[1]):
    if ( True== imgArray[y][x] ):
      bit=0
    else:
      bit=1
    multiple=multiple/2
    byte=byte+bit*multiple
    if (1==multiple):
      multiple=256
      imagebuffer.append( byte )
      byte=0
  if (256!=multiple):
    imagebuffer.append( byte )
for x in range(7):
  for y in range(size[1]):
    imagebuffer.append( 0 )

# wb_im.show()


def touroku(p):
  p._raw(b'\x1c'+b'\x71'+b'\x01')
  x_8 = math.ceil( size[0]/8 )
  y_8 = math.ceil( size[1]/8 )
  print( x_8.to_bytes(2, 'little'))
  print( y_8.to_bytes(2, 'little'))
  p._raw( x_8.to_bytes(2, 'little')+y_8.to_bytes(2, 'little'))

  for  item in range(x_8*y_8*8):
#    print( item," ",int(imagebuffer[item])," ",end="")
    p._raw(six.int2byte(int(imagebuffer[int(item)])))
  p._raw(b'\x01')
  time.sleep(15.0)


#p = printer.Serial("/dev/ttyUSB0",9600,8,1)
p = printer.Usb(0x1A86,0x7584)

# image register to NVRAM
touroku(p)

#Print text
p._raw(b'\x1c'+b'\x70'+b'\x01'+b'\x00') # NV image 1 print
p.text("Hello World\n")
p.cut()
```