概要
GATT(Generic Attribute Profile)は、BLE(Bluetooth Low Energy)デバイス間でデータをどのように構造化し、読み書き・通知を行うかを定めたプロファイルです。BLEアプリケーション層のデータモデルの根幹を担い、Peripheral(センサーなど)が持つデータをCentral(スマートフォンなど)が取得・操作する際の共通言語として機能します。
GATTはATT(Attribute Protocol)の上に構築されており、ATTが定義する「属性(Attribute)」をサービス・キャラクタリスティック・デスクリプタという階層構造で整理します。この階層構造はBluetooth SIGが標準化しており、標準サービスのUUIDが割り当てられているため、異なるメーカーのデバイス間でもデータの意味を共通理解できます。
歴史・背景
GATTはBluetooth 4.0(2010年)でBLEとともに策定されました。それ以前のBluetooth ClassicにもSDP(Service Discovery Protocol)がありましたが、GATTはより簡潔でリソース制約の大きいセンサーデバイス向けに再設計されました。
Bluetooth SIGはSIG標準プロファイルを継続的に追加しており、2024年時点で心拍数・血圧・血糖値・温度・電池残量・位置情報など数十種類の標準サービスが定義されています。独自のカスタムサービスはランダム生成した128bit UUIDで定義できます。
技術仕様
GATTの階層構造
GATT Server(Peripheral)
└── Service(サービス) ← UUID で識別
├── Characteristic(キャラクタリスティック) ← UUID + Properties + Value
│ ├── Value(データ本体)
│ └── Descriptor(デスクリプタ) ← メタデータ
└── Characteristic(別のキャラクタリスティック)
├── Value
└── Descriptor
Service(サービス)
サービスは関連するキャラクタリスティックのグループです。16bitの標準UUIDまたは128bitのカスタムUUIDで識別されます。
主な標準サービスUUID(16bit)の例:
| サービス名 | UUID |
|---|---|
| Generic Access | 0x1800 |
| Generic Attribute | 0x1801 |
| Battery Service | 0x180F |
| Heart Rate | 0x180D |
| Health Thermometer | 0x1809 |
| Blood Pressure | 0x1810 |
| Device Information | 0x180A |
| Environmental Sensing | 0x181A |
Characteristic(キャラクタリスティック)
キャラクタリスティックは実際のデータを保持する単位です。以下の属性を持ちます:
- UUID: データの種類を示す識別子
- Value: 実際のデータ(最大512バイト、ATT MTU に依存)
- Properties: Read / Write / Notify / Indicate などの操作権限
主なProperties:
| Property | 説明 |
|---|---|
| Read | Centralがデータを読み取れる |
| Write | Centralがデータを書き込める(応答あり) |
| Write Without Response | Centralが応答なしで書き込める(高速) |
| Notify | Peripheralが変化時にCentralへ通知(応答不要) |
| Indicate | Peripheralが変化時にCentralへ通知(応答あり) |
| Broadcast | アドバタイズパケットに含めてブロードキャスト |
Descriptor(デスクリプタ)
デスクリプタはキャラクタリスティックに付属するメタデータです。最も重要なのはCCCD(Client Characteristic Configuration Descriptor、UUID: 0x2902)で、CentralがNotify/Indicateを有効化する際に書き込みます。
ATT MTU(Maximum Transmission Unit)
ATT MTUはATT層の最大パケットサイズです:
- デフォルト: 23バイト(実効データは20バイト)
- Bluetooth 4.2以降: 最大247バイト(実効データ244バイト)まで交渉可能(MTU Exchange)
- Bluetooth 5.x: LE 2M PHYと組み合わせると高スループット
大きなデータを送る場合はMTUを拡大するか、アプリケーション層でパケット分割(Chunking)が必要です。
ATT プロトコル操作
Central(Client) Peripheral(Server)
| |
|--- ATT_READ_REQ(UUID)-------->| ← 読み取り要求
|<-- ATT_READ_RSP(Value)--------| ← 応答
| |
|--- ATT_WRITE_REQ(Value)------>| ← 書き込み要求
|<-- ATT_WRITE_RSP(確認)--------| ← 応答
| |
|<-- ATT_HANDLE_VALUE_NTF--------| ← Notify(応答なし)
| |
|<-- ATT_HANDLE_VALUE_IND--------| ← Indicate(応答あり)
|--- ATT_HANDLE_VALUE_CNF------->| ← Indicate確認
動作原理
サービス探索(Service Discovery)
Central接続後、GATTサービス・キャラクタリスティックの構造を探索します:
- Primary Service Discovery: サービス一覧を取得(ATT_READ_BY_GROUP_TYPE_REQ)
- Characteristic Discovery: 各サービスのキャラクタリスティック一覧を取得
- Descriptor Discovery: 各キャラクタリスティックのデスクリプタを取得
Notify の有効化手順
1. Centralがサービス・キャラクタリスティックを探索
2. Notify対象キャラクタリスティックのCCCD(0x2902)に0x0001を書き込む
3. PeripheralはCCCDに0x0001が書かれたことを確認しNotifyを有効化
4. データ変化時にPeripheralが自動的にNotify送信
5. 切断するとCCCDは0x0000にリセット
Handle(ハンドル)
ATT属性はすべて16bitのHandle番号で識別されます。サービス発見の際にHandleが割り当てられ、読み書きはUUIDではなくHandleで行われます(UUIDでの検索後にHandleを使い回す形が一般的)。
用途・ユースケース
標準プロファイルの利用
標準GATTプロファイルを使うことで、専用アプリなしでもOS標準機能で認識されます:
- Battery Service(0x180F): スマートフォンが自動的に電池残量を表示
- Heart Rate Profile: Androidヘルスアプリが心拍データを自動取得
- HID over GATT(HOGP): キーボード・マウスとしてOS標準ドライバで動作
カスタムサービスの設計
独自センサーデータを送る場合は128bit UUIDのカスタムサービスを定義します:
# Python(bleak)でBLEデバイスからデータ読み取り
import asyncio
from bleak import BleakClient
TARGET_MAC = "XX:XX:XX:XX:XX:XX"
SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
CHAR_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
async def main():
async with BleakClient(TARGET_MAC) as client:
# サービス一覧の表示
for service in client.services:
print(f"Service: {service.uuid}")
for char in service.characteristics:
print(f" Char: {char.uuid}, Props: {char.properties}")
# データ読み取り
value = await client.read_gatt_char(CHAR_UUID)
print(f"Value: {value}")
# Notify購読
def notification_handler(sender, data):
print(f"Notify from {sender}: {data}")
await client.start_notify(CHAR_UUID, notification_handler)
await asyncio.sleep(10.0)
await client.stop_notify(CHAR_UUID)
asyncio.run(main())
実装・開発のポイント
ESP32でGATTサーバーを実装する例
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
// カスタムサービス・キャラクタリスティックUUID
#define SERVICE_UUID "12345678-1234-1234-1234-123456789012"
#define CHAR_TEMPERATURE_UUID "12345678-1234-1234-1234-123456789013"
#define CHAR_HUMIDITY_UUID "12345678-1234-1234-1234-123456789014"
#define CHAR_CONTROL_UUID "12345678-1234-1234-1234-123456789015"
BLECharacteristic* pTempChar;
BLECharacteristic* pHumidChar;
BLECharacteristic* pCtrlChar;
// 書き込みコールバック
class ControlCallback : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic* pCharacteristic) override {
String value = pCharacteristic->getValue().c_str();
if (value == "ON") {
// 制御処理
}
}
};
void setup() {
BLEDevice::init("SensorNode");
BLEServer* pServer = BLEDevice::createServer();
BLEService* pService = pServer->createService(SERVICE_UUID);
// 温度キャラクタリスティック(Read + Notify)
pTempChar = pService->createCharacteristic(
CHAR_TEMPERATURE_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
pTempChar->addDescriptor(new BLE2902()); // CCCDを追加
// 湿度キャラクタリスティック(Read + Notify)
pHumidChar = pService->createCharacteristic(
CHAR_HUMIDITY_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
pHumidChar->addDescriptor(new BLE2902());
// 制御キャラクタリスティック(Write)
pCtrlChar = pService->createCharacteristic(
CHAR_CONTROL_UUID,
BLECharacteristic::PROPERTY_WRITE
);
pCtrlChar->setCallbacks(new ControlCallback());
pService->start();
BLEDevice::getAdvertising()->start();
}
void loop() {
float temp = 25.4f;
float humid = 60.2f;
// float を バイト列に変換してセット
pTempChar->setValue(reinterpret_cast<uint8_t*>(&temp), sizeof(temp));
pTempChar->notify();
pHumidChar->setValue(reinterpret_cast<uint8_t*>(&humid), sizeof(humid));
pHumidChar->notify();
delay(5000);
}
MTU拡張でスループット向上
// ESP32: MTUを517バイトに要求(接続後)
BLEDevice::setMTU(517);
Bluetooth SIGの標準UUIDを使う利点
- スマートフォンのOSが自動的にサービスを認識する場合がある
- nRF Connect等の汎用BLEツールで即座に値の意味が分かる
- Apple Health / Google Fit 等のプラットフォームと連携しやすい
他技術との比較
GATT vs カスタムプロトコル(Notify Raw Data)
GATTを使わずATT上で独自プロトコルを構築することも可能ですが、標準ツールでのデバッグ・スマートフォンアプリ開発のコスト面でGATT利用が推奨されます。
GATT vs CoAP / MQTT
大規模IoTではゲートウェイを経由してMQTTやCoAPへブリッジすることが一般的です。BLE GATTは端末-スマートフォン間、MQTTはクラウドとの通信と役割を分担します。
ATT MTUサイズとスループットの関係
ATT MTU = 23バイト(デフォルト)の場合、実効データは20バイト/パケット。1秒間に7.5パケット交換とすると実効スループットは約150 bytes/secに留まります。MTU = 247バイトに拡大すると実効データ244バイト/パケット、2M PHYと合わせると数十kbps〜数百kbpsが実現できます。