【BLEを使う】GATT(Generic Attribute Profile)概要
青いぞ、
ゆきは まっさを、
もも、さくらぎに花咲かず、
青いこなゆき、
光る山路に泣きくらす。
青いぞ。
萩原朔太郎「青いゆき」より引用
いままでの記事はこちらをご覧ください。
ここではGATTについて自分のメモ用に調べた結果を書きます。 勉強しながら随時追加修正を行うので、内容の正確さを保証しません。
本記事は、以下の文献およびサイトを参照しています。
http://reinforce-lab.github.io/blog/2013/08/13/blebook-ch2-ble-spec/
GATTの重要性
GATTは、実際にBLE通信でデータを送受信する方法や形式を決める。
異なる会社のBLEもGATTに従って形式を整え、送信されているからこそ、共通にデータのやり取りができる。
だからアプリケーションを作る際には是非ともGATTを理解しておくべきである。
GATTの役割
GATTは装置の通信役割を規定する。
①クライアント
クライアントはサーバにリクエストを送信し、サーバからの応答を受信する。
②サーバ クライアントからの要求を受信すると、応答を返す。
GAPの記事においても言及したように、GAPの役割とGATTの役割は独立している。
GAPがPeripheralであっても、GATTはクライアントにもサーバーにもなれる。
UUID
Universally unique identifier(UUID)は16bytesの識別子で、他のUUIDと重複することのない数字である。
リンク層においてデータpayloadは27bytesまでなので、UUIDの16bytesはそのほとんどを占有してしまう。
また、データが長いと通信時間が増え、消費電力も増えてしまう。
そのためUUIDには短縮した2bytes版と4bytes版がある。
この短縮版はBluetooth SIGで定義されたものだけを使える。また短縮版から16bytesのUUIDを再構築する時は
xxxxxxxx-0000-1000-00805F9B34FB
が使われる。xxxxxxxxの部分に短縮されたUUIDが入る。(2bytesの場合は0000xxxxのxxxxに代入する。)
SIGで定義されていないUUIDを使う時は、短縮版は存在しないので、必ず16bytesを使わないといけない。
例えば自分で新しいサービス、「ボタンを押すとLCDにメッセージが表示される」等を作る場合は、Bluetooth SIGにそのようなサービスに該当するUUIDはないので自分で16bytesのUUIDをUUID generatorを利用して作らないといけない。
Attributeとデータ階層
attributeはGATTにおけるデータ構造の最小単位である。クライアントとサーバのデータ交換は全てこのattributeを用いて行われる。
attributeを複数集めてグループ化したものをサービスと呼ぶ。ここでサービスとは機能を意味する。心拍計サービス、温度計サービスなど、何らかの機能を果たすために、一連のデータ交換のやり取りを一つにまとめたものがサービスである。
サービスはattributeより大きな単位に分類できる。サービスは複数のcharacteristicに分解できる。characteristicは複数のdescriptorが含まれることがある。
(サービスがcharacteristicを含まずにattributeだけから成る場合もある。characteristicがdescriptorを含まずにattributeだけから成る場合もある。)
もちろんcharacteristicやdescriptorも最小単位であるattributeで出来ている。
Attributeの構造
attributeの構成要素を以下に示す。
①Handle
handleとはGATTサーバの全attributeに与えられる、各attributeに固有の16bit識別子である。
handleは0x0001から0xFFFFまでのどれかの値が割り振られ、接続中にその値が変わることはない。
handleの値は、クライアントがアクセスできるattributeの順に従って0x0200→0x0201→0x0202と大きくなっていく。
ただしその値は連続しておらず、0x0202→0x0210と値を飛ばすこともある。
②Type
TypeとはUUIDのことに他ならない。
UUIDにおいて述べたように、サイズは2bytes、4bytes、16bytesのどれかになる。
attributeの値にどのようなデータを格納するのかを定義する。
例えばattributeの値にcharacteristic宣言のデータを格納する場合は、UUID=0x2803、CCCDのデータを格納するならUUID=0x2902となる。
③Permissions(許可)
各attributeに個別に与えられている。ATT operationがattributeに読み書きできるか、どのようなセキュリティになっているかを示している。
Permissionsはmeta-dataなのでクライアントが直接に値を読むことはできない。
valueの読み書き許可について、以下の4つのどれかを設定する。
・NONE:読むことも書くこともできない
・Readable:読み込みのみ可能
・Writable:書込みのみ可能
・Readable and writable:読み書き可能
クライアントがattributeにアクセスする際に、どのようなレベルの暗号が必要かも示す。
・Security mode Lv1:暗号化なし
・Security mode Lv2:このattributeにアクセスするには暗号化された接続が必要。ただし、暗号鍵による認証は必要としない。
・Security mode Lv3:このattributeにアクセスするには認証された鍵で暗号化された接続が必要。
④Value(値)
Typeで定義した種類のデータを格納する。Permissionsの設定がReadableやWritableでさえあれば、クライアントは自由にこの値を読み書きできる。
一方で①~③はクライアントが直接アクセスして読み出しや修正することはできない。
Valueには最大512bytesまで格納でき、整数、文字列、小数点と様々な型のデータが使えて、指定したtypeによってattributeそのものの情報やアプリケーションの実データを持つ。
Service
何らかの意図や目的に関連するattributeを集めてセットにしたのがサービスである。実際に全てのattributeは何らかのサービスに所属しているので、GATTサーバはサービスで出来ているともみなせる。
各サービスの先頭には、サービスの開始を示す1個のattribute、サービス宣言が存在する。サービス宣言のattributeは以下の構造を持つ。
Handle | Type | Permissions | Value | Value の長さ |
---|---|---|---|---|
0x●●●● | 0x2800 or 0x2801 | Read Only | Service UUID | 2,4,or 16 bytes |
Handleの値は、クライアントのアクセス順に従い、その時々に応じた値になる。
TypeはPrimary Service UUID(0x2800)かSecondly Service UUID(0x2801)になる。いずれも2bytesに短縮されたUUIDである。
Secondly Serviceは他のPrimary Serviceのincluded service(後述)としてのみ使えて、外部には公開されず、単独では意味を成さない。主に使われるのはPrimary Serviceの方である。またPrimary Serviceは別のPrimary Serviceのincluded serviceとしても使用できる。
Permissionsは読み込みしかできないように設定されている。
ValueにはService UUIDsを持つ。Service UUIDについてはGAPの解説も参照。
サービスは他のサービスをInclude serviceとして参照できる。別のサービスを含めることで、既存のサービスを拡張した新しいサービスを作成することが出来る。またサービスを使いまわすことで、似たようなサービスが重複することを避けられるのでメモリの節約にもなる。
(より詳しい解説はこちらのサイトのサービスの項目を参照)
Include serviceとして使用したいサービスは1個のattribute、include定義で指定する。include定義のattributeは以下の構造を持つ。
Handle | Type | Permissions | Value | Value の長さ |
---|---|---|---|---|
0x●●●● | 0x2802 | Read Only | Include service handle, end group handle, Included Service UUID | 6,8,or 20 bytes |
Handleの値は、クライアントのアクセス順に従い、その時々に応じた値になる。
TypeはInclude UUID(0x2802)の2bytesに短縮されたUUID。
Permissionsは読み込みしかできないように設定されている。
ValueにはInclude Serviceとして使用したいサービスについて、サービス宣言のhandleの値2byte、そのサービスの最後のattributeのhandleの値2byte、サービスのUUIDの値2bytesもしくは4bytesか16bytesを使う。
characteristicの構造
characteristicはユーザデータを格納するのに用いられる。characteristicは常に最低でも2つのattribute、characteristic宣言とcharacteristic valueを持つ。
また、descriptor(記述子)がcharacteristic valueに続くこともある。
これらの種類のattributeが集まって1つのcharacteristicを構成する。
・characteristic宣言
characteristic宣言のattributeは以下の構造を持つ。
Handle | Type | Permissions | Value | Value の長さ |
---|---|---|---|---|
0x●●●● | 0x2803 | Read Only | Properties, value handle, characteristic UUID | 5,7,or 19 bytes |
Handleの値は、クライアントのアクセス順に従い、その時々に応じた値になる。
Typeはcharacteristicを示すUUID(0x2803)の2bytesに短縮されたUUID。
Permissionsは読み込みしかできないように設定されている。
Valueは以下の構造を持つ
vlaueの項目 | byte数 | 内容 |
---|---|---|
Properties | 1 | このcharacteristicに対して許されている操作 |
value handle | 2 | characteristic valueのhandle |
characteristic UUID | 2,4 or 6 | このcharacteristic固有のUUID |
Propertiesは1byteデータで、以下の種類がある。0x80に設定した場合は、descriptorのCharacteristic Extended Propertiesの設定を用いる。
Property | 値 | 内容 |
---|---|---|
Broadcast | 0x01 | これを設定すると、指定されたcharacteristic valueのデータがadvertisingパケットで送信される |
Read | 0x02 | これを設定するとクライアントからの読み込み可能 |
Write Without Response | 0x04 | これを設定すると、クライアントからの書き込み可能。(サーバからのレスポンスなし) |
Write | 0x08 | これを設定すると、クライアントからの書き込み可能。書き込みリクエストに対して、サーバからのレスポンスが有る。 |
Notify | 0x10 | これを設定すると、サーバがクライアントにcharacteristicの変更を通知できる |
Indicate | 0x20 | これを設定すると、サーバがクライアントにcharacteristicの変更を通知できる。Notifyとの違いは、Indicateはクライアントからの応答も要求することである。 |
Signed Write Command | 0x40 | これを設定すると、クライアントからの署名付き書き込み可能。 |
Extended Properties | 0x80 | これを設定すると、descriptorのCharacteristic Extended Propertiesを使用できる。 |
value handleは実際のデータを持っているcharacteristic value attributeのhandleの値である。
このhandleの値は、必ずしもcharacteristic宣言のhandle値+1の連続した値にはならないことに注意する。
characteristic UUIDはcharacteristic固有のUUIDである。事前に定義された短縮版の2bytes,4bytesと、そうではない16bytesがあるのは他のUUIDと同じである。
・characteristic value
characteristic valueのattributeは以下の構造を持つ。
Handle | Type | Permissions | Value | Value の長さ |
---|---|---|---|---|
0x●●●● | characteristic UUID | 任意 | 実際のデータ値 | 可変 |
Handleの値は、クライアントのアクセス順に従い、その時々に応じた値になる。characteristic宣言のvalueでも使われる。
Typeはcharacteristic宣言のvalue で設定したのと同じcharacteristic UUIDを使う。
Permissionsは状況に応じて任意の設定を使用する。
Valueにはあらゆる種類のデータ、温度、keyコード、文字、速度、心拍などが用いられる。
descriptor
descriptorはcharacteristic valueの後に続き、characteristic valueの追加情報を持つ。
descriptorは1個のattributeで構成されている。
Expanded properties descriptor(拡張プロパティ記述子)
characteristic宣言のpropertiesを0x80に設定することで、この拡張propertiesが使用できる。
名称 | 内容 |
---|---|
Queued Write | これを設定すると、クライアントにQueued Write ATT Operationsの使用を許可する。 |
Writable Auxiliaries | これを設定すると、クライアントからdescriptorへ書き込みができる。 |
CCCD (Client Characteristic Configuration Descriptor)
最もよく使われる重要なdescriptor。
CCCDに1を書き込むとNotifyを許可し、2を書き込むとIndicateを許可する。0を書き込むと、NotifyおよびIndicateの両方を無効にする。
CCCDの設定が適用されるのは、そのCCCDが属しているCharacteristicに対してのみである。別のCharacteristicは、そのCharacteristicが持つCCCDで設定しないといけない。
また、通常、Characteristicの値は接続が切れると失われるが、CCCDの値だけは接続が切れても保持される。
Characteristic User Description descriptor