TouchDesignerでThermal Printerの制御

2017/10/23

TDでサーマルプリンタをいじったのでメモ
需要があるのか？
やっていきます

一応がんばったら印刷できた、というものなのでもっといいやり方はあるかもです

###準備するもの

- Arduino UNO
- [Mini Termal Receipt Printer Starter Pack](https://www.adafruit.com/product/600#tutorials)
- (おそらくほぼ同じものがaitendoで購入できるようです。aitendo最高…！→[サーマルプリンタユニット \- aitendo](http://www.aitendo.com/product/16099))
    - [aitendoのサーマルプリンタを使う \- Qiita](https://qiita.com/tsurime/items/e1873ea3580ca600f413)


###参考

- [データシート](https://www.sparkfun.com/datasheets/Components/General/Driver%20board.pdf)
- [Arduino Thermal Printer ライブラリ](https://github.com/adafruit/Adafruit-Thermal-Printer-Library)
- [ofでやってる例](https://forum.openframeworks.cc/t/controlling-thermal-printer-through-arduino/16367)
- [（ARDUINO）ARDUINOにプリンターを接続する：ロシア語はじめました：So\-netブログ](http://roshiago.blog.so-net.ne.jp/2012-01-29)

基本的にシリアルで値をぶち込めばプリントできます。データシート参考に。

##配線
プリンタの配線をします。付属のケーブルでは黒がGND、黄色がRX、緑がTXなのでそれぞれArduinoのGND、0番ピン、1番ピンへ。またACアダプタでプリンタに電源供給もしてあげます。これだけ、簡単ですね

##文字の印刷
<img width="1390" alt="スクリーンショット 2017-10-23 17.22.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/211574/57337f44-2929-ec37-dcd4-7fb5777f5998.png">
Serial DATの使い方を参考にポートを開けます。Baud Rateは19200にします。あとはCHOP Executeからsend()等で印字したい文字を送信し、改行することでプリントできます。
sendBytes()でASCIIコードを送ってもよい。0x0a(改行)を送るとプリントされます。
→ASCIIコード表: http://www9.plala.or.jp/sgwr-t/c_sub/ascii.html
<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/iOMrjB8Jm9">pic.twitter.com/iOMrjB8Jm9</a></p>&mdash; koma (@koma_JB62) <a href="https://twitter.com/koma_JB62/status/1002166548558053376?ref_src=twsrc%5Etfw">2018年5月31日</a></blockquote>


```.py
def onOffToOn(channel, sampleIndex, val, prev):
    op('serial1').send('Hello World', terminator='\n') # 文字列 + 改行
    op('serial1').sendBytes(0x0a, 0x0a, 0x0a)
    return
```

for文を使ったりして、tableから文字列を読み出すこともでき、すごい、というかTouchDesignerがすごい

```.py
for i in range(op('table1').numRows):
        t = str(op('table1')[i, 0])
        op('serial1').send(t)
        op('serial1').sendBytes(0x20) # space
    op('serial1').sendBytes(0x0a, 0x0a, 0x0a)　# print
```

##コマンドによる操作
コマンド入力によって文字を太くするなど様々な設定ができます。
データシートに一覧がのっているので参考にしてください。

文字に関するコマンド

```.py
    # print mode select
    op('serial1').sendBytes(0x1b, 0x21, 0b00000000)

    # double width mode
    # op('serial1').sendBytes(0x1b, 0x0e) # enable
    # op('serial1').sendBytes(0x1b, 0x14) # disable
    
    # character updown mode
    # 0:disable 1:enable
    op('serial1').sendBytes(0x1b, 0x7b, 0)
    
    # white/black reverse printing
    # 0:disable 1:enable
    op('serial1').sendBytes(0x1d, 0x42, 0)
    
    # select charset(?)
    op('serial1').sendBytes(0x1b, 0x52, 0)
    # select character code table(?)
    op('serial1').sendBytes(0x1b, 0x52, 0)
```

print mode selectは２進数の各位の01を切り替えることでモードのon/offを切り替えます。0がoff、1がon。
BIT0: 
BIT1: 
BIT2: 
BIT3: Emphasized mode - 太字
BIT4: Double height mode - 高さが二倍になる
BIT5: Double Width mode - 幅が二倍になる
BIT6: Deleteline mode - 打ち消し線 使えない？
BIT7: Underline mode - 下線

例) 0b10101000: 幅二倍で下線かつ太字

##TOPで出力した画像を印刷する
Arduinoのライブラリを使えば簡単にjpg画像を印刷できますが、Arduinoのメモリの問題で大きな画像は印刷できません。SDカードスロットを使うなどの方法があるようですが、今回は動的に生成した画像を印刷したいので、Arduinoのメモリを経由せずにシリアルから直接データを送ることで印刷したいと思います。
横幅384px、高さが255px以上のものは一度に送信ができないので、分割する必要があります。

###画像の用意

<img width="1043" alt="4bb91d67-bb74-4c6e-9217-32737f733780.png" src="https://qiita-image-store.s3.amazonaws.com/0/211574/2eba4fda-740c-4072-861e-6d14177d1c77.png">
適当な画像を用意します。解像度を384x255以下にし、ThresholdTOPで２値化してnullにおいておきましょう。


###bitmap化して送る
画像をプリンタで印刷できる形にするためには、１つ１つのピクセルが白か黒か、という1bitの情報を8bitづつまとめて送る必要があります。
![IMG_8338.jpg](https://qiita-image-store.s3.amazonaws.com/0/211574/75161c35-cba8-5e80-7b41-58ab8d9d3d6b.jpeg)

はじめにprint bit imageのコマンド、画像の高さ、横幅のバイト数を送り、その後バイトに変換したデータを一つづつ送ります。一度に大量のデータを送信するとエラーが出るので、処理の途中にsleepを噛ませてあげています。

以下をCHOPExecuteで実行することで印刷ができる！
コードはこれを参考にしました。→https://github.com/adafruit/Python-Thermal-Printer

```.py
import time

def onOffToOn(channel, sampleIndex, val, prev):
    image = op('null1') 
    serial = op('../serial1')
    width = image.width
    height = image.height
    
    if width > 384:
        width = 384 # 384px以上は切れる
        
    rowBytes = int((width + 7) / 8)
        
    numHeight = int(height / 255)  # 画像を何分割するか

    for l in range(numHeight+1):
        if l == numHeight:
            _height = height-255*numHeight
        else:
            _height = 255
        
        serial.sendBytes(18, 42, _height, rowBytes) # print bit image
        
        for y in range(_height):
            n = y * rowBytes
            x = 0
            for b in range(rowBytes):
                sum = 0
                bit = 128
                while bit > 0:
                    if x >= width: break
                    if int(image.sample(x=x, y=(height-255*l)-y)[0]) == 0:
                        sum |= bit
                    x    += 1
                    bit >>= 1
                serial.sendBytes(sum)
                time.sleep(0.001)
        time.sleep(0.01)

    return
```

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">プリンタのハック大成功🍌 <a href="https://twitter.com/hashtag/touchdesigner?src=hash&amp;ref_src=twsrc%5Etfw">#touchdesigner</a> <a href="https://t.co/n6sLUxBIX2">pic.twitter.com/n6sLUxBIX2</a></p>&mdash; koma (@koma_JB62) <a href="https://twitter.com/koma_JB62/status/926600597209473024?ref_src=twsrc%5Etfw">2017年11月4日</a></blockquote>

バナナが伸びてるのは仕様です