バーコードリーダーでバーコードを読むと発話するPythonサンプル

 バーコードリーダーやUSBキーボードを接続するでPaPeRo i 本体に接続したUSBバーコードリーダーやキーボードをC言語で読む例を紹介しましたが、今回はバーコードを読むと登録した名前を呼んで挨拶するサンプルアプリをPythonで作成しました。

PythonからUSBバーコードリーダーを読む方法

 一般にUSBバーコードリーダーは接続したホストから見るとUSBキーボードと同じに見えるものが多い様なので、そのタイプのUSBバーコードリーダーを対象としています。通常のLinuxシステムでは映像出力があってコンソール端末として使用でき、USBキーボードも標準入力から取得できる様にドライバが組み込まれていますが、PaPeRo i にはコンソールがないためInput Subsystemレベルで扱う必要があります。具体的には以下の方法でバーコード入力を読み取ることができます。

  • USBキーボードやバーコードリーダーを接続すると/dev/input以下にevent0などのファイルが生成され、/dev/input/by-id以下には例えば”usb-Opticon_Opticon_USB_Barcode_Reader-event-kbd”といった判別しやすい名前で/dev/input/eventXへのシンボリックリンクが作成されます。
  • そのファイルをバイナリモードでopen()しread()することでバーコードのデータを取得できます。
  • データはlinux/input.hで定義されているstruct input_eventのフォーマットになっており、Pythonのstructモジュールを使って個々の値に分解できます。

Pythonコードの例です。

import glob
import struct

fmt = 'llHHI'
event_size = struct.calcsize(fmt)
lst = glob.glob('/dev/input/by-id/*event-kbd')
if 0 < len(lst):
    path = lst[0]
    fd = open(path, 'rb')
    if fd is not None:
        while True:
            try:
                event = fd.read(event_size)
                (tv_sec, tv_usec, type, code, value) = struct.unpack(fmt, event)
            except Exception as e:
                # バーコードリーダーが外された
                fd.close()
                break
            print('type: {} code:{} value:{}'.format(type, code, value))

ここで得られるtype、code、valueは、

  • type: キーイベントの場合1
  • code: キーに対応したコード
  • value: キーを押した場合1、放した場合0

となっています。codeは文字ではなくキーに対応したコードなので、例えばバーコードリーダーが大文字の’A’を読んだ場合、

(1) Shiftキーが押された
(2) ‘a’キーが押された
(3) Shiftキーが放された
(4) ‘a’キーが放された

というシーケンスが送られて来るので、Shiftキーの状態を保持して’a’なのか’A’なのか判別する必要があります。数字や記号についても同様です。

キーコードから文字への変換

 キーコードから文字への変換には、キーコードをキーとし文字を値とする辞書を用いました。キーコードと文字の対応はUSキーボードやJISキーボードなどのキーボードの種類によって異なりますが、通常バーコードリーダーはUSキーボードとして振る舞う様ですので、辞書は以下の様になりました。

from enum import Enum

class Const(Enum):
    KEY_ESC = 1
    KEY_1 = 2
    KEY_2 = 3
    KEY_3 = 4
    KEY_4 = 5
    KEY_5 = 6
    KEY_6 = 7
    KEY_7 = 8
    KEY_8 = 9
    KEY_9 = 10
    KEY_0 = 11
    KEY_MINUS = 12
    KEY_EQUAL = 13
    KEY_BACKSPACE = 14
    KEY_TAB = 15
    KEY_Q = 16
    # ...略...

char_dic = [
    {
        Const.KEY_1.value: '1',
        Const.KEY_2.value: '2',
        Const.KEY_3.value: '3',
        Const.KEY_4.value: '4',
        Const.KEY_5.value: '5',
        Const.KEY_6.value: '6',
        Const.KEY_7.value: '7',
        Const.KEY_8.value: '8',
        Const.KEY_9.value: '9',
        Const.KEY_0.value: '0',
        Const.KEY_MINUS.value: '-',
        Const.KEY_EQUAL.value: '=',
        Const.KEY_TAB.value: '\t',
        Const.KEY_Q.value: 'q',
        # ...略...
    },
    {
        Const.KEY_1.value: '!',
        Const.KEY_2.value: '@',
        Const.KEY_3.value: '#',
        Const.KEY_4.value: '$',
        Const.KEY_5.value: '%',
        Const.KEY_6.value: '^',
        Const.KEY_7.value: '&',
        Const.KEY_8.value: '*',
        Const.KEY_9.value: '(',
        Const.KEY_0.value: ')',
        Const.KEY_MINUS.value: '_',
        Const.KEY_EQUAL.value: '+',
        Const.KEY_Q.value: 'Q',
        # ...略...
    },
]

def get_char(code, shift=False):
    # キーコードから文字への変換
    idx = 1 if shift else 0
    if code in char_dic[idx]:
        return char_dic[idx][code]
    return ''

バーコード入力監視スレッドクラス

 バーコードリーダーは読んだコードの最後に改行コードを付加して送ってくるので、

  • バーコードからの入力文字を蓄積する
  • 改行コードが送られてきたら入力文字列でコールバックを呼び出す

という動作をするスレッドクラスBarcodeReaderを作成し、PaPeRo i アプリと連携させました。

PaPeRo i アプリ

 PaPeRo i の制御には今回もpypapero.pyのラッパークラスstpypapero.pyを使いました。以下の様にBarcodeReaderクラスのコールバックでstpypaperoのイベントを発生させています。

import pypapero
from stpypapero import StPyPapero

MID_BARCODE = '#barcode'


class BarcodeApp(StPyPapero):
    def initialize(self):
        # 初期化
        self.barcode_reader = BarcodeReader(callback=self.barcode_callback)
        self.barcode_reader.start()
        self.trans_state(self.in_init)

    def barcode_callback(self, txt):
        # バーコード入力コールバック: MID_BARCODEイベントを生成する
        self.put_ex_event(MID_BARCODE, {KEY_VALUE: txt})

    def in_init(self, name, msg):
        # 初期状態イベント処理
        if name == 'Ready':
            self.speech('バーコードを読込ませてね')
            self.trans_state(self.in_idle)

    def in_idle(self, name, msg):
        # 待ち状態イベント処理
        value = msg.get(KEY_VALUE)
        if name == MID_BARCODE:
            if value in self.user_dic:
                self.speech('{}さんこんにちは'.format(self.user_dic[value]))
            else:
                self.speech('こんにちは')

バーコードリーダーからの入力文字列から名前に変換するのにself.user_dicを使用していますが、これはwebconfを使用してブラウザから設定できる様にしました。

ソースコードはこちらからダウンロードできます。


0