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のまま)。
ロックビットをロックというのがややこしいですねー。