HF RFIDリーダを使って
スマートシェルフを作ってみよう

演習の概要

RFIDの小売店舗におけるアプリケーションの一つとして、スマートシェルフがあります。このスマートシェルフは、”賢い棚”、つまり、消費者がどのような商品に興味があったのかのデータを取得したり、あるいは、興味のある商品に関する情報を提供するような機能がある商品棚という意味で使用されることが多いようです。

このチュートリアルでは、店舗における複数の棚がスマートシェルフで、それらのデータをDBで管理するというアプリケーションを考えてみましょう。

■ 完成イメージ

完成イメージは、下の図のようなものです。棚にはRFIDのアンテナ、また、商品にはRFタグが貼付されているとして、商品が棚にあるときには、常に商品のタグが読み取られ、商品を消費者が手に取ることで、タグが読み取られなくなり、その情報が送られなくなるというものです。

完成イメージ
図 完成イメージ

今回は、制御PCにおいて、読取機(R/W)を制御し、読み取った結果をDBに送信するアプリケーションの開発を行ってみます。今回は以下の開発環境を用います。

ハードウェア

ハードウェアは次のように接続します。PCとの接続には、USB-シリアルアダプタを使用することになると思いますが、その際に、先にドライバーをインストールすることを忘れないようにしましょう。また、壊れやすいので、R/W部分と、アンテナの接続と取り外しには十分注意しましょう。

RFIDの接続方法
図 RFIDの接続方法

開発環境

RXTX for JavaはJavaでシリアル通信を行う際に使用するライブラリーです。

ただし、64bitのOSで利用する際には、次のリンクからライブラリをダウンロードします。

RXTX for Java 64ビットダウンロードサイト
図 RXTX for Java 64ビットダウンロードサイト

ダウンロードしたzipファイルを解凍し、jarファイルは任意のディレクトリーに、dllファイル2つは、使用するJDKのBinフォルダにコピーします。

処理のフローは次のようなフローとします。

処理フロー
図 処理フロー

ページの先頭へ

(1)画面の作成

制御PCの画面は以下のような画面とします。

画面イメージ
図 画面イメージ

読取開始ボタンを押下するとタグの読取、及び、データの送信を開始し、読取終了ボタンを押下すると終了するというものです。

Javaでの開発ですので、画面はSwingを使用します。ここでは、画面用のクラス(ContFrame.java)を作成し、mainメソッドのあるクラスから、画面インスタンスを作成するという処理にします。また、レイアウトは、GridBagLayoutを使用します。ボタンを大きさを(100,50)、表示領域をJTextAreaとして、サイズを(250、100)とし、自動で改行するように設定します。

また、JTextAreaについては、大量の文字が表示されても見ることができるように、ScrollPaneを用いることとします。画面のサイズは、(400、200)としましょう。

mainメソッドのあるクラスは次のようになります。ライブラリのインポートは適宜実施してください。また、起動時にシリアルポートの番号を入力することとします。

public class SmartShelf {
    public static void main(String[] args) {
        JFrame frame = new ContFrame(args[0]);
        frame.setTitle("RFID Reader Controller");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400,200);
        frame.setVisible(true);
    }
}
          

画面用のクラスは次のようになります。

    ContFrame(String commId){
        // GUI作成
        GridBagLayout layout = new GridBagLayout();
        JPanel panel = new JPanel();
        panel.setLayout(layout);
        GridBagConstraints c = new GridBagConstraints();

        JButton btn1 = new JButton("読取開始");
        btn1.setPreferredSize(new Dimension(100,50));
        c.gridx=0;
        c.gridy=0;
        panel.add(btn1,c);

        JButton btn2 = new JButton("読取終了");
        btn2.setPreferredSize(new Dimension(100,50));
        c.gridx=0;
        c.gridy=1;
        panel.add(btn2,c);

        JTextArea txt = new JTextArea();
        txt.setSize(new Dimension(250,100));
        txt.setLineWrap(true);
        c.gridx=1;
        c.gridy=0;
        c.gridwidth=1;
        c.gridheight=2;
        c.fill=GridBagConstraints.VERTICAL;
        panel.add(new JScrollPane(txt),c);

        getContentPane().add(panel, BorderLayout.CENTER);
    }
          

この状態で実行してみましょう。実行する際に、引数にCOM番号を入れるのを忘れないようにしましょう。

Comm番号の入力
図 Comm番号の入力

さて、以下のような画面が作成されたでしょうか。

画面テスト
図 画面テスト

GridbagLayoutの使い方については、参考書などを参照してください。

ページの先頭へ

(2)ボタン処理の追加

次に、ボタンを押下した際の処理を追加してみましょう。ここでは、標準出力にボタンを押したことを出力するだけとします。実装には、無名クラスを利用します。

        // 開始ボタンを押下した際の処理
        btn1.addActionListener(new  ActionListener(){
            public void actionPerformed(ActionEvent e){
                System.out.println("Start is pressed!");
            }
        });

        // 終了ボタンを押下した際の処理
        btn2.addActionListener(new  ActionListener(){
            public void actionPerformed(ActionEvent e){
                System.out.println("Stop is pressed!");
            }
        });
          

さて、アプリケーションを起動して、標準出力に文字が表示されるのが確認できたでしょうか。

ページの先頭へ

(3)スレッド処理の追加

次に、スレッド処理を追加します。これは、制御アプリケーションが、定期的に読取コマンドを発信するようにR/Wに対して信号を送るためです。ここでは、setVisible()メソッドをオーバーライドして、スレッドを開始する処理を追加します。スレッド処理としては、JTextAreaに番号を表示するという処理を書いてみましょう。

まず、ContFrame.javaにRunnableインターフェースを追加します。

          public class ContFrame extends JFrame implements Runnable{
          

次に、setVisibleメソッドを以下のように追加します。

    // setVisibleメソッドをオーバーライドして、スレッドを開始する処理を追加する
    public void setVisible(boolean b){
        if(b && this.thread==null){
            this.thread=new Thread(this);
            this.thread.start();
        }else if(!b && this.thread!=null){
            this.thread=null;
        }
        super.setVisible(b);
    }
          

次に、スレッド処理を追加します。ここでは、1秒ごとに数字がカウントアップします。

    // スレッド処理
    public void run(){
        while(this.thread!=null){
            cnt++;
            txt.setText(Integer.toString(cnt));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
        }
    }
          

このときに、

  1. JTextAreaをグローバル変数に変更すること
  2. 必要なライブラリーをインポートすること

を忘れないようにしましょう。アプリケーションを実行してみましょう。ちゃんと、カウントアップされたでしょうか。

ページの先頭へ

(4)読取開始と終了処理の追加

今回は、読取開始ボタンを押下した際に読み取りを開始し、終了ボタンを押下した際に読み取りを終了する処理としたいと思います。実装方法はいろいろあると思いますが、今回は、真偽の変数rCmdを定義し、この値が真のときに読み取りコマンドを送信、偽のときに送信をしないという処理とします。まずは、テストのために、開始のときにカウントアップ、終了のときに停止という処理を実行してみましょう。

まず、クラス変数としてrCmdを定義します。

          private boolean rCmd=false;
          

それぞれのボタンを押下した際に、rCmdをtrue、falseにする処理を追加します。そして、スレッド処理のカウントアップの処理を以下に変更します。

            if(rCmd)cnt++;
            txt.setText(Integer.toString(cnt));
          
カウントアップ
図 カウントアップ

ボタンを押すことでカウントアップを制御することはできましたか。

ページの先頭へ

(5)シリアル通信

では、いよいよシリアル通信を使って、R/Wを制御してみましょう。R/Wを制御するには、

  1. ライブラリの追加
  2. シリアル通信の設定
  3. )読取コマンドの送信
  4. 受信データの受け取り

を行う必要があります。

1. ライブラリの追加

Eclipseでは以下のようにすることで外部ライブラリ(jarファイル)を追加できます。

Javaプロジェクト(この場合は、SmartShelf)を右クリック>「ビルド・パス」選択>「ビルド・パスの構成」を選択>インストールしたライブラリーの追加

RXTXライブラリーの追加
図 RXTXライブラリーの追加

2. シリアル通信の設定

今回使用する雪岡製作所のRFIDキットは、以下のような設定で動作します。

        // シリアル通信設定
        try{
            CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(commId);
            SerialPort port = (SerialPort)portId.open("serial", 2000);
            port.setSerialPortParams(19200, SerialPort.DATABITS_8, 
                    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
            // port.enableReceiveTimeout(Integer.MAX_VALUE);
            port.addEventListener(this);
            port.notifyOnDataAvailable(true);
            bufReader = new BufferedReader(new InputStreamReader(port.getInputStream()));
        }catch(Exception ex){
            ex.printStackTrace();
        }
          

このときに、クラス変数として、BufferReader bufReaderを定義しておきます。

          private BufferedReader bufReader;
          

3. 読取コマンドの送信

RFIDキットの読取コマンドは、複数タグを読み取ることができるコマンドを使用します。先ほど、スレッド処理を書いた部分を変更します。このときに、port変数もクラス変数にします。

          private SerialPort port;
          

読取コマンドの送信処理は次のようになります。

    // スレッド処理
    public void run(){
        while(this.thread!=null){
            try {
                OutputStream out = port.getOutputStream();
                String input = "IM:00\r\n"; // 読み取りコマンド
                if(rCmd)out.write(input.getBytes());
                Thread.sleep(1000);
            } catch (Exception e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
        }
    }
          

受信データの受け取り

シリアル通信で受信処理をするためには、SerialPortEvent処理を実装する必要があります。そのためには、まず、ContFrameクラスにSerialPortEventListenerインタフェースを追加します。

          public class ContFrame extends JFrame implements Runnable,SerialPortEventListener{
          

次に、serialEventメソッドを追加します。

    // シリアル通信における受信データの取得
    public void serialEvent(SerialPortEvent event) {
        String buffer = "";
        if(event.getEventType()==SerialPortEvent.DATA_AVAILABLE){
            try{
                while(bufReader.ready()){
                    buffer=bufReader.readLine();
                    txt.setText(buffer);
                }
            }catch(IOException ex){
                ex.printStackTrace();
                System.exit(1);
            }
        }
    }
          

さて、ここまでで、タグのUIDを読み取るコードができたと思います。

UID読み取り結果(タグ1枚)
図 UID読み取り結果(タグ1枚)
UID読み取り結果(複数タグ)
図 UID読み取り結果(複数タグ)

さて、ここまでで、タグのUIDを読み取るコードが完成しました。ここから先は、読み取った結果をDBに格納する処理です。JavaにおけるDB処理は、別のチュートリアルがありますので、それを参考に実施してみてください。

ページの先頭へ