Bluetooth Low Energyのデータ通信プロトコルの基本(★)
[BLE(Bluetooth Low Energy)の開発依頼は、フィールドデザインまでお気軽にお問い合わせください。]
接続処理が完了すると、マスター、スレーブともにデータチャネルに移ります。どちらかがサーバになり、もう一方がクライアントになります。通常は、アドバータイズをしている側(スレーブ側)が、サーバになります。そのサーバに対して、スマートフォンなどのクライアントがアクセスに行くことになります(1つの機器で、サーバの役割、クライアントの役割とも持つことが可能です)。
下記のようなプロトコルスタックになっています。正確にはGAPからLinkLayerにアクセスするケース(スキャン時、接続時)など例外的なパスはありますが、おおよそこの図で説明できます。
なお、上記のプロトコルスタック図は、サーバ側、クライアント側の両方で共通です。
LinkLayerは、LLと略すこともあります。HCIは、Host Control Interfaceの略ですが、昔はこのレイヤでHost CPUとBluetoothデバイスがUART等で接続され制御していたのですが、現在はすべてSingle Chipで処理されていますので、このレイヤはなくなっています。
ATTは、Attribute Protocolの略です。GATTは、Generic Attribute Profileの略です。GAPは、Generic Access Profileの略です。これらの詳しい説明は、以降で行います。LayerとかProfileとかProtocolとかいろいろな名称が使われてますが、通信ですので中身はプロトコル(約束事)です。
属性(Attribute)
サーバには属性(Attribute)というデータの箱があり、それにクライアント側からアクセスします。この属性のやり取りをするプロトコルをATT(Attribute)プロトコルと呼んでいます。さらに、この属性(Attribute)を複数組み合わせて、GATTデータベースというものを作ります。そのGATTデータベースにアクセスするには、ATTプロトコルを組み合わせて使います。
属性(Attribute)は、下記の集まりです。これらはサーバ内部で保存しておく値ですので、どのような形で保存しておいてもかまいません。
- Attributeハンドル(2Byte)
- Attributeタイプ(2 or 16Byte)
- Attribute値(0-512Byte)
- Attributeパーミッション
Attributeハンドルは、通し番号です。Attributeタイプは、UUIDという識別子で、その値によってサービス名(Service)を示したり、キャラクタリスティック(Characteristic)と言われるものを示したりします(なお、ServiceとかCharacteristicというのは、GATTでの概念で、ATTでは定義されていません)。Attribute値は、アプリケーションレイヤが利用するデータが入ります。Attributeパーミッションは、読み書きできるとか、読み込みのみとか、暗号化ありとかの属性です。この属性(Attribute)が、いくつも集まりGATTデータベースとなっています。
ATTプロトコル
属性(Attribute)にアクセスするATTプロトコルで使われるパケットフォーマットです。このパケットを使って、サーバとクライアントが通信をします。
Attribute |
Attribute |
Authentication |
Read Request, Write Request, Handle Value Notification, Handle Value Indicationなどのハンドルを操作する場合は、上記Attribute Parametersに、Handle番号が入ります。
ATTプロトコルでは主に下記の操作ができます。カッコの中は、実際のAttribute OpCode値です。
- Error Handling
- Error Response(0x01)
- Server Configuration
- Exchange MTU Request(0x02)
- Exchange MTU Response(0x03)
- Find Information
- Find Information Request(0x04)
- Find Information Response(0x05)
- Find By Type Value Request(0x06)
- Find By Type Value Response(0x07)
- Reading Attributes
- Read By Type Request(0x08)
- Read By Type Response(0x09)
- Read Request(0x0A)
- Read Response(0x0B)
- Read Blob Request(0x0C)
- Read Blob Response(0x0D)
- Read Multiple Request(0x0E)
- Read Multiple Response(0x0F)
- Read by Group Type Request(0x10)
- Read by Group Type Response(0x11)
- Writing Attributes
- Write Request(0x12)
- Write Response(0x13)
- Write command(0x14)
- Prepare Write Request(0x16)
- Prepare Write Response(0x17)
- Execute Write Request(0x18)
- Execute Write Response(0x19)
- Signed Write command(0xD2)
- Server Initiated
- Handle Value Notification(0x1B)
- Handle Value Indication(0x1D)
- Handle Value Confirmation(0x1E)
例えば、Reading Attributesの中のコマンドの1つで、クライアントがサーバのデータを読む場合は、Read Request(Attribute OpCode:0x0A)というのをクライアントからサーバに送り、サーバがRead Response(Attribute OpCode:0x0B)というのを返してきます。
このATTプロトコルは低レベルのプロトコルなので、温度計の値を読みこむとかの操作はできません。センサーの値を読むときなどは、一つ上のレイヤのGATTの処理(procedure)が必要になります。
GATTデータベース
属性(Attribute)を集めたGATTデータベースですが、イメージがしづらいので、実際のセンサーの値を格納したデータベースで説明します。Bluetooth Low Energy規格では、この辺が非常に理解しずらいです。理解しづらいのは、サービスの宣言までが、1つの属性(Attribute)に割り当てられてしまっているからです。
通信の世界ではなく、MySQL等のデータベースの世界になっています。
具体的な温度計のGATTデータベースは下記のようになります。1行ずつは、属性(Attribute)になってます。
Handle | Type | Value | Permission |
0x0023 |
0x2800 |
0xAA00 | Read |
0x0024 |
0x2803 |
0x12(プロパティ), 0x0025(データのHandle値), 0xAA01(データのUUID値) | Read |
0x0025 |
0xAA01 |
0x00, 0x00, 0x00, 0x00 (=温度値) | Read |
0x0026 |
0x2902 |
0x00, 0x00 | Read/Write |
0x0027 |
0x2901 |
“Temp Data” | Read |
0x0028 |
0x2803 |
0x0A, 0x0029(データのHandle値), 0xAA02(データのUUID値) | Read |
0x0029 |
0xAA02 |
0x01 | Read/Write |
0x002A |
0x2901 |
“Temp Conf” | Read |
実際は、サービスは、キャラクタリスティックの集まりとして定義されています。
さらに、キャラクタリスティックは、Characteristic Declaration、Characteristic Value、Characteristic Descriptorの集まりとして定義されます。
そのため、上記は、下記のような構造になります。
サービス |
|
キャラクタリスティック1 |
Characteristic Declaration |
Characteristic Value |
|
Characteristic Descriptor |
|
Characteristic Descriptor |
|
キャラクタリスティック2 |
Characteristic Declaration |
Characteristic Value |
|
Characteristic Descriptor |
GATT処理(procedure)
GATT処理(procedure)は、MTUを変更するものや、GATTデータベースに対してアクセスするものなど、いくつかのATTプロトコルの組み合わせで行います。
よく使われる処理には下記のようなものがあります。
- Discover Characteristic:クライアントがCharacteristicを探す(ATTのRead By Type Request/Responseを使う)
- Read Characteristic Value:クライアントがCharacteristic値を読む(ATTのRead Request/Responseを使う)
- Write Characteristic Value:クライアントがCharacteristic値を書く(ATTのWrite Commandを使う)
- Notify Characteristic Value:サーバがCharacteristic値を通知する(ATTのHandle Value Notificationを使う)
長さについて
Bluetooth Low Energyには、いろいろな長さがあり、混乱することがあります。下記ではそれらを説明します。
LinkLayer
物理レイヤであるLinkLayerでの最大データ長は、ver4.1までは27byteで、ver5.2からは251byteとなっています。
デフォルトでは、27byteなので、Data Length Extension(DLE)と言われるLL_LENGTH_REQ, LL_LENGTH_RSPの交換処理によって、セントラルとペリフェラル両者で最適な値を決定します。
L2CAP
L2CAPでは、長さフィールが2byteであるため、0から65535までの長さを取ることが可能です。ただし、Bluetooth Low Energyでは、上位レイヤにATT,GATTを使うことから、最大長はATTの最大長になってしまいます。
物理レイヤが27byteである時に、L2CAPでデータ100byteを送る場合は、複数に分割して送ることになります。
ATT,GATT
GATTのExchange MTU Request/Responseによって、セントラルとペリフェラル両者で最適なATTプロトコルでの最大長(ATT_MTU)を決定します。ATT_MTUの最小値は23です。最大値は定義されていません。ただし、L2CAPで送ることを考えると、65535-4以下であることは間違いありません。また、Attribute値の長さは512byte以下であることが仕様で決まっているので、ATT_MTUを515以上にしておけば、どのようなAttribute値でも転送可能です。
転送スピードを出したい時は、物理レイヤの長さが251byteの場合、L2CAPの4byteを除いた247byteにATT_MTUを設定し、フレームが分割されることのないようにします。
また、下記のように、GATTの操作によって、送れるAttribute値のデータ長は変わってきます。なお、characteristicの長さはAttribute値を使うので、最大で512byteということになります。
【GATT:Notifications】(ATTのHandle Value Notificationを利用)
ATT Opcode(1byte),ATT handle(2 byte)が必要なので、データは、(ATT_MTU -3) byte送れる。
【GATT: Indications】(ATTのHandle Value Indicationを利用)
ATT Opcode(1byte),ATT handle(2 byte)が必要なので、データは、(ATT_MTU -3) byte送れる。
【GATT:Read Characteristic Value 】(ATTのRead Request/Responseを利用)
Read Responseにデータをいれるが、ATT Opcode(1 byte)が必要なので、データは、(ATT_MTU -1) byte送れる。
【GATT: Write Without Response】(ATTのWrite commandを利用)
ATT Opcode(1byte),ATT handle(2 byte)が必要なので、データは、(ATT_MTU -3 )byte送れる。
【GATT: Write Characteristic Value】(ATTのWrite Request/Write Responseを利用)
ATT Opcode(1byte),ATT handle(2 byte)が必要なので、データは、(ATT_MTU -3) byte送れる。