USB接続のQRコードリーダ（バーコードリーダ）をRaspberryPiにつなぐ

このページでは、Amazonなどで安く手に入るUSBのQRコードリーダ（バーコードリーダ）をRaspberryPiで利用する手段を作ります。

（目標内容）
　●USBのQRコードリーダが読み取った情報を標準出力で出力するだけ
　　　- QRコードの入力を排他で占有して、プログラムにのみデータを流し込み
　　　- リターン（改行）で一区切りとして、標準出力するだけ

（使うQRコードリーダ）
　●Amazonなどで安いQRコードリーダは、USBのキーボードとして認識されます。
　●シリアル接続として認識される機種もあるようですが、今回は安いキーボード入力として
　　認識されるものを使います
　●QRコードなどを読み取ったデータの後に、リターン（改行）を付与する設定とする。（多分デフォルト）

 今実験で使っているもの
　https://www.amazon.co.jp/gp/product/B07GRMDF2N/

----
（プログラムの内容）
USBのQRコードリーダは、USBのキーボードとして認識されるので、
LinuxのInput Subsystemとしての処理をQRコードリーダ用にカスタマイズして
標準出力するだけ

　次のページを参照させていただきました。（大変勉強になりました）
　http://www.tatapa.org/~takuo/input_subsystem/input_subsystem.html

以下がソースコードになります。gccでコンパイルします。
https://github.com/bb-mint/qrcodereader
> git clone https://github.com/bb-mint/qrcodereader
> cd qrcodereader
> gcc -o qrcodereader qrcodereader.c

```qrcodereader.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>

/* 英語キーボードのマップから
char *key_table_sample[] = {
  "Reserved", "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Minus", "Equal", "Backspace",
              "Tab",    "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Left Brace", "Right Brace", "Enter",
     "Left Control",     "A", "S", "D", "F", "G", "H", "J", "K", "L", "Semicolon", "Apostrophe", "Grave",
     "Left Shift",
          "Backslash",    "Z", "X", "C", "V", "B", "N", "M", "Comma", "Dot", "Slash", "Right Shift",        "Keypad Asterisk",
  "Left Alt", 
  	                                "Space",
  "Caps Lock"
  // 0x00 to 0x3a (00 to 58)
};


char *key_table_shift_off[] = {
  "\0", "\0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\0",
      "\0",    "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n",
      "\0",     "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`",
      "\0", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "\0",        "*",
  "\0", 
  	                                " ",
  "\0"
  // 0x00 to 0x3a (00 to 58)
};

char *key_table_shift_on[] = {
  "\0", "\0", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\0",
      "\0",    "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n",
      "\0",     "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~",
      "\0", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "\0",        "*",
  "\0", 
  	                                " ",
  "\0"
};
*/

//安全性を考慮して使える文字列を絞る　("\0"は出力されない）
char *key_table_shift_off[] = {
  "\0", "\0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\0",
      "\0",    "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n",
      "\0",     "a", "s", "d", "f", "g", "h", "j", "k", "l", "\0", "\0", "\0",
      "\0", "\0", "z", "x", "c", "v", "b", "n", "m", "\0", ".", "/", "\0",        "*",
  "\0", 
  	                                "\0",
  "\0"
};

char *key_table_shift_on[] = {
  "\0", "\0", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "\0",
      "\0",    "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "\n",
      "\0",     "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\0", "~",
      "\0", "\0", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "\0",        "*",
  "\0", 
  	                                "\0",
  "\0"
};

static char **key_table[] = {
	key_table_shift_off,
	key_table_shift_on
};

int main(int argc, char *argv[])
{
  int fd;
  struct input_event events[64];

  int i;

  int shift_on = 0;
  char output_char;

  if (argc <= 1) {
    printf("Usage: command event_device_file");
    exit(EXIT_FAILURE);
  }
  fd = open(argv[1], O_RDONLY);
  if (fd < 0) {
    perror("open error");
    exit(EXIT_FAILURE);
  }

//デバイスの占有
  if(ioctl(fd, EVIOCGRAB, 1))
    printf("Couldn't grab %s. %s.\n", argv[1], strerror(errno));

//入力待ち
  for (;;) {
    size_t read_size;
    
    read_size = read(fd, events, sizeof(events));
    for (i = 0; i < (int)(read_size / sizeof(struct input_event)); i++){

      if (events[i].type == 0x1 && events[i].value == 0x1 && ( events[i].code == 0x2a || events[i].code == 0x36 )) {
      	  //Shift押下状態
      	  shift_on = 1;
      }else if(events[i].type == 0x1 && events[i].value == 0x0 && ( events[i].code == 0x2a || events[i].code == 0x36 )) {
      	  //Shift離した状態
      	  shift_on = 0;
      }else if(events[i].type == 0x1 && events[i].value == 0x1 && events[i].code <= 0x3a) {
         output_char = key_table[shift_on][events[i].code][0];
         //出力可否判定
         if (output_char != '\0'){
             printf("%c", output_char);
         }
         if (output_char == '\n'){
         	 //改行で終了とする
         	 close(fd);
             exit(0);
         }
      }


    }
    
  }
  
  close(fd);
}
```

中華の安いQRコードリーダは、出力が英語キーボード設定になっているようですので
英語キーボードでの入力配列で、エベントを処理して、出力を作っています。
不必要と思われるデータは、今後の処理で困ることもあると思うので、
出力しないように設定しました。

USBで接続すると、QRコードリーダのデバイスは、次のフォルダに表示されます。
> pi@raspberrypi:~ $ ls /dev/input/by-id/*
> /dev/input/by-id/usb-PixArt_USB_Optical_Mouse-event-mouse
> /dev/input/by-id/usb-PixArt_USB_Optical_Mouse-mouse
> /dev/input/by-id/usb-SEM_USB_Keyboard-event-if01
> /dev/input/by-id/usb-SEM_USB_Keyboard-event-kbd
> /dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd

今回の場合、
/dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd
がQRコードリーダですので、次のように使います。

> ./qrcodereader /dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd

待ち受け状態になりますので、適当なQRコード、バーコードを読み込ませます。

> pi@raspberrypi:~/src $ ./qrcodereader /dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd
> 9784861522802

書籍のバーコードを読み込ませてみました。
結果を標準出力して、プログラムは終了します。

これを使って、適当なプログラムにQRコードやバーコードの内容を送り込むプログラムを
作ります。

```適当なループプログラム.sh
#!/bin/bash

while true
do
  ./qrcodereader /dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd | xargs echo "input data :  $@"
done
```

動作確認
> pi@raspberrypi:~ $ ./適当なループプログラム.sh 
> input data :   4987107619013
> input data :   4987107619013
> input data :   4987107619013

とりあえず、ここまで、QRコードリーダを、RaspberryPiにつないで、
とりあえず、データを標準出力してみました。