はじめに
@yukisato1987 さんの『「OKグーグル領収書印刷して!」をobnizとGoogleホームで作る』記事を読んで、「自分もこのサーマルプリンタ(DP-EH600)から何か出力したい」衝動に駆られ、Amazonでポチったのが切っ掛けです。
さらに、DP-EH600 をググっていたら、「Maker向けサーマルプリンタ『DP-EH600』を爆速化する」記事を見つけ、イメージ出力にも十分耐える性能が出せることが分かり、この記事に述べる制作に取り掛かることにしました。
制作するもの
スマホで撮影、画像データを白黒二値化、obnizを通してサーマルプリンタ(DP-EH600)にイメージデータを出力、 という流れです。
作業としては、大きく下記の3つ。
- DP-EH600の高速化
- スマホで撮影、画像データを白黒二値化、obnizへ出力する一連のHTMLとJavaScriptを書く
- obnizからDP-EH600をアクセスするためのライブラリの作成
DP-EH600の高速化
白黒とはいえ多量のイメージデータをプリンタに送り込むため、可能な限りプリンタ出力の高速化が必要だと思います。
前出の記事によると、UARTのボーレートを9600bpsから115200bpsに上げることによりトータルのプリンタ性能を劇的に上げる方法が詳しく説明されています。9600bpsでは64秒かかったものが115200bpsでわずか11秒に。さらにサーマルヘッド関係のパラメタをチューニングすることで7秒にまで高速化できたというものです。
文字通り爆速化により速度を9倍にできたというから、やらない手はないと思います。
この記事で使われているUSBシリアル変換器と同型の物を持ち合わせていたので、作業を始めてからわずか数分で115200bps化が完了しました。ボーレートの変更と同時に使用する文字セットをUTF-8に変更したせいかテストプリントの内容が変わりました。
前出のAmazonの商品説明には「日本語は使用できません」としっかり書かれていますが、UTF-8に変更したことにより、日本語もちゃんと出ました。(しかし、'・'(中点)は印字できなかったので、すべての日本語(漢字)が出る分けではないのかも)
文字がかすれ気味なのはサーマルヘッド関係の設定をちゃんとしなかったからでしよう。
通信レートが上がったことでプリンタの印字速度を上回ってしまうため、このプリンタの基板からRTS信号を引き出してフロー制御に利用する必要がでてきました。前出の爆速化の記事では、
RTSピンを引き出すには、もちろん分解して半田付けしてもいいけれど、ひとまずサンハヤトのスルーホール用テストワイヤをぶっ刺した。
と、説明されていますが、私が購入したプリンタは、この穴がハンダで完全に埋められていたため、仕方なく分解してジャンパー線をハンダ付けしました。
以上で高速化は完了です。
HTMLとJavaScriptを書く
DP-EH600で出力できるイメージの横幅は384ピクセルのため、画像データの短辺が384pxになるようにイメージデータをリサイズし、次にイメージデータを白黒二値化します。白黒二値化には誤差拡散法というアルゴルズムを使うのが一般的なようですが、まずは単純に閾値判定による二値化で進めます。後ほど体力があれば誤差拡散法にも挑戦したいと思います。
HTML5になって、スマホのカメラ起動から画像データを得ることが、下記のようにinputタグ一つですごく簡単に書けます。同じHTMLをPCで開くと、イメージデータ選択ダイアログとして働く優れもの。
<h1>Camera</h1>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" id="ufile" name="capture" accept="image/*" capture="camera" />
</form>
<2018.12.16追加>
上記のinputタグの記述は間違いではないですが、これまで通りのinputタグ(type=file)でもカメラで撮影やライブラリから選択が普通にできます。
DP-EH600アクセスライブラリ
どうせなのでイメージ出力専用ではなく、普通のプリンタのようにテキストやバーコードも出力できるようにして、将来、obnizのパーツライブラリに取り込める形で、汎用的な機能を盛り込みます。
以下、使用例です。
//Javascript
const pr = obniz.wired('DPEH600', {gnd:0, vcc:1, gnd2:9, tx:10, rx:11, baud:115200, cts:7});
pr.init();
pr.printText("Hellow!");
pr.printText(["こんにちは。", "日本語も出力できました。", "書体が中華風ですが..."]);
pr.linefeed(2);
//Javascript
const pr = obniz.wired('DPEH600', {gnd:0, vcc:1, gnd2:9, tx:10, rx:11, baud:115200, cts:7});
pr.init();
const canvas = $("#canvas");
const context = canvas[0].getContext('2d');
const imageData = context.getImageData(0, 0, width, height).data;
const bwBitmap = dithering(imageData, width, height);//白黒二値化
pr.printBitmap(bwBitmap, width, height, true); //白黒反転
pr.linefeed(2);
まとめ(中間)
白黒二値化の方法として、一旦グレースケール値を求めそれを閾値との比較で白・黒と判定する単純なアルゴリズムとしました。
const grayScale = (0.299 * imageData[n + 0] + 0.587 * imageData[n + 1] + 0.114 * imageData[n + 2]);
black_and_white_pixels[index] = (grayScale < this.black_threshold) ? 0 : 1;
閾値(black_threshold)を48で試した結果が、次の画像(家の中を撮った写真)です。まあ輪郭は分かるかな程度の精度ですね。
今回の出力時間はおよそ13秒でした。
obnizにつながっている灰色のジャンパー線がプリンタから引き出したRTS信号で、obnizのuartインタフェースのctsピンに指定しています。
閾値の調整でもう少し見映えが良くなる可能性がありますが、やはりもっと高度なアルゴリズムにする必要がありそうです。今回は 「まとめ(中間)」 として、今後改善してこの記事をアップデートしたいと思います。
また、その時に各ソースコードもすべて公開したいと思います。
<2018.12.16追加>
誤差拡散法に変更した続編はこちら。