今回は、バーコードリーダを作ってみたいと思います。早速、考察から開始しましょう。
と、言っても、ご存知ですよね?スーパマーケットに行けば、ほぼ全ての商品にバーコードが付いているし、記録する情報量を圧倒的に増加させる、二次元バーコード(QRコード等)なんてものもあるくらいです。
バーコードを検索する(別ウインドウが開きます)
今回作ろうとするのは、一次元のバーコードを読み取るバーコードリーダです。
前節で検索してみたような、一般的に使用されているバーコードは線が細かすぎて、手持ちの部品を使って読み取るのは困難です。本ページで作製するのは、オリジナルのバーコードを読み取る、「バーコードもどき」リーダです。以下の通り、読み取り可能なバーコードの仕様を策定します。
バーコードもどき仕様:
上の仕様に基づき、作図したバーコードは、以下の通りです。
これをプリンタで印刷して実験に使うのが良いでしょう。以下のスクリプトを使うと、任意の8ビットデータをバーコードに変換して描画することができます。
バーコード作図スクリプト:bcode_draw.olv
※スクリプト(.olv)の実行には拙作Olive+をWindowsにインストールしてください。
実行すると、数値を問い合わせるウインドウが開きますので、8ビットの数値を書き換えて、OKボタンを押してください。バーコードを別なウインドウに描画します。
描画が終わると、「スキャンするか?」と問い合わせてきますが、Cancelボタンでキャンセルしてください。この先、バーコードリーダを組み上げたら、ArduinoをUSBで接続して、「スキャンするか?」の問いにOKボタンを押すと、Arduinoへ【capt】コマンドを送信し、吸い上げたデータをグラフ化して表示するデバッグ用の機能が備わっています。
仕様として、細線:太線の比率を1:2と規定したのですが、前節のサンプル画像で確認すると、意外に細線と太線の差が感じられません。本当は、もう少し細線と太線の比率を変えたかったのです。細線は、より細く、太線は、より太く、例えば1:3とか、1:4とか。
でも、実際に試してみると、以下の様な不都合が生じることが判りました。
細線と太線のバランスを取った結果、1:2程度に落ち着きました。
もう少し、明るさの変化に対して感度が高く、出力レベルの変化速度も高速な明度センサを使ったり、スリットをもっと細くしたりすると、対策可能なはずです。CdSセルだと、この辺が限界でした。
※フォトトランジスタとか、フォトダイオードを使うと、もう少しマシなのかな??手持ちにTCRT5000Lと言う、反射型赤外線センサ(赤外線エミッタ→フォトトランジスタ対)が有ったので、そちらを使った方が良かったか?興味のある方は、明度センサの種類を変えて試してみてください。
ここに至るまでの考察で、ちらほら登場している通り、CdSセル(フォトレジスタ)を使って、紙面から反射してくる光の明るさの変化を読み取る方法でバーコードを読み取ろうと考えています。
CdSセルを裸で使うと、広い角度から光が差し込んでしまって、細いバーコードの黒線を識別できません。そこで、スリットを使って入射光の範囲を絞り、細いバーコードの黒線を認識させようと考えました。
スリットとして使用するため、ピンホールの様な、先端に穴の開いた筒状の物を探してみたところ、使えなくなったボールペンの先に目が止まりました。下図の様なボールペンのペン先の中にCdSセルを入れ、ペン先の穴から光を導入します。
自然光の、紙面からの反射光だけを期待すると、スキャナ自体の影等によって明度が変化してしまうため、スリットの近くに高輝度の白色LEDを配置して紙面を照らす事にしました。
LEDの光 → 紙面で反射 → スリット → CdSセルと到達します。白い紙面と黒いバーコード部分では、反射光の明るさが異なるので、CdSセルで明度の変化を読み取る事でバーコードを識別します。
バーコードスキャナの回路図は以下の様になりました。
CdSセルは、周囲の明るさに従って抵抗値が変化する素子です。
アナログ端子A0からAD変換(analogRead())して電位を読み取り、明るさの変化を使ってバーコードを読み取ります。
スーパマーケットのPOSシステムなどで使われる、レーザ光線などで読み取る方式のバーコードリーダと異なり、バーコードにスキャナ(上図)を密着させて、バーコードの上を一定速度で移動させることで読み取る手動方式と、なります。
エミッタ(高輝度白色LED)の電流制限抵抗値R2は、D2端子の出力電圧Vo、電流駆動能力Io、LEDの順方向電圧Vf、等から、適切な値を選択してください。
関係式:Vo−Vf=Io×R2
スキャナヘッドの組み立て方はこちらです。
今回は、表示器としてOLEDディスプレイ(3.3V定格、下図)を使うため、Arduino DUE(3.3V定格)を使おうと思います。
バーコードリーダとしては、Arduino UNO(5V定格)等でも、動作させることは可能だと思われます。
※ただし、UNO(5V定格)を使う場合、OLEDディスプレイ(3.3V定格)との接続には、間にレベルシフタ(L/S)を挟む必要があるし、当方では、DUEでしか動作確認していないことに留意してください。
まず、バーコード上でスキャナを移動させた時のCdSセルから読み取った電位変化が、どの様な値となるのか調べる事から始めました。
後ほどリンクする完成プログラムに組み込んだ【capt】コマンドを実行すると、スキャナから読み取ったアナログ量をデータ列として取り込むことができるようにしてあります。このデバッグ用機能を使って取得した、バーコード取り込みデータをグラフ化したところ、以下図の様になりました。
横軸のフルスケールが取り込み時間約4s、縦軸は8ビットに丸めたAD変換値で、フルスケールが256です。
この様な波形を何度か取得して調整したことで、何とか、0を表す細い線と、1を表す太い線を判別できそうな見通しを得ました。
前節の予備実験結果を踏まえて、以下の通り、検出アルゴリズムを考案しました。
バーコード検出アルゴリズム:
検出した後で、バーコードの目標仕様と照らし合わせ、以下のエラーをチェックします。
エラーが発生しなかった場合は、正常に読み取りできたと判定します。
なお、UNOでも実行できる様に、取り込むデータ量と、処理速度を調整した結果、以下のようにパラメータを決定しました。
取り込み間隔(サンプリングレート):4ms
取り込みデータバッファ数:1,024個
const int bcr_msec_INTVL=4; // bcr: sampling rate [ms]
const int bcr_MAX_BUF=1024; // bcr: buffer size
10ビットAD変換結果の上位8ビットだけを残し、8ビット/1サンプルとすることで、1KバイトのバッファRAMが有れば良く、RAM容量2Kバイトしか持たないUNOでも、動作可能なはずです。
読み取り時間は、最大約4秒(≒4ms×1,024サンプル)です。
完成したプログラムを以下にリンクしておきます。
完成プログラム:bcode_reader.ino
コンパイルしてArduinoへ書き込むと、シリアルモニタから以下のコマンドを受け付けるようになっています。
シリアルモニタコマンド仕様:
次節の実験で実際に使ってみて、動作を確認しましょう。
以下の結線表に従って、DUE、スキャナ回路、アクティブブザー、OLEDディスプレイを結線してください。
Arduino DUE | 接続 | スキャナ |
GND | ⇔ | GND |
D2 | ⇔ | D2 |
D3 | → | D3 |
A0 | ← | A0 |
接続 | アクティブブザー | |
D12 | → | + |
D9 | → | (−) |
接続 | OLEDディスプレイ | |
5V | → | VCC |
GND | ⇔ | GND |
D21(SCL) | ⇔ | SCL |
D20(SDA) | ⇔ | SDA |
アクティブブザーとOLEDディスプレイはオプションですので、使わない場合は結線しなくてOKです。以下図に実験中の様子を示します。
実験に使うバーコードは、こちらのスクリプトで描画したバーコードをプリンタで印刷して使えば良かったのですが、こんな時に限ってインク切れ!仕方なく、手書きでバーコードを作図しました(下図)。
上図に示した手書きバーコードの幅は11cmです。1ビット当たり1cm幅で、細線を約2.5mm、太線を約5mmとして作図しました。フレームは、0(スタート),[1,0,1,0,1,1,0,0](データ),1(パリティ),1(ストップ)です。データとして0xacが読み取れたら成功となるバーコードです。
PCなどで正確に作図したバーコードではなく、手書きのバーコード(※)でも、正確に読み取る事ができるでしょうか?実験してみましょう。
※PCなどで機械的に作図したバーコードと異なり、手書き故、「ばらつき」や「ゆらぎ」を多分に含んでいます。
実験サンプル:【 】内はシリアルモニタからの送信データ
【scan】
OK+ data=0xac
OK: scan(0x2b3)
LEDが点灯したら、スキャン開始です。4秒以内にバーコード全体を通過できる様に、スキャナを動かしてください。スキャナを速く動かし過ぎると、CdSセルのレベル変化が間に合わず、検出できなくなってしまうため、逆に、4秒間、目いっぱい使ってバーコード部分をなぞる様、なるべくゆっくり動かすことが成功のカギです。
当方の実験では、一応、読み取り成功しました。何度か繰り返して、スキャナの動かし方に慣れてくると、成功率は上がります。
ただ、何度か繰り返していると、時々、間違ったデータを読み取るケースもありました。
スタートビット"0"とストップビット"1"が正しく読めて、1フレーム11ビット分がスキャンできてしまうと、パリティは確率50%で必ず合致してしまうため、誤ったデータを読み取った場合も、エラーチェックをすり抜けてしまうのです。
根本的には、細線と太線のクリアランスが足りない等の理由が考えられますが、読み誤ることを前提として、パリティビットを2ビット以上に拡張するとか、工夫が必要かもしれないと思いました。
※PCなどで機械的に作図したバーコードを使えば、もう少し認識率が上がるはずなので、それほど悲観的に捉える必要は無いかと……。
以下の通り、バーコードリーダ稼働中の様子をデモ動画にしてみました。是非とも、ご一見ください。
ここから先は皆様の工夫にお任せいたします。
より完成度を向上するためには?:
ご検討(健闘)ください。
手持ちの部品では、こんな太いバーコードを読み取ることがやっとでした。セルフレジなどで使われるバーコードリーダでは、バーコードの位置、回転角度、歪みなどが有っても読み取っていますので、カメラ📷で撮影した映像の中から画像処理で読み取っているのでしょう。今回作ったバーコードリーダは、昔の初期バーコードリーダに近い原理で読み取る物です。