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
OpCode
(1Byte)

Attribute
Parameters
(0-N Byte)

Authentication
Signature
(0 or 12Byte)

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
(=Client Config)

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の集まりとして定義されます。
そのため、上記は、下記のような構造になります。

サービス
0x2800

キャラクタリスティック1

Characteristic Declaration
0x2803
(=キャラクタリスティック)

Characteristic Value
0xAA01
(=温度計データ)

Characteristic Descriptor
0x2902
(=Client Config)

Characteristic Descriptor
0x2901
(=ユーザーデータ)

キャラクタリスティック2

Characteristic Declaration
0x2803
(=キャラクタリスティック)

Characteristic Value
0xAA02
(=設定用)

Characteristic Descriptor
0x2901
(=ユーザーデータ)


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送れる。