ArduinoでNFCを扱う

th_Arduino-NFC

前回までArduinoで音楽プレーヤーを作っていましたが、今回はちょっと方向性を変えて、ArduinoでNFCをなんやかんやしてみようと思います。

素直に考えたら、NFCシールドを使うのが最も簡単だと思います。

こんなのとか。

こんなのでしょうか。ただ、いずれも日本での入手性があまり良い気はしません。また、シールド全般に言えることですが、他に使いたいシールドがある場合に、利用必須のピンがかぶってしまう可能性があります。

よって、ここでは

これを使いたいと思います。これならスイッチサイエンスさんが取り扱っているので簡単に手に入りますし、

これも一緒に買えば、Arduinoのハードウェアシリアルを使ってNFC通信ができます。またありがたいことに、開発元のSONYさんも、Arduino用のライブラリを公開してくれています。こちらからダウンロード可能です。

 

が。

 

このライブラリにはいくつか落とし穴があります。

まず1つ目。これは些細な問題ですが、このライブラリは2010年の製作以降アップデートされていないので、最近のArduino環境で使いたい場合は、”RCS620S.cpp”の”Wprogram.h”のインクルードを”Arduino.h”に変更してやる必要があります。

//#include "Wprogram.h"
#include "Arduino.h"

 

2つ目。これはしょうがない話ですが、RC-S620SはArduinoとシリアル(Tx/Rx)で通信するので、Arduino Unoとかで利用しようとすると、シリアルコンソールを使ったデバッグはできません(ついでに言うと、RC-S620Sを繋いでいるとプログラムの書き込みもできない)。

デバッグしながらRC-S620Sを使いたい場合は、ハードウェアシリアルを複数持つArduino Megaを使うのが素直だと思います。

ただ、このライブラリはTx0/Rx0を利用するのが前提なので、例えばArduino MegaのTx1/Rx1で利用したい場合は、”RCS620S.cpp”で”Serial”の部分を”Serial1“に変更してやる必要があります。具体的には、以下です。

void RCS620S::writeSerial(
    const uint8_t* data,
    uint16_t len)
{
    //Serial.write(data, len);
    Serial1.write(data, len);
}

int RCS620S::readSerial(
    uint8_t* data,
    uint16_t len)
{
    uint16_t nread = 0;
    unsigned long t0 = millis();

    while (nread < len) {
        if (checkTimeout(t0)) {
            return 0;
        }

        //if (Serial.available() > 0) {
        //    data[nread] = Serial.read();
        if (Serial1.available() > 0) {
            data[nread] = Serial1.read();
            nread++;
        }
    }

    return 1;
}

void RCS620S::flushSerial(void)
{
    //Serial.flush();
    Serial1.flush();
}

ライブラリを利用するプログラム本体の方でも、setup関数内で”Serial1.begin(115200);”を呼ぶことを忘れずに。

これでデバッグしながらRC-S620Sを利用できるようになります。ちなみに、RC-S620Sとの通信をソフトウェアシリアルでやってしまう、ということも不可能じゃないのかもしれませんが、こちらの方の記事を見てみるとなんだかハマリそうなので、素直にArduino Megaを使う方が良いと思います。

 

そして3つ目。これが一番大問題です。

“RC-S620S”は、スイッチサイエンスさんでは”Felica リーダー・ライター”として販売されており、 “NFC リーダー・ライター”として売られているわけではありません。これが意味するところは、「RC-S620SはFelica (= NFC Type F)のリーダー・ライターとして使ってね」ということです。

SONYさんの製品仕様上では、RC-S620SはFelica (Type F)だけではなくてType AにもType Bにも対応していることが明記されています。しかし、提供されているArduinoライブラリはType Fのタグの読み取りにしか対応してくれていません。試しにサンプルプログラムを書き込んでType Aのタグを近づけてみても、うんともすんとも言いません。

タグもFelicaに統一するのならそれはそれで良いのですが、自分の場合はちょっと事情があって、どうしてもType Aのタグを読み取れるようにしたかったのです。

いろいろ情報を探し回った結果、hirokumaさんがType A / Type B / Type Fを読み書き可能なArduino向けRC-S620Sライブラリを作成してくださっていることがわかりました。

これをありがたく利用させていただきます。こちらも通常のシリアルポートを利用することが前提になっているので、Arduino Megaでデバッグしながら利用したい場合は、ライブラリの”devaccess_uart.cpp”で”Serial”になっているところを”Serial1″に変更する必要があります。

ここでは、ライブラリに上記の変更を加えてArduino Megaで使うことを前提に、NFC Type Aのタグを検出してUIDによって処理を分ける、というサンプルプログラムを載せます。UIDはNFCのチップごとに固有とされている情報なので、電子工作で少し試すようなアプリケーションなら、このUIDで処理を分けるだけで大概のことはできると思います。Type AのUIDについては、以下が参考になるかと思います。

以下、ソースコードです。


#include <HkNfcRw.h>
#include <HkNfcA.h>
#include <inttypes.h>
#include <string.h>

uint32_t last_uid    = 0;
uint32_t current_uid = 0;
// Type A MIFARE UltralightのUIDは7byte固定
// (CheckByteを入れたら9byteだが、戻り値には含まれない)
uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0}; 

const uint8_t UID_RESET_COUNT PROGMEM = 2;
uint8_t not_detected_count = 0;

const uint32_t TAG_1 PROGMEM = 2852876548;
const uint32_t TAG_2 PROGMEM = 2987950596;

void setup()
{
  bool ret;

  Serial.begin(115200);

  ret = HkNfcRw::open();
  while (!ret) {}
}

void loop()
{
  HkNfcRw::Type type = HkNfcRw::detect(true, false, false);
  if(type == HkNfcRw::NFC_A) {
    // Type A タグが見つかったとき

    //Serial.println(F("Type A Detect!"));

    if(HkNfcRw::getNfcId(uid)){      

      // for Debug
      /*
      for(int i=0; i<8; i++){
        Serial.println(uid[i]); 
      }
      */

      current_uid = uid[6];
      for(int i=5; i>=0; i--){
        current_uid <<= 8; current_uid |= uid[i];
      }
      Serial.print(F("UID:"));
      Serial.println(current_uid);   

      // UIDが変わった時のみ処理
      if(last_uid != current_uid){
        if(current_uid == TAG_1){
          Serial.println(F("Find TAG 1!"));
        }else if(current_uid == TAG_2){
          Serial.println(F("Find TAG 2!"));
        }        
        last_uid = current_uid; // 現在検出中のUIDを保持
      }

      not_detected_count = 0; // 未検出カウントの初期化
    }else{
      Serial.println(F("Failed to get UID."));
    }
  } else {
    // Type A タグが見つからなかったとき
    // (ライブラリの都合か、タグを見つけた後も検出->未検出->検出->未検出-> ... という動作になる)

    // 未検出カウントが規定回数連続すると、UIDを初期化する。
    // (未検出カウントはタグの検出時とUIDの初期後に初期化される)

    Serial.println(F("No Tag."));

    not_detected_count++;
    if(not_detected_count == UID_RESET_COUNT){
      Serial.println(F("Reset UID."));
      last_uid = 0;
      not_detected_count = 0;
    }
  }

  delay(500);
}

“TAG_1″と”TAG_2″の値は、事前にSerial.printlnで確認した値を記述しています。

次回はこれを使った電子工作の作品を一つご紹介したいと思います。