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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <winscard.h>

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に設定するのがいいようです。

2013年6月17日月曜日

NTAG210のUIDミラーを試してみる

NTAGの新しいシリーズにNTAG210とNTAG212がありますが今回NTAG210を入手しましたので早速試してみました。新シリーズでは新しくUIDミラーという機能が搭載されています。タグのUIDをNDEFメッセージの任意の場所にミラーできるという機能です。

NTAG210とNTAG212ですが仕様書を見るとサイズの違いだけのようです。NTAG210は80byte、NTAG212は164byteです。自由に書込みできる容量だけみると、NTAG210はMifareUltralightと同じ48byte、NTAG212はNTAG203より少し少ない128byteになっています。

こちらがNTAG210のBullsEyeです。


中身を見てみます。


Page3の1バイト目が 0xE1 になってますのでNDEFフォーマットされています。データは 0x03 0x00 0xFE で空のNDEFが確かに入っていますね。
まずここにタグのUIDをパラメータとするURLを書きます。


書きました。UIDの部分は 00 00 00 00 00 00 00 にしています。
ここでタグに細工をするとこのUIDの部分にタグ自身のUIDがコピーされるようになります。
正確には上書きされるのではなくて、タグ読み込み時に置き換えられるようです。
細工はNTAG210の場合Page16に行います。Page16の構造は仕様書によりますと、

  Page 16   MIRROR_BYTE  RFUI  MIRROR_PAGE  AUTH0

となっています。MIRROR_BYTEとMIRROR_PAGEで、何ページの何バイト目にミラーするかを指定します。ですので、UIDをミラーしたい開始のページ(4~15)とバイト(0~3)を書き込むわけです。
MIRROR_PAGEには開始ページをそのまま書けばいいですが、MIRROR_BYTEの方は特殊で上位3ビット目と4ビット目の2ビットを使って0~3を指定するようです。

ではやってみましょう。先ほど書き込んだタグのデータを見てみると、


Page10の2バイト目から 0x30 (ASCIIで数字の0)が続いているので、ここにミラーすればいいようです。というわけで MIRROR_PAGE には 0x0A(Page10)、MIRROR_BYTE には2進数で 0001 0000(16進数で 0x10)を書込みます。
書き込んだあと、もう一度NDEF読み込みをしてみると、


ちゃんとUIDのところにタグのUIDが表示されています!便利ですねー。

ネットと連携させる場合、ほとんどの場合タグのUIDをパラメータとするURLを使用します。そうするとタグごとにエンコードする内容が異なることになります。しかしこの機能を利用すればエンコードする内容はすべてのタグで同じにできるわけですね。なるほど。

2013年6月6日木曜日

Galaxy Nexusの謎

NFCには3つのモード(リーダ/ライタモード・P2Pモード・カードエミュレーションモード)がありますが、カードエミュレーションモードが使用できる端末はあまりなくて、Galaxy Nexusもカードエミュレーションには対応していません。しかしカードエミュレーションに対応してないからといってUIDが無いわけでもありません。
MANICA EXCEL TOOLとACR122リーダを使用してGalaxy NexusをACRリーダにのせてみると、


ほら、IDが入ってきます。ですがACR122の方は激しくピコピコピーピーしています。そのままにしておくと、


あら、どんどん別のIDが入ってきます。何なんでしょうか、これは。

NFCIP-1という通信規格を見てみると、この答えがあるようです。
NFCIP-1ではNFCIDというものが出てきます。これは複数のタグが通信範囲に存在する場合、1枚をどのように特定するかといういわゆるアンチコリジョンを実現するためのIDになります。

で、このNFCIDですが、規格では基本的にランダムということになっています。
同じ通信範囲の特定の1つを選択できればよいわけですから、ランダムでもOKなんですね。
ただ、NTAGやFeliCaなどのチップの場合は独自に設定された自分のUID(IDm)をNFCIDとしているわけです。

具体的にどんな感じなんでしょうか。
NFCIDにはNFCID1、NFCID2、NFCID3の3つ(※NFCIP-1の場合)があるようです。

NFCID1
106kbpsの通信速度で、カスケードレベルというのがあって、レベル1の場合は4byte、レベル2は7byte、レベル3は10byteのNFCIDになります。4byte、7byteということで何となくわかるかと思いますがMIFAREですね。NFC-A相当ということになります。また、レベル1の場合で先頭が 0x08 の場合は残り3バイトはランダムという規定になっているようです。

NFCID2
212kbps、424kbpsの通信速度で、8byteのNFCIDになります。こちらはNFC-F相当です。FeliCaですね。先頭が 0x01 0xFE の場合は残り6バイトはランダムということになっているようです。

NFCID3
P2Pを行う場合に使用される10byteのNFCIDのようです。

では先ほどGalaxy Nexusから取得したUIDを見てみましょう。8byteですね。ということはNFCID2が来てるってことですかね。先頭が確かにすべて 0x01 0xFE になってますので後ろの6バイトはランダムって事なんですね!

なるほどー。UIDは固定って思っちゃってるんで違うIDがバラバラ飛んでくるとウッ?ってなっちゃいますよね。

でもちょっと待ってください。NFCID2がGalaxy Nexusから飛んで来てる?NFCID2?NFCID2ってNFC-Fじゃね?
え?何で?なかよしなの?複雑ですねー。 謎です。

2013年5月31日金曜日

PC/SCに潜む謎

WindowsでNFCなどのICカードを利用するのに便利なPC/SCというインターフェース仕様があります。

通常はリーダライタはそれぞれドライバがあってSDKが提供されたりしてそのリーダ用のプログラムを作成するわけなんですが、PC/SCに対応したリーダライタでPC/SCを利用してプログラミングしてしまえば、他のPC/SC対応リーダに変更してもそのまま使えちゃうわけですね。(まぁ、そんなにうまくはいかないんですが)

こういう共通インターフェース仕様っていうのはぶっ飛び系(なんで?こんだけのことするのに何でこんなにコードがいるわけ?など)が多いわけですが、このPC/SCも結構不思議ちゃんな部分があります。

実際に見てみましょう。ACSさんのACR122リーダを使用します。

PC/SCの実体というかAPIの呼び出し先はwinscard.dllというシステムファイルです。Windowsに含まれてますので、特に何かインストールしなくても普通に使えます。

で、だいたい次のようなAPIの呼び出しをして使います。

 SCardEstablishContext - まあ、これから使うよー的な。
     ↓
 SCardListReaders - これでWindowsが認識しているPC/SCに対応したリーダの一覧を取得します。
     ↓
 SCardGetStatusChange - リーダの状態の変化を察知します。カードが置かれたとかがわかります。
     ↓
 SCardConnect - カードが置かれてるようなら、これでカードと接続(おおげさな)します。
     ↓
 SCardTransmit - カードに対してコマンドを送信します。
     ↓
 SCardDisconnect - カードとの接続を切断(またおおげさな)します。
     ↓
 SCardReleaseContext - 終わったよー的な。

至って普通に見えますね。重要なのは SCardTransmit で、これでAPDU(Application Protocol Data Unit)という形式のバイナリデータを送ることでカードを操作します。たとえば「お前だれー」というときは

 FF CA 00 00 00 (16進数)

といったバイナリ列を送ればカードのIDを返してきます。APDUは定義されたものもあればメーカ独自のものもあります。
先ほどのIDの取得やタグの読み書きなどは定義されていますが、ACR122リーダではブザーを鳴らしたりLEDを点灯したりみたいなこともAPDUを送ることで可能です。

 FF 00 40 50 04 05 05 03 01

なんて送ると、赤のLEDが3回点滅してそれに合わせてピーピーピーと鳴ります。便利ですねー。これを使えばカードをかざしてお前のカード認証されてねーよみたいなときに警告としてピーピー言うこともできそうです。

ところが不都合が1つあります。先ほどのAPDUを送信するAPIなんですが、SCardTransmitなんです。そうです。S「Card」Transmitです。カードなんです。カードに送るんです。
まあ、カードに送るって言っても一旦はリーダに送るんだからいいんじゃね?とも思いますが、実際SCardTransmitの引数はこうなっています。

 SCardTransmit(カードハンドル, 送信パラメータ, 送信データ, 送信データの長さ, 受信パラメータ, 受信用バッファ, 受信用バッファの長さ)

いきなりカードハンドルというのがあります。これは SCardConnect の時に取得できるカードの識別子みたいなもんです。これがないと送信してくれません。0とかにしてもダメです。もちろん送信するときにはカードとリーダが通信できている状態にある必要があります。

要するに、カードが置いてないときにAPDUコマンドは送信できないのです。例えそのAPDUがリーダのブザーを鳴らすとかカードに全く関係のないコマンドであったとしてもです。えー!なんでー?謎。
(注:ACR122リーダではこれを回避?するため DirectTransmit というものがPC/SCとは別に用意されているようです)

※2014/07/04 追記 カードが無くてもコマンド送信は可能でした。お詫びいたします。詳細は新しいエントリー「PC/SCに潜む謎 その2」を参照ください。

2013年5月24日金曜日

WindowsとAndroidのおしゃべり


WindowsでNFCを使うときにはよくこのリーダを使います。


ACSさんのACR122リーダです。
4千円くらいで手頃ですし、PC/SCにも対応していて開発もやりやすいです。

使い方としては社員証のFeliCaなんかをユーザの識別用に使ったりとかします。
あとは大量のNFCタグをDBに登録しなきゃいけないようなときにMANICA EXCEL TOOLと繋いで
タグのIDを片っ端からエクセルに入れて登録したりします。
MANICA EXCEL TOOLではNDEFの書込みもできるようにしてますので、スマートポスターの作成とかもできます。

これだけだとつまんないので、ふとAndoridにBeam飛ばせないのかと思って調べたらありました。
https://code.google.com/p/ismb-snep-java/wiki/SNEP_From_ACR122_To_Phone

こちらはJavaで実装されてますが、まぁ何で実装しようがリーダの制御でBeamが飛ばせるという
ことはわかりました。

Android Beamといっても単にNDEFメッセージをSNEPというプロトコルで送信しているだけのようです。SNEPについてはこちらに資料がありました。
http://www.jortec.neec-fct.org/wp-content/uploads/2013/02/Simple_NDEF_Exchange_Protocol.pdf

 Version(1byte) | Request(1byte) | Length(4byte) | NDEF Data

これでいけそうです。しかしこれだけではなくて、SNEPデータをさらにLLCPというプロトコルに載せないとダメのようです。この部分がまたややこしくてInitiatorになったりTargetに接続したり(いわゆるビームが走るっていう部分ですね)とまぁいろいろあるんですが、先ほどのJavaのソースを参考に同じようにやればできそうなのでとりあえず実装してみました(多謝~)。
MANICA EXCEL TOOLを改造して、NDEF書込みの部分を書き込まずにSNEPで送信するように変更しました。

MANICA EXCEL TOOL(改造版)を起動してACRリーダの設定をします。


B列に送信したいURLを書きます。


で、Galaxy Nexusをリーダに置くと...


おー!URLが表示されました。
ってこれ何かに使えますかね?(笑)

2013年5月23日木曜日

EPCの謎に迫る2


Gen2のUHFパッシブタグのEPCの値は通常は96bit(12byte)なんですが変更も可能です。
EPCメモリバンクの構造は次のようになってます。

 CRC(チェックサム)2byte | PC(Protocol Control) 2byte | EPCの値 12byte(通常は)

EPCの値の前にPCという値があって、じつはこのPCの先頭5bitが後に続くEPCの値の長さになっています。正確にはワード単位の長さです。5bitなので、2進数で(00000)~(11111)までで10進数でいうと0~31までです。1ワードは2byte(16bit)ですから、0byte(0bit)~62byte(496bit)まで指定できることになります。

実際PCの部分をリーダで読むと、大概 30 00(16進数)になっています。リーダのアプリケーションによってはタグ読取時に表示するEPCの値がPCの部分からの表示になっているものもあり、「はじめの3000って何ね~」と言う人もかなりいます。3000は2進数にすると(0011 0000 0000 0000)で先頭5bitは(00110)で10進数で6になり、6ワード=12byteということで96bitになっているわけです。

496bitというと結構な量ですが本当にそんなに長くできるんでしょうか。AlienのHiggs3の仕様をみてみると、「96-EPC Bits, extensible to 480 Bits」って書いてありますので、480bitまでは拡張できるようです。

ということで実験です。
Morphicリーダに付属のアプリケーションではPCの書き換えまではできないので今回はNECのマルチリーダライタを使います。ソフトウェアは当社のMANICA EXCEL TOOL(改造版)です。この改造版はNECマルチリーダライタを使って「セルへ入力」をすると、セルへ現在のEPCの値を入力した後、右隣にあるセルの値をPC含めてEPCバンクに書込みをします。


NECマルチリーダライタです。950MHzの古いタイプです。


MANICA EXCEL TOOLを起動して、NECマルチリーダライタに設定します。

書き込みたい値をセルに入れておきます。480bit(60byte)なので、レングスは30ワードになり、PCの部分は(1111 0000 0000 0000)で、EPCの値は適当に16進数で 11 22 33 44 55 66 77 88 99 00 (これを6回繰り返し。計60byte)にします。ということでセルには


といれておきます。このセルの左のセルにフォーカスがある状態で


Higgs3のタグをリーダに乗せると(ピコッ)


セルに現在のEPCの値が入りました。

で、書き換えできたんでしょうか。Morphicで読んでみると


はみ出してて見えません(笑

実はMorphicもMANICA EXCEL TOOLに接続できます(便利ですね!)。


設定を追加して


Morphic側のMANICA EXCEL TOOLを起動し、読み込んでみると(ピッ)


確かに書込みできています!
こんなに長い値が書込みできるなんて、なんて開発者泣かせの迷惑便利なんでしょうか!
ちなみに496bitも試してみると何と書けました(笑)。仕様は480bitまでとなってるのであんまりやらない方がいいかもしれません。

拡張できることはわかりました。では縮小はどうでしょうか?


1ワード(2byte, 16bit)にしてみます。


できました。
ここで疑問が。いっそのこと0ワードってできるんでしょうか?

やってみます。


できました(笑)。ステルスタグとでも呼びますか。どうやって戻すんだろう(笑)