Generic Attribute Profile(GATT)層は、Bluetooth Low Energyデバイスがクライアントにデータを公開する方法を定義します。Bluetoothモジュールを製品に統合するエンジニアにとって、GATTサービスアーキテクチャはアプリケーションの性能、消費電力、相互接続性を直接決定します。本記事ではモジュールベース製品における実践的なGATT設計判断を解説します。
GATT階層構造
GATTはデータを4段階の階層で構成します:
| レベル | 説明 | 例 |
|---|---|---|
| Profile | 用途に応じたサービス群 | Heart Rate Profile |
| Service | 関連するCharacteristicのグループ | Heart Rate Service (0x180D) |
| Characteristic | プロパティ付きの名前付き値 | Heart Rate Measurement (0x2A37) |
| Descriptor | Characteristicのメタデータ | Client Characteristic Configuration (0x2902) |
モジュールは通常1つ以上のPrimary Serviceを実装します。Secondary Serviceは他のサービスから参照される場合のみ意味を持ちます。実用上、ほとんどのモジュール設計ではSecondary Serviceを避けます。
サービス設計
Primary Service登録
各サービスは128-bit UUIDで識別されます。2つの割り当て戦略があります:
- 16-bit UUID(0x1800–0x26FF):Bluetooth SIGが標準サービスに割り当て。モジュールが標準プロファイル(例:Battery Service 0x180F)を実装する場合に使用。
- 128-bitカスタムUUID:独自サービス用。ベースUUIDを生成し、各サービスとCharacteristicの下位バイトを増分。
一般的なベースUUIDパターン:
Base: 6E40XXXX-B5A3-F393-E0A9-E50E24DCCA9E Service: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E Char 1: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E Char 2: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E
NordicのUARTサービスで使用されるこの手法は、製品ライン全体のUUID管理を簡素化します。
Characteristicプロパティ
各Characteristicはクライアントとの相互作用を制御するプロパティを宣言します:
| プロパティ | オペコード | 方向 | 用途 |
|---|---|---|---|
| Read | 0x0A | Client → Server | 設定値の読み取り |
| Write | 0x12 | Client → Server | 制御コマンド |
| Write Without Response | 0x52 | Client → Server | 高頻度コマンド |
| Notify | — | Server → Client | センサデータストリーミング |
| Indicate | — | Server → Client | 重要なアラート |
| Broadcast | — | Server → All | ビーコン形式のアドバタイズ |
Notification vs Indication
両者はサーバからクライアントへのデータプッシュですが、信頼性保証が異なります:
- Notify(ATT_HANDLE_VALUE_NOTIFICATION):応答確認なし。レイテンシ:1 Mbps PHYで~3 ms/パケット。スループット:23-byte MTUで最大270 kbps、247-byte MTUで最大~800 kbps。
- Indicate(ATT_HANDLE_VALUE_INDICATION):クライアントからのATT確認必須。1往復追加(~6–10 ms)。配送確認が必須のデータに使用。
10 HzでセンサデータをストリーミングするモジュールではNotifyが明確な選択です — 次のサンプルが消失パケットを補完します。アラームイベントを報告するモジュールではIndicateがクライアントの受信確認を保証します。
Descriptor設計
NotifyまたはIndicateをサポートするCharacteristicにはClient Characteristic Configuration Descriptor(CCCD、UUID 0x2902)が必須です。CCCDに0x0001を書き込むとNotifyが有効、0x0002でIndicate有効、0x0000で両者無効になります。
推奨されるオプションDescriptor:
| Descriptor | UUID | 目的 |
|---|---|---|
| Characteristic User Description | 0x2901 | 人間可読名 |
| Characteristic Presentation Format | 0x2904 | 単位、指数、データ型 |
| Characteristic Extended Properties | 0x2900 | Reliable Writeサポート |
Presentation Format Descriptorは複数センサタイプを提供するモジュールで特に有用 — クライアントアプリケーションが値が温度(0x07、単位0x272F = 摂氏)か湿度(0x06、単位0x272F = パーセント)かを自動検出できます。
MTUとスループット最適化
デフォルトATT MTUは23バイト(ペイロード20バイト)。最近のスマートフォンはMTU 247以上をサポートします。接続時にMTU交換を要求すべきです:
// MTUネゴシエーションの擬似コード ble_att_exchange_mtu_request(247); // 交換後、有効MTU = min(ローカル, リモート) // Notifyペイロード = MTU - 3バイト ATTヘッダ
各MTU値でのスループット比較(接続間隔30 ms、1 Mbps PHY):
| MTU | ペイロード/パケット | パケット/間隔 | スループット |
|---|---|---|---|
| 23 | 20 B | 4 | 21 kbps |
| 185 | 182 B | 1 | 48 kbps |
| 247 | 244 B | 1 | 65 kbps |
Data Length Extension(DLE)対応モジュール(Bluetooth 4.2+)では、DLEと大MTUの組み合わせで1 Mbps PHY上のスループットが800 kbpsを超える可能性があります。
実践例:マルチセンササービス
温度、湿度、加速度センサを統合するモジュールのフラットサービス設計:
Service: 6E400001-... (Custom Sensor Service) ├── Char: 6E400002-... (Temperature, Read|Notify) │ └── CCCD: 0x2902 │ └── Presentation Format: 0x2904 (int16, 0.01°C, unit Celsius) ├── Char: 6E400003-... (Humidity, Read|Notify) │ └── CCCD: 0x2902 │ └── Presentation Format: 0x2904 (uint16, 0.01%, unit percentage) ├── Char: 6E400004-... (Acceleration XYZ, Notify) │ └── CCCD: 0x2902 └── Char: 6E400005-... (Sampling Rate, Read|Write)
Sampling Rate Characteristicにより、クライアントはモジュールの通知頻度を設定できます — 10を書き込むと10 Hz、0で全通知無効化。
消費電力への影響
各Notifyは1 Mbpsで~1.2 msの無線アクティブ時間を消費します。10 Hz、20バイトペイロードの場合:
- 無線アクティブ率:~12 ms / 1000 ms = 1.2% デューティサイクル
- 推定平均電流:0.012 × 8 mA (TX) + 0.988 × 5 µA (sleep) ≈ 100 µA
- CR2032電池寿命(220 mAh):~2200時間 ≈ 91日
1 Hzに低下すると、平均電流は~13 µAに低下し、電池寿命は~1.9年に延長されます。
一般的な設計上の落とし穴
- 過剰に分割されたサービス:関連データを複数サービスに分散すると、クライアントは複数回のサービス発見が必要になります。関連Characteristicを1つのサービスにまとめましょう。
- CCCDの欠落:Notifyプロパティを持つCharacteristicにCCCDがないと、一部のスタックがサービスを拒否します。Notify可能なCharacteristicには必ずCCCDを含めましょう。
- UUID衝突:体系的なベースパターンなしでランダム128-bit UUIDを使用すると、製品バリアント間で衝突のリスクがあります。連続下位バイトのベースUUIDを採用しましょう。
- 過剰なIndicate使用:高頻度データ(例:50 Hz IMU)にIndicateを使用すると、確認応答で帯域幅を浪费します。Indicateは低頻度・高信頼性イベントに限定しましょう。
- Presentation Formatの無視:0x2904 Descriptorがないと、クライアントアプリケーションはデータ解釈をハードコードしなければならず、nRF Connect等の汎用GATTブラウザとの相互接続性が損なわれます。
結論
GATTサービスアーキテクチャはBluetoothモジュールとホスト間のアプリケーション層契約です。適切なサービス構成、Characteristicプロパティの選択、Descriptorの正しい使用は、モジュールが第三者エコシステムにクリーンに統合されるか、デバッグの負担となるかを決定します。フラットサービス設計、ストリーミングデータのNotify、体系的UUID割り当て、MTU最適化 — これらの原則がモジュールベース製品開発の確かな基盤となります。