【NFC】NDEFについて理解する

NDEF(NFC Data Exchange Format) についてまとめる機会があったのでその忘備録として

NDEF とは NFC を介して FeliCa や MIFARE、さらにiOS11にて提供が開始される Core NFC などへデータを読み書きするデータフォーマットを指します。

殆どの場合はライブラリなどを通じてデータの読み書きを行うが NDEF そのものに触れる機会があったので NDEF とはどういう構造なのかをまとめていこうと思います。

公式のスペックはこちら。
http://nfc-forum.org/our-work/specifications-and-application-documents/specifications/nfc-forum-technical-specifications/

# NDEF の構造を理解する

## NDEF Message 編

NDEF は1つの NDEF Message と呼ばれるコンテナに、1～n 個の NDEF Record を含みます。
この NDEF Record 1つに対してURLやテキストなどのデータを記録します。

実装する際はこの NDEF Message を取り扱うか(NDEF Record のみで良い場合があったり…)は実装する環境・状況によってケースバイケースと思いますので、リファレンスなどの記載に従ってください。

NEDF で採用されているデータ構造は TLV 方式です。Tag Length Value の3組で、Tag の部分にデータ型を指定し、Length 部分に Octet 長を、Value に実際の値をいれるというデータ構造となっています。

![NDEF Messageのデータ構造概要.png](https://qiita-image-store.s3.amazonaws.com/0/172469/fa581cbe-bf28-cc72-a122-5e2a8d75c6f3.png)

NFC 対応の IC チップまたは機器(以下"タグ"と表記します)に書き込みを行う場合、NDEF Message の総容量がタグの PAYLOAD 容量を超えないようにします。
例えば、[こちらのタグ](https://www.sanwa.co.jp/product/syohin.asp?code=MM-NFCT)であれば 144Byte 以内に収めなければいけません。

よほど大容量の記録が行えるタグを除き、現実的には1つのタグに1つの NDEF Record で精一杯だと思います。

NDEF Messageのデータ構造は下図のとおりに表される。(NFC Forum Type 2 Tagの場合)
![NDEF Messageのデータ構造.png](https://qiita-image-store.s3.amazonaws.com/0/172469/d453c5e7-4760-cea3-0973-dfa1e1f256da.png)

まず 1バイト目に NDEF であることを示す識別子 0x03 を指定します。続いて 2バイト目に NDEF Record の容量(単位:バイト)を符号なし8bit整数で、そして3バイト目以降に NDEF Record 本体を記録します。

ちなみに、識別子は NFC Forum Type 2 Tag で定義されているものを使用する場合は以下のパターンを使用することができます。

|識別子|名称|説明|
|---|---|---|
|0x00|NULL TLV|NULLです。特になし。メモリ領域の調整用|
|0x01|Lock Control TLV|ロック対象のビットを設定する。|
|0x02|Memory Control TLV|予約されたメモリ領域を識別する。|
|0x03|NDEF Message TLV|NDEF Message のコンテナ|
|0xfd|Proprietary TLV|専有情報にタグを付ける。|
|0xfe|Terminator TLV|終端コード。データの終りを示す。|

## NDEF Record 編

NDEF Record は複数の設定情報を含んだ HEADER 領域と本体データを含んだ PAYLOAD 領域が存在します。

NDEF Record のデータ構造を下図の通りです。青色が HEADER や終端指定コード、オレンジ色が PAYLOAD を示します。

![NDEF Record のデータ構造.png](https://qiita-image-store.s3.amazonaws.com/0/172469/4d13afe7-99f5-294b-968c-ccaaea085da5.png)

各要素の説明がこちら

|要素|説明|
|---|---|
|MB (Message Begin)|NDEF Message の始まりを意味するフラグ|
|ME (Message End)|NDEF Message の終わりを意味するフラグ|
|CF (Chunked Flag)|分割された NDEF Message の一部を示すフラグ|
|SR (Short Flag)|255Byte 以下の Record を意味するフラグ。**有効にすると PAYLOAD LENGTH の枠を 4Byte から 1Byte 変更することができます。**|
|IL (ID Length)|ID が設定されていることを示します。**無効（0指定）にすると ID LENGTH と ID を省略することができます。**|
|TNF (Type Name Format)|TYPE フィールドの種類を示します。3bit で指定します。（コードの内容は後述）|
|TYPE LENGTH|TYPE フィールドの長さを符号なし 8bit 整数で指定します。この値で TYPE フィールドで格納できる容量が決まります。|
|PAYLOAD LENGTH|**前述の SR の値に応じて使用できる桁数が変わります**。PAYLOAD フィールドの長さを符号なしビックエンディアンで指定します。この値でPAYLOADフィールドの容量が決定する。|
|ID LENGTH|**IL の値が 0 の場合は省略されます。** ID フィールドの長さを符号なし8bit整数で指定します。この値で ID フィールドの格納できる容量が決まります。|
|TYPE|TYPE LENGTH に応じて使用できる桁数が変わります。TNF の値によって設定できる内容が変わります。|
|ID|**IL の値が 0 の場合は省略されます。** ID を記録できます。ID LENGTH に応じて使用できる桁数が変わります。|
|PAYLOAD|PAYLOAD LENGTH に応じて使用できる桁数が変わります。TNF と TYPE によって内容が変わります。（後述）|


TNFの設定内容はこちら

|値|説明|
|---|---|
|0x00|Empty|
|0x01|NFC Forum well-known-type|
|0x02|Media-type as define in RFC2046|
|0x03|Absolute URI as define in RFC3986|
|0x04|NFC Forum external type|
|0x05|Unknown|
|0x06|Unchanged|
|0x07|Reserved|

### TNF と PAYLOAD

TNF の値に応じて、TYPE で設定できる内容 と PAYLOAD内のデータ構造が変化します。
今回は NFC Forum well-known-type に準じて一例を挙げてみます。

NFC Forum well-known-type では Type と PAYLOAD に RTD(Record Type Difinition) と呼ばれるものが使用できます。
RTD で使用できる種類で代表的なものはこちら。

|分類|TYPE|
|---|---|
|Text|T|
|URI|U|
|SmartPoster|Sp|

ここで指定した内容に応じて、端末で読み取ったときの挙動を制御することができるようになります。
例えば URI であればかざしたときにブラウザが立ち上がるようになります。

RTD の詳細や使用方法は冒頭で紹介した公式スペックを参照してくださいね。

今回は URI を例に挙げてみます。

### NFC URI Record Type Definition

まず、PAYLOAD 内はこのようなデータ構造となります。

![NDEF Record PAYLOAD のデータ構.png](https://qiita-image-store.s3.amazonaws.com/0/172469/cb44dfac-7c4b-5a3a-1e8a-cf975a5fa27a.png)

各要素の説明がこちら

|要素|説明|
|---|---|
|Identifier code|URI のプロトコル識別子を指定します。識別子とプロトコルの対応表は後述します。|
|URI field|URI 本文を UTF-8 で記録します。Identifier code で指定した分は省略するようにします。|

Identifier code の代表例がこちら。その他のコードは NFC URI Record Type Definition Technical Specification を見てみてください。

|Identifier code|プロトコル|
|---|---|
|0x00|N/A|
|0x01|http://www.|
|0x02|https://www.|
|0x03|http://|
|0x04|https://|
|0x05|tel:|
|0x06|mailto:|

ここで注意すべきことですが、この URI の PAYLOAD は Identifier code で1バイトを使用しています。
つまり、前述で紹介した NDEF Record の PAYLOAD LENGTH には URI field の容量に 1バイトを加えて指定する必要があります。

# NDEF を構築してみる

ここまでの内容を踏まえて、「 http://qiita.com/ 」にアクセスできるNDEFを構築してみましょう。

![NDEF例.png](https://qiita-image-store.s3.amazonaws.com/0/172469/30cac082-9c24-a3ce-8162-ab10fa4e8105.png)
（2020/09/30 追記） 図の 3Byte にて「INF」を表記がありますが正しくは「TNF」です。

※ もし、タグに NDEF Message ごと書き込む場合は末尾に終端コード（0xfe）が必須となります。

この図よりタグに書き込むデータは、

03 0f d1 01
0b 55 03 71
69 69 74 61
2e 63 6f 6d
2f fe

となります。(もしNDEF Recordのみで良い場合は、先頭2バイトと最終1バイトを除けば良い)

# 参考

[避けては通れないバイナリ地獄 - NDEFってなんだろう -](https://www.slideshare.net/tomorrowkey/ndef-13784268)