Bluetooth GATT クライアント
この記事では、ユニバーサル Windows プラットフォーム (UWP) アプリに Bluetooth 汎用属性 (GATT) クライアント API を使用する方法について説明します。
重要
Package.appxmanifest で "bluetooth" 機能を宣言する必要があります。
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
重要な API
概要
開発者は、Windows.Devices.Bluetooth.GenericAttributeProfile 名前空間の API を使って Bluetooth LE デバイスにアクセスすることができます。 Bluetooth LE デバイスは、その機能をコレクションを通じて公開します。コレクションには次の情報が含まれています。
- サービス
- 特性
- 記述子
サービスは、LE デバイスの機能的なコントラクトを定義するもので、サービスを定義する特性のコレクションを含みます。 これらの特性はさらに、その特性を表す記述子を含みます。 これら 3 つの用語は、一般的に、デバイスの属性と呼ばれます。
Bluetooth LE GATT API は、生のトランスポートにアクセスするのではなく、オブジェクトと関数を公開します。 また、GATT API で Bluetooth LE デバイスと連携することによって、次のことが可能となります。
- 属性の検出の実行
- 属性値の読み取りと書き込み
- 特性の ValueChanged イベントで呼び出されるコールバックの登録
実用的なアプリケーションを作成するためには、利用する GATT のサービスと特性についての予備知識が開発者に求められます。実際に必要な特性値を処理し、API から提供されるバイナリ データを実用的なデータに変換したうえで、ユーザーに提示しなければなりません。 Bluetooth GATT API が公開するのは、Bluetooth LE デバイスとの通信に必要な基本的なプリミティブだけです。 データを解釈するためには、Bluetooth SIG の標準のプロファイルか、デバイスのベンダーが実装したカスタム プロファイルによって、アプリケーション プロファイルを定義する必要があります。 プロファイルは、交換されるデータが表す内容や、その解釈の方法に関して、アプリケーションとデバイスとの間で交わされるバインド コントラクトを形成します。
Bluetooth SIG は、利便性向上のため、一連のプロファイルを一般公開しています。
例
完全なサンプルについては、Bluetooth 低エネルギーのサンプルに関するページを参照してください。
近くのデバイスの照会
近くのデバイスを照会するための主なメソッドは 2 つあります。
- Windows.Devices.Enumeration の DeviceWatcher
- Windows.Devices.Bluetooth.Advertisement の BluetoothLEAdvertisementWatcher
2 つ目のメソッドについては、アドバタイズに関するドキュメントで詳しく説明されているため、ここでは簡単に説明します。基本的な考え方は、特定のアドバタイズ フィルターの条件を満たす、近くにあるデバイスの Bluetooth アドレスを検出するということです。 アドレスを検出したら、BluetoothLEDevice.FromBluetoothAddressAsync を呼び出して、デバイスへの参照を取得します。
DeviceWatcher メソッドの説明に戻ります。 Bluetooth LE デバイスは、Windows の他のデバイスと同じように列挙 API を使って照会できます。 DeviceWatcher クラスを使用して、検索するデバイスを指定するクエリ文字列を渡します。
// Query for extra properties you want returned
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;
// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
// Start the watcher.
deviceWatcher.Start();
DeviceWatcher を開始すると、対象のデバイスの Added イベントのハンドラーで、クエリを満たすデバイスごとに DeviceInformation を受信します。 DeviceWatcher について詳しくは、Github にある完全なサンプルをご覧ください。
デバイスへの接続
目的のデバイスが検出されたら、DeviceInformation.Id を使用して、対象のデバイスの Bluetooth LE デバイス オブジェクトを取得します。
async void ConnectDevice(DeviceInformation deviceInfo)
{
// Note: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent.
BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
// ...
}
一方、デバイスの BluetoothLEDevice オブジェクトへのすべての参照を破棄すると (システム上の他のアプリがそのデバイスを参照していない場合)、短いタイムアウト期間後に自動切断がトリガーされます。
bluetoothLeDevice.Dispose();
アプリが再びデバイスにアクセスする必要がある場合は、単にデバイス オブジェクトを再作成して特性にアクセスする (次のセクションで説明します) と、必要に応じて、OS による再接続がトリガーされます。 デバイスが近くにある場合は、デバイスへのアクセスが取得されます。近くにない場合は、DeviceUnreachable エラーと共に制御が戻ります。
注意
このメソッドを単独で呼び出し、BluetoothLEDevice オブジェクトを作成しても、接続が開始するとは限りません。 接続を開始するには、GattSession.MaintainConnection を true
に設定するか、BluetoothLEDevice でキャッシュされていないサービス検出方法を呼び出すか、デバイスに対して読み取り/書き込み操作を実行します。
- GattSession.MaintainConnection が true に設定されている場合、システムは、接続を無期限に待機し、デバイスが使用可能になると接続します。 GattSession.MaintainConnection はプロパティであるため、アプリケーションによる待機は必要ありません。
- GATT でのサービス検出および読み取り/書き込み操作の場合、システムは、さまざまな長さに制限された時間を待機します。 瞬間から、ほんの数分まで、時間の長さはさまざまです。 要素には、スタック上のトラフィック、および要求のキューの配置方法が含まれます。 保留中の他の要求が存在せず、リモート デバイスに到達できない場合、システムは、タイムアウトになるまで 7 秒間待機します。保留中の他の要求が存在する場合、キュー内の各要求の処理には 7 秒かかる場合があることから、キューの後ろになるほど待機時間が長くなります。
現時点では、接続プロセスを取り消すことができません。
サポートされているサービスと特性の列挙
BluetoothLEDevice オブジェクトが取得されたので、次の手順はデバイスが公開するデータを検出することです。 これを行うための最初の手順は、サービスの照会です。
GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var services = result.Services;
// ...
}
対象のサービスが識別されたら、次の手順は特性の照会です。
GattCharacteristicsResult result = await service.GetCharacteristicsAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var characteristics = result.Characteristics;
// ...
}
OS は、操作を実行できる GattCharacteristic オブジェクトの ReadOnly リストを返します。
特性の読み取り/書き込み操作の実行
特性は、GATT ベースの通信の基本単位です。 これには、デバイス上の各種データを表す値が含まれています。 たとえば、バッテリ レベル特性には、デバイスのバッテリ レベルを表す値が含まれます。
特性のプロパティを読み取って、どのような操作がサポートされているかを特定します。
GattCharacteristicProperties properties = characteristic.CharacteristicProperties
if(properties.HasFlag(GattCharacteristicProperties.Read))
{
// This characteristic supports reading from it.
}
if(properties.HasFlag(GattCharacteristicProperties.Write))
{
// This characteristic supports writing to it.
}
if(properties.HasFlag(GattCharacteristicProperties.Notify))
{
// This characteristic supports subscribing to notifications.
}
読み取りがサポートされている場合は、値を読み取ることができます。
GattReadResult result = await selectedCharacteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var reader = DataReader.FromBuffer(result.Value);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
// Utilize the data as needed
}
特性への書き込みは、同様のパターンに従います。
var writer = new DataWriter();
// WriteByte used for simplicity. Other common functions - WriteInt16 and WriteSingle
writer.WriteByte(0x01);
GattCommunicationStatus result = await selectedCharacteristic.WriteValueAsync(writer.DetachBuffer());
if (result == GattCommunicationStatus.Success)
{
// Successfully wrote to device
}
ヒント
DataReader と DataWriter は、多くの Bluetooth API から取得する生バッファーを操作する場合に不可欠です。
通知の受信登録
特性が Indicate または Notify をサポートしているかどうかを確認します (確認するには特性のプロパティを調べます)。
各値変更イベントがクライアント デバイスからの受信確認と組み合わされているため、より信頼性が高いと見なされます。 多くの GATT トランザクションでは、非常に高い信頼性よりも電力の節約が重視されるため、Notify の方が一般的です。 いずれの場合も、そのすべてがコントローラー レイヤーで処理されるため、アプリは関与しません。 これらをまとめて単なる "通知" と見なします。
通知を取得する前に処理することが 2 つあります。
- Client Characteristic Configuration Descriptor (CCCD) への書き込み
- Characteristic.ValueChanged イベントの処理
CCCD への書き込みによって、特定の特性値が変化するたびに、このクライアントでその変化を把握する必要があることを、サーバー デバイスに指示します。 手順は次のとおりです。
GattCommunicationStatus status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
if(status == GattCommunicationStatus.Success)
{
// Server has been informed of clients interest.
}
これで、リモート デバイスで値が変更されるたび、GattCharacteristic の ValueChanged イベントが呼び出されます。 あとはハンドラーを実装するだけです。
characteristic.ValueChanged += Characteristic_ValueChanged;
...
void Characteristic_ValueChanged(GattCharacteristic sender,
GattValueChangedEventArgs args)
{
// An Indicate or Notify reported that the value has changed.
var reader = DataReader.FromBuffer(args.CharacteristicValue)
// Parse the data however required.
}
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示