2013年10月9日水曜日

Raspberry Pi で NFC

せっかく Raspberry Pi を入手したので、NFCリーダを取り付けてみましょう。リーダの制御PCの替わりになればいろんなところで使えそうです。リーダはいつもの ACR-122U を使います。


まずはネットで情報収集します。あー、やはり Raspberry Pi に ACR-122U を取り付けてる人いますねー。しかしみんな USB給電では足りないらしく、給電用のハブを利用しているようです。そうすると電源が2個になっちゃいますね。あぁ、早くも暗雲が...

何はともあれやってみましょう。Raspberry Pi を標準でセッティングして、とりあえず ACR-122U を USB に挿します。何も起きませんが、dmesg を見ると ACR122U PICC Interface として認識してます。通常ですとここでリーダの赤LEDが点くんですが点きません。
確か初期化コマンド投げないと点かなかったのでまぁよしとしましょう。

で、どうやって ACR-122U を制御するかが問題です。直接USBにアクセスしてもいいんですがあまりお行儀がよくないのでできれば PC/SC を使いたいところです。ということでパッケージを検索してみます。debian のパッケージサイトから pcsc 関連を見てみます。

おー、pcsc-lite ありますねー、しかも armhf 版の acsccid ドライバまであります。何て素敵な世の中なんでしょう!みんなありがとうぉー!
というわけで acsccid ドライバをインストール

pi@raspberrypi ~ $ sudo apt-get install libacsccid1

で、libpcsc-lite1をインストール

pi@raspberrypi ~ $ sudo apt-get install libpcsclite1

あ、これはすでに入ってるみたい。なんで?まぁいいや。
その後 pcscd を入れてみると、

pi@raspberrypi ~ $ sudo apt-get install pcscd

勝手にデーモンが起動し、


赤LED点きました。
給電は大丈夫なのか?タグを置くと一応反応しています。
うわー、こんな簡単でいいのか?後は PC/SC でコーディングすればよしと。PC/SCかぁ・・・。これが一番面倒だな(笑
開発用のパッケージをインストールします。

pi@raspberrypi ~ $ sudo apt-get install libpcsclite-dev

で、テストプログラムをコーディング

sample.c

#include 
#include 
#include 

#include 

int main(int argc, char *argv[])
{
    SCARDCONTEXT hContext;
    SCARDHANDLE hCard;

    LPSTR lpszReaderName = NULL;    
    LONG lResult;    
    DWORD dwActiveProtocol;    
    DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;    
    int i;    
    
    char UID[24];    
    memset(UID, 0, sizeof(UID));    
    
    lResult = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);    
    if (lResult != SCARD_S_SUCCESS) {    
        printf("not service\r\n");    
        return 1;    
    }    
    lResult = SCardListReaders(hContext, NULL, (LPTSTR)&lpszReaderName, &dwAutoAllocate);    
    if (lResult != SCARD_S_SUCCESS) {    
        printf("no reader\r\n");    
        return 1;    
    }    
    printf("%s\r\n", lpszReaderName);    
    
    lResult = SCardConnect(hContext, lpszReaderName, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);    
    if (lResult == SCARD_S_SUCCESS) {    
        SCARD_IO_REQUEST request;    
        request.dwProtocol = dwActiveProtocol;    
        request.cbPciLength = 8;    
        DWORD dwResponseSize;    
        BYTE response[128];    
        BYTE getUID[] = { 0xff, 0xca, 0x00, 0x00, 0x00 };    
    
        dwResponseSize = sizeof(response);    
        lResult = SCardTransmit(hCard, &request, getUID, sizeof(getUID), NULL, response, &dwResponseSize);    
        if (lResult == SCARD_S_SUCCESS && response[dwResponseSize - 2] == 0x90 && response[dwResponseSize - 1] == 0x00) {    
            for (i = 0; i < dwResponseSize - 2; i++)    
                sprintf(UID, "%s%02X", UID, response[i]);    
            printf("%s\r\n", UID);    
        }    
        SCardDisconnect(hCard, SCARD_LEAVE_CARD);    
    }    
    SCardReleaseContext(hContext);    
    
    return 0;    
}

コンパイルして

pi@raspberrypi ~ $ gcc -I/usr/include/PCSC -lpcsclite -o sample sample.c

実行すると

pi@raspberrypi ~ $ ./sample
ACS ACR122U 00 00
04A57EE2613380

おー、読めてるー。ここまでくればあとは大丈夫そうですねー。

2013年10月8日火曜日

NTAG21x のパスワード機能を試す

NATG21xシリーズにはパスワードプロテクションの機能が搭載されています。
単純に書込みのパスワード制限がかかるだけかなーと思って仕様書を見ると読取にもかけられるようです。というわけで試してみます。試すタグは前回登場したNTAG210です。

タグの最後の方の領域に設定情報が書き込まれています。


Page16からPage19の部分です。UIDミラーのときにも少し触れましたが、この部分の構造は以下のようになっています。

Page 16  MIRROR_BYTE  RFUI  MIRROR_PAGE  AUTH0
Page 17  ACCESS  RFUI  RFUI  RFUI
Page 18  PWD(4byte)
Page 19  PACK(2byte)  RFUI  RFUI

この中でパスワードに関連するパラメータは AUTH0、ACCESS、PWD、PACKの4つです。
さらにACCESSがビットごとに設定が決まっていて、PROT、CFGLCK、AUTHLIMの3つの
設定が含まれています。たくさんありますねー。それぞれどんな設定なんでしょうか。

AUTH0
 ページ番号を設定するようです。ここで設定した値以上のページに対してプロテクションがかかるようです。初期値はFFになっていますので、ここを書き換えないとプロテクションが有効になりません。

PWD
 パスワードの実体ですね。4バイトです。初期値は FF FF FF FF のようです。

PACK
 よくわかんないんですが、パスワード認証に成功するとここの値が返り値として戻ってきます。

PWD、PACKの2つは、リードコマンドで読み込もうとしても常にオール00が返ってきます。

PROT
 プロテクションの種別です。1ビットの情報でここが0だと書込みが制限され、1だと読み込みと書込みが制限されます。

CFGLCK
 設定領域のロックフラグのようです。1ビットでここが1になると設定情報が永久に変更できなくなってしまいます。

AUTHLIM
 認証の試行回数のようです。この回数を超えて認証に失敗すると何か起きるようです。

まずは認証コマンドを試します。認証コマンドは 0x1B で、続けてパスワードの4ケタを送信します。
初期値のパスワード FF FF FF FF で投げてみます。


00 00 が返ってきました。ここがPACKの値らしいです。
今度はPACKの値を 01 02 に書き換えてから同じように認証コマンド投げてみます。


確かにPACKの値が返ってきてるようです。
じゃパスワードを別の値にしてみます。


「Tag was lost.」というエラーになりました。まぁ、しっくりいきませんが認証に失敗してるってことらしいです。
では AUTH0 に4を書き込みます。こうすることでユーザ領域の4ページ以降は書込み不可状態になります。


実際に書き込もうとするとエラーになりました。
さらに PROT を1にしてみます。これで読み込みも制限されます。試しにTagInfoで読んでみたらアプリ落ちました(笑
Readコマンドを投げると


00が返ってきました。通常は4ページ分のデータ(16バイト)が返ってくるはずなんですが、コマンド自体はエラーにならずに 00 の1バイトだけ返ってきました。あぁー、プログラマー泣かせですなー。
認証後にReadコマンドを投げると、


きちんと読めてます。

さて、ここで疑問が。AUTH0 に 0 を書いたらどうなるんでしょうか。Page 0 と 1 はタグのUIDが入っています。とするとUIDまで読めなくなってしまうのか!?

というわけでやってみたんですが、UIDは読めました。しかし認証が成功しないようになってしまいました(理由は不明)。というわけで貴重な NTAG210 タグがまた1つ UID しか読めないタグになってしまいました。あーあ、たっちなうには使えるかなー。

2013年10月1日火曜日

UHFは本当に水に弱いのか?

UHF帯のタグを使用する場合、金属に貼りつけると読めなくなるのはHF帯と同じですが、水分があると電波を吸収されてしまうため水分にも弱い特性があります。

じゃーどのくらい弱いんでしょうか。弱いとか言いながら実は結構読めたりするんじゃないでしょうか。ということで試してみました。

用意したのはタッパーです。
これに通常のシールタグ(Alien Squiglette Higgs4)を使います。


タッパーの底にタグを貼ります。


いつものようにMorphicのリーダで読んでみます。
水無しの状態では結構飛びます。


これくらい離れてもまだまだ読んでます。250mWですが十分な距離が出ています。
ここでタッパーにタグが被るくらいの少し水を入れます。


早速読んでみましょう。


これぐらいで限界。やはり弱かった!
下から読むとこんな感じ。


定説通り水に弱いことがわかりました。疑った自分が悔やまれます。まあ、読めただけいいのかもしれませんが(何が?)。

これで終わりは面白くないのでもっと水入れてみます。


これは読めないでしょう。はっはっは。


ピ、ピ、ピ、あれ?

読んでるー。

下からはこんな感じ。


あんまり水の量では変わんないですねー。不思議ー。

2013年8月15日木曜日

Windows8でNFCを使う

Windows8ではNFCがサポートされていると言いますが、実際どんな事ができるのでしょうか。NFC R/Wを搭載したWindows8のPC(VAIO Pro 11)で調べてみました。

このPCではタッチパッドの所にR/Wがあります。
まずはNFCタグ(NTAG)を置いてみます。



・・・何も反応ありません。
(でも実はこの時NFCタグが置かれた事をWindowsは分かっているようです。後述します)

次はNexusを、Webブラウザで適当なページを開いた状態で置いてみます。



Nexusで例のNFC音が鳴って、画面に変化が出ました!Androidビームを送信できるようです。タップしてみます。



Windows8の画面に通知が出ました。通知をクリックすると、IEが起動してNexusで表示していたページが開きます。Android端末同士でURLをビームしたときの動作と似ています。

逆はできるのでしょうか。
Windows8でIEで適当なページを表示した状態でチャーム(Windowsキー+Cで表示されるメニュー)を開いて「デバイス」を選択すると「タップして送信」というアイコンがありますのでクリックしてみます。



何となく予想していた通り、Nexusでブラウザが開いてIEと同じページが表示されました。
URLのAndroidビームを受信したときの動作です。

-----

Androidビームというのは別の記事にあるように、NDEFメッセージのやりとりらしいです。
これまでの動きを見ると、Windows8でNDEFメッセージを送受信できるらしいという事がわかりました。では、自分で作成するアプリケーションで送受信したい場合はどうしたらいいのでしょうか?

Windows8ではWinRTに含まれるAPIを使用します。Windows.winmdというDLLのラッパー(?良くわかりません)に含まれています。
このWindows.winmdはVisualStudio Express 2012でWindowsストアアプリを作成すると標準で参照されているのですが、デスクトップアプリを作成した場合は参照されていません。なのでストアアプリ用かなと思ったのですが、デスクトップアプリを作る場合でも手動で参照に追加してやれば普通に使えるようです。(サポート内なのかは良くわかりません)

とりあえず試すだけなので、作り慣れたデスクトップアプリを使うことにします。
Button1とButton2という2つのボタンだけがあるWindowsフォームアプリケーションを作成したと思ってください。機能はButton1をクリックするとURLのNDEFメッセージを送信し、Button2をクリックすると、URLのNDEFメッセージを受信したときにメッセージボックスが開くだけです。

NFC用のクラスは名前空間Windows.Networking.Proximityにまとまっているようです。
ProximityDeviceというのがメインとなるクラスのようで、最初にこのクラスを取得しておきます。

private ProximityDevice proximityDevice;
private void Form1_Load(object sender, EventArgs e)
{
    proximityDevice = ProximityDevice.GetDefault();
}

NDEFメッセージを送信する場合はPublish○○○Messageというメソッドのどれかを使用します。
URIを送る場合はPublishUriMessageを使うのが簡単そうです。

private long publishUriId = -1;
private void button1_Click(object sender, EventArgs e)
{
    //一度にPublishするメッセージは1つにするのが作法のようなので
    //既にPublishしてたらまずはStopしておく
    if (publishUriId != -1)
        proximityDevice.StopPublishingMessage(publishUriId);
   
    //http://www.yahoo.co.jp をPublishする
    publishUriId = proximityDevice.PublishUriMessage(new Uri("http://www.yahoo.co.jp"));
}
NexusをR/Wに置いた状態でButton1をクリックすると、Nexusでブラウザが起動してYahooが開きました。送信できました!
ちなみに試していませんがPublishBinaryMessageメソッドでタグに書き込みできるようです。

次にNDEFメッセージの受信です。この場合はSubscribeForMessageメソッドを使用します。
1番目の引数には受信するメッセージの型を、2番目の引数には受信したときに呼び出されるハンドラーを渡します。

private void button2_Click(object sender, EventArgs e)
{
    proximityDevice.SubscribeForMessage("WindowsUri", messageReceived);
}
private void messageReceived(ProximityDevice device, ProximityMessage message)
{
    //なぜかURLの間に\0が挟まってるのでトリムする
    MessageBox.Show(message.DataAsString.Replace("\0", ""));
}
Button2を押してから、先程と同じようにNexusで適当なWebページのURIをビームすると、Windows8でメッセージボックスが開いてURIが表示されます。受信もできました。

-----

冒頭でやったように、タグをポンと置いてもWindowsは(一見)無反応でしたが、APIを使えば検知できます。
ProximityDeviceにはDeviceArrived、DeviceDepartedというイベントが定義されています。
ほかのデバイスがR/Wの範囲に入ったとき、または範囲から離れたときに発生するイベントです。

private ProximityDevice proximityDevice;
private void Form1_Load(object sender, EventArgs e)
{
    proximityDevice = ProximityDevice.GetDefault();
    proximityDevice.DeviceArrived += proximityDevice_DeviceArrived;
}
void proximityDevice_DeviceArrived(ProximityDevice sender)
{
    MessageBox.Show(sender.DeviceId);
}
タグをR/Wに置くとメッセージボックスが表示されます。
ちなみにDeviceIdの値は \\?\acpi#nxp5442#xxxxxxxx#{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} みたいな値で、どのタグでも同じ値になります。Windows側のデバイスIDという事でしょうか。
タグのUIDをとれるかと思ったのですが無理なようです。残念でした。

※補足
ここで使っているタグは何も書き込みされていないタグでした。NDEFメッセージを書き込んだタグをR/Wに置くと反応がありました。URLを書き込んでおけば、Nexusでビームしたときと同じくIEでそのページが開きました。


2013年8月5日月曜日

NTAGのロック機能を試す

Mifare Ultralightもそうですが、NTAGにはロック機能が付いています。タグは書き換えができるのが便利なのですが、スマートポスターなど勝手に書き換えられたら困るものもありますので、書き換えができないようにロックをするわけです。

ロックの情報は、タグの3ページ目(Page02)にあります。仕様書によると、3ページ目の後ろ2バイトがロック情報になっています。
2バイトの詳細は次のようになっています。

 1バイト目
  bit7 : LOCK PAGE 7
  bit6 : LOCK PAGE 6
  bit5 : LOCK PAGE 5
  bit4 : LOCK PAGE 4
  bit3 : LOCK PAGE 3
  bit2 : BLOCK LOCKING LOCKBITS 15 - 10
  bit1 : BLOCK LOCKING LOCKBITS 9 - 4
  bit0 : BLOCK LOCKING LOCKBIT 3

 2バイト目
  bit7 : LOCK PAGE 15
  bit6 : LOCK PAGE 14
  bit5 : LOCK PAGE 13
  bit4 : LOCK PAGE 12
  bit3 : LOCK PAGE 11
  bit2 : LOCK PAGE 10
  bit1 : LOCK PAGE 9
  bit0 : LOCK PAGE 8

これを見ると解る通り、ページごとにロックができます。ロックしたいページのビットを1にすると、そのページは読取専用になるわけです。
このロック情報は一度1にすると0には戻せないようになっているようです。

ではやってみましょう。NTAG203を利用します。AndroidアプリのTagInfoを使って中身を確認します。


Page02 の後ろ2バイトを見ると 0x00 0x00 になっています。ロックされてない状態です。
試しに Page07 と Page06 のロックビットを1に書き換えてみます。


Page07 と Page06 の部分に x マークが付きました。Page07 と Page06 はもう書込みができなくなっています。ロック情報をリセットできないか試してみましたが、ロックビットを0に戻すことはできませんでした。

もうひとつ、ブロックロックというのがありますね。これ何でしょうか。試しに 15 - 10 のブロックロックビットを1にしてみます。


今度はページの部分に + マークが付きました。これってロックされてるんでしょうか。ということで試しに Page10 のデータを書きかえてみます。


あら?書けました。よく見ると BLOCK LOCKING LOCKBITS になってますので、ロックビットをロックするということらしいです。現在は 15 - 10 のページはロック無しになっていますが、これをロック状態にできない(ロックビットを変更できない)ということです。
試しに Page15 と Page14 のロックビットを1に書いてみましたが(ロックバイトの2バイト目を 0xC0 へ)、書き換えできませんでした(0x00のまま)。
ロックビットをロックというのがややこしいですねー。

2013年7月5日金曜日

AARメッセージをゲットする

NFCの便利なところは何と言ってもタッチだけでいろんな動作をさせることができるところではないでしょうか。これの肝となるAndroidの機能が、タグディスパッチシステムというものになります。
Androidデバイスは画面がロック状態でなければ常にNFCタグを探しています。そして見つかるとすばやく中身を見てNDEFかどうか判断したりして、それからそれを必要とするアプリケーションへ通知します。この部分がタグディスパッチシステムになります。

で、どのアプリが必要としているかはアプリのマニフェストファイルに記述した内容で判断されます。
「こういうタグがきたら俺を呼んでくれ」的なことをアプリのマニフェストファイルに書いておくわけですね。

マニフェストに指定する場合、まずは大きな分類として「NDEF_DISCOVERD」、「TECH_DISCOVERD」、「TAG_DISCOVERD」の3種類の指定方法があります。タグがNDEFだった場合、まずはNDEF_DISCOVERDをくれと設定してあるアプリから検索され、見つかればそのアプリを起動します。複数あった場合は、例の選択画面が出てきます。それが無かった場合、今度はTECH_DISCOVERDをくれと設定してあるアプリを探します。それでも無い場合はTAG_DISCOVERDをくれのアプリを探します。

また、NDEF_DISCOVERDとTECH_DISCOVERDはさらに細かい設定ができます。NDEFの場合はスマートポスターがきたらとか、特定のメッセージに反応するように指定できます。TECH_DISCOVERDの方は、反応したいタグの種類(タイプAとかBとかFeliCaとか)を指定できます。

例えば、

<intent-filter>  
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimetype="text/plain" />
</intent-filter>

のように記述しておくと、テキストのNDEFが書かれているタグがきたら教えてくれるようになるわけですね。で、アプリ側ではonCreateあたりでgetIntentをすれば、NDEF_DISCOVERDの情報を含んだインテントがゲットできるわけです。

話は変わってNDEFなんですが、Android4.0からAAR(あんどろいど あぷりけーしょん れこーど)というのが追加されています。これはアプリケーションを一意に選択できるアプリのパッケージ名をデータとするレコードで、これがNDEFメッセージに含まれているとそのアプリケーションを起動してくれるという便利な機能です。さらには端末にそのアプリケーションがインストールされてない場合はGoogle Playの該当アプリのダウンロードページに飛ばしてくれるという至れり尽くせりの機能です。

ということでここからこれらの話がつながってくるんですが、AARを書き込んで起動してくれるようにしておいて、さらに起動時にゲットしたインテントからタグのUIDを取ろうとしたんですが、これがうまく行きませんでした。

起動してくれるんやからマニフェストの指定はいらんやろ~ということでまずは記述しなかったんですが、それだと起動時に取得できるインテントにタグ情報が含まれていませんでした。よってUIDの取得ができません。

じゃあTECH_DISCOVERDでも入れるか~ということで入れてもダメ。まあ、AARといってもNDEFですから、タグディスパッチシステムはNDEFとして認識してるはず、よっしゃもうこうなったらNDEF_DISCOVERD入れたろ~ということで入れてもダメ。
NDEF_DISCOVERDの詳細設定が必要なのか?でもAARって何?NFCフォーラムで規定されてるもんじゃないよね?アンドロイドってネーミングに入ってるし。
困ったときのNFC TagInfoということでAARを書き込んだタグを見てみると、


おー、えくすたーなる?そういえばそういうのあったな。ということでドキュメントを見ると、エクスターナルの場合は

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="vnd.android.nfc"
        android:host="ext"
        android:pathPrefix="/example.com:externalType"/>
</intent-filter>

こんな風に指定せいと書いてありました。じゃあ、この「example.com:externalType」の部分を「android.com:pkg」にすればいいのかな?ということでこんな風に変更してみると。

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="vnd.android.nfc"
        android:host="ext"
        android:pathPrefix="/android.com:pkg"/>
</intent-filter>

うまく行きましたー。ちゃんと1回の読み取りで、AARから起動→アプリでUIDを取得までできるようになりましたー。だから何とかやめてー。

2013年6月18日火曜日

インベントリのパラメータを試す1

UHF帯のRFIDタグを読み取りする場合、リーダでインベントリというのを行います。インベントリを行うと、たくさんタグがあってもアンチコリジョンとかうまくやってくれて、こんなタグがあったよーというのを通知してくれます。そのインベントリですが、動作を設定するパラメータがわんさかあります。
今回はその中で Session というのを試してみます。

Gen2のタグには、インベントリ済みフラグというのが内蔵されています。リーダの読み取り範囲にたくさんタグがある場合、すべてのタグが同じように返答していたらこれはなかなかすべてを聞き分けるのは難しいです。そこで、自分はもうインベントリされたよというフラグをタグが持っていて、インベントリ済みの場合はもう黙っておくということになっています。そうすることでインベントリが済んでいないタグだけが返答するようになります。

で、このフラグなんですが、インベントリされたらフラグを ON にするんですが、こんどは OFF にするタイミングはどうするのさ?という問題が出てきます。リーダから「ほうらお前らインベントリフラグを OFF にするんだ」的なコマンドを出すのが1つのやり方です。それだけだとずっと ON の状態のままのタグも出てきてしまいますので、一定時間過ぎたら OFF にするという仕組みにもなっています。で、一定時間って何よ。どんだけよ。という話になるんですが、これが Session の設定になります。

SessionにはS0、S1、S2、S3と4種類あって、S0は速攻で OFF にします。正確にはリーダからの電波が途切れてチップへの給電が無くなったら OFF になります。S1は 500msくらい ON の状態を保持します。S2とS3は違いがよくわからないんですが、どちらも5秒ほど ON の状態を保持します。

S0だと、すぐにフラグがリセットされるのでタグがやいのやいの反応し、タグがたくさんある場合に混線が生じます。S2とかにすると急におとなしくなって、短時間に何度も読み取りしようとすると反応しない場合があるわけです。

試してみましょう。いつものMorphicです。


設定の右下に「Session」がありますねー。
まずは0でタグの読み取りをします。
タグは適当に15個ほど置きました。


読み取りを開始するとピーピーピーピーひっきりなしに音がしています。
しばらく読み取ってから統計を見てみると、


平均して53個/秒で読み取りしたようです。
では Session を3にしてもう一回読み取ります。
今度は何回かピーといった後、全然音がしなくなりました。読み取ってない(タグが黙ってる)ようです。
しばらく読み取ってから統計を見てみると、


平均で2個/秒になってしまいました。
というわけで Session の効果はかなり違いが出るようです。
大量にタグがあって、1回読めばもういいや―的な場合は3、タグは少量で高速で読み取りたい場合は0に設定するのがいいようです。