Wireless・のおと

GATTのはなし

ブログ
技術解説 Bluetooth

GATT は Bluetooth LE における標準的なデータフォーマットです。今まで何度か「SNMP の MIB のようなもの」と言及してきましたが、具体的な解説は避けてきました。今回はこの GATT について具体的に・かつ簡単に解説してみます。

GATT はデータフォーマットとプロトコルの定義である ATT と、ATT を用いて構築されたデータ構造である GATT という階層構造になっています。GATT 自身も「サービス」「グループ」「キャラクタリスティック」などの内部構造が階層をなしていて、必ずしもわかりやすい仕様ではありません。今回はこれをなるべく短く・簡単に解説してみます。

ATT について
ATT(Attribute Protocol) はデータ表現規則とアクセス手順の定義です。大抵のアーキテクチャにおいて、データはデータ本体である「値(Value)」と、その種別を現わす「型(Type)」「名前(Name または Handle Tag, ID など)」で表記されます。例えば SNMP の ASN.1 表記であれば

02 01 01

は整数型(02)で長さ 1 の値 1 (01)を示しています。これだけだと「型」と「値」しかわからないので、SNMP では OID という「名前」を付けて管理します。例えば

30 0D 06 08 43 06 01 02 01 02 01 00 02 01 01

30 は ASN.1 における構造体(SEQUENCE)、0D は構造体データの長さ、06 は OID 型、08 は OID の長さを示し、43 06 01 02 01 02 01 00 は OID 空間における iso.org.dod.internet.mgmt.mib-2.interface.ifNumber.0 という「名前」を示しています。この後に先ほどの 02 01 01 が連なることで、「そのシステムが備えるインターフェースの個数は 1 である」という情報が記されています。

ATT も概念としては同じようなもので、全ての ATT オブジェクトは「型(Type)」「ハンドル(Handle)」「値(Value)」そして「属性(Permission)」を持ちます。ただし実装は ASN.1 や TLV とは大きな違いがあります。
まず、ATT オブジェクトの「ハンドル」は便宜的な識別子に過ぎず、SNMP の OID のように一意性を持つ「名前」ではありません。ハンドルの値は「それが指すデータ」とは直接何の関連も持たず、ただ単に紐づいているだけの関係です。
一方、ATT オブジェクトの「型」はむしろ SNMP における「名前」OID に近いものです。ATT における Type は 128bit の UUID 値であり、「Type UUID=2A00-0000-1000-8000-00805F9B34FB は Device Name」のように定義されています。「整数」「文字列」のような一般型は ATT にはありません。これは ATT/GATT を理解する上で紛らわしいところなので注意してください。
なお ATT の 128bit Type は長すぎるので、Bluetooth SIG が定義した標準型については通常、共通項の上位を省略した「UUID16」として表記されます。上記の Device Name は UUID16 で「2A00」と表記されます。

ATT オブジェクトは ATT プロトコルを用いて読み書きができます。しかし、ここにも SNMP との相違があります。SNMP と異なり、ATT プロトコルには自己記述性がありません。ATT は原則として「ハンドルを指定して値を読み出す(書き込む)」プロトコルであり、そのハンドルに紐づけられている型(Type)や属性(Permission)には直接アクセスできないのです。ATT Read Request で Handle=2 と Handle=3 を読みだした様子を下図に示します。
 

ATT Read Request の例

ATT Read Request の例


ATT Read Response では 02 03 00 00 2A とか 57 65 69 67 68 74 20 4D 65 61 73 75 72 65 6D 65 6E 74 とか、「ただのデータ」しか返ってきません。それが整数なのか文字列なのか、後者は見るからに文字列っぽいですが、文字列だとしても何の名前なのかは全然わかりません。

ATT Read Request ではハンドルに紐付いた型を読み出すことはできませんが、逆に「型に紐づいたハンドル(と値)を読み出す」機能は用意されています。これが ATT Read by Type Request で、これを用いると「指定された型に一致する ATT オブジェクトの中で、指定したハンドル範囲内の一覧をまとめて読み出す」ことが可能です。たとえば UUID16=0x2803 の型に紐づいた値をまとめて読みだすと下記のようになります。

gatt02.jpg

ATT Read by Typeの例


しかし、UUID16=0x2803 が何を意味するのかは ATT の枠内では定義されていません。ここから先は GATT で定義される世界になります。


GATT について
GATT(Generic Attribute Profile) は ATT を用いて記述されたデータ構造体系です。GATT は複数の ATT オブジェクトを組み合わせて一連の意味を実装したもので、Type UUID の意味付けとそれが取るべき値のフォーマットも GATT において定義されます。
GATT は主として「サービス(Service)」と「キャラクタリスティック(Characteristic)」から構成されます。サービスは機能を示す Type UUID で、キャラクタリスティックはサービスを構成する要素です。...うーん、わかりにくいなぁ。
具体例を出すと、「血圧計」という機能は Brood Pressure Profile として UUID16=0x1810 の Service Type を持ち、その構成要素として Blood Pressure Measurement(UUID16=0x2A35), Intermediate Cuff Pressure(UUID16=0x2A36), Blood Pressure Feature(UUID16=0x2A49) などのキャラクタリスティックを持つ、という風になります。

「キャラクタリスティック」は、2つの ATT オブジェクトを用いて1つの意味のある値を既述する要素です。キャラクタリスティック・オブジェクト自身は値を持たず値ハンドル(Value Handle)として他の ATT を参照し(通常はハンドルが連番になる)、他に属性(Properties)、と UUID を持ちます。例えば前記の Handle=3 を改めてキャラクタリスティックとして書き下すと下図のようになります。

キャラクタリスティック構造の例

キャラクタリスティック構造の例(表示)


Handle=2 の ATT オブジェクトが既述子(Characteristic Descriptor)、Handle=3 の ATT オブジェクトが値(Characteristic Value)を持ち、これが2つペアになって1つのキャラクタリスティックを構成しています。先ほど「何の名前なのかは全然わからない」と書いた中身については、Handle=3 の Type が UUID16=0x2A00="Device Name" であることが Handle=2 の Value を読み出すことでわかり、Bluetooth SIG で定義されている GATT 0x2A00 の定義を調べれば Device Name の意味・フォーマットがわかります。あーわかりにくい、まどろっこしい!何で Handle=3 を読んだとき Type=0x2A00, Value="Weight Measurement" が一緒に読み出せるようにしないんだ!と思いますが、とにかくこういう物なので諦めて受け入れてください。

GATT のノードは複数のサービスを持つことができます。サービスには機能を提供する「プライマリサービス」と、他のサービスの下回りとして動く「セカンダリサービス」があり、それぞれ UUID16=0x2800、0x2801 が与えられています。GATT の実装系は1個以上のサービス UUID、そのサービスに含まれる複数のアトリビュートハンドル、そのハンドルに紐づけられた ATT オブジェクト...その多くは2つペアでキャラクタリスティックを構成する、という構造になります。
 

GATT構造の例

GATT構造の例(表示)


ATT/GATT には他にもグループ(Group)の概念があり、同一のサービスに属するアトリビュートは同一グループに包括されます。グループもハンドルから直接読み出すことはできず、ATT の Read by Group Type Request でサービス UUID とハンドル範囲を指定することで、各グループ(≒サービス)の UUID と先頭と最終のハンドル値の一覧が返されます。これは GATT におけるサービス一覧検索として利用されます。

GATTサービス検索の例

GATTサービス検索の例


まとめ
今回は ATT Permission や GATT Characteristic Properties の解説は割愛しました。ATT プロトコルのコマンドも Read Request, Read by Type, Read by Group Type の3つしか解説していませんし、GATT も全体の3割くらいしか解説していません。細部へ踏み込んでゆくとキリがなく、結局 Bluetooth 仕様書を全文書き写すような話になってしまうので、基本動作がわかるようあえて細部を切り捨てました。
ATT/GATT はこのように少し癖のあるデータ構造・プロトコルですが、Bluetooth LE の世界では標準フォーマットであり、多くのプロファイルが GATT ベースで定義されています。Bluetooth Classic 時代にはプロファイルの実装・認証は大仕事であり、新プロファイルを定義して Bluetooth SIG に登録するのは大変な難事だったのですが、それが GATT によって標準化され敷居が大きく下がったこともまた事実です。
この記事が BTLE ATT/GATT への理解が深まる助けになれば幸いです。

関連記事

製品のご購入・サービスカスタマイズ・資料請求など
お気軽にお問い合わせください