次の方法で共有


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 デバイスを操作できます。

  • 属性の検出を実行する
  • 属性値の読み取りと書き込み
  • Characteristic ValueChanged イベントのコールバックを登録する

有用な実装を作成するには、開発者は、アプリケーションが使用する GATT サービスと特性に関する事前知識を持ち、API によって提供されるバイナリ データがユーザーに提示される前に有用なデータに変換されるように、特定の特性値を処理する必要があります。 Bluetooth GATT API は、Bluetooth LE デバイスとの通信に必要な基本プリミティブのみを公開します。 データを解釈するには、アプリケーション プロファイルを、Bluetooth SIG 標準プロファイルまたはデバイス ベンダーによって実装されたカスタム プロファイルによって定義する必要があります。 プロファイルは、交換されたデータが何を表し、それを解釈する方法に関して、アプリケーションとデバイスの間にバインド コントラクトを作成します。

便宜上、Bluetooth SIG はパブリック プロファイルの リストを保持 使用できます。

完全なサンプルについては、Bluetooth 低エネルギーのサンプルに関するページを参照してください。

近くのデバイスのクエリ

近くのデバイスに対してクエリを実行するには、主に次の 2 つの方法があります。

2 番目のメソッドは、 Advertisement ドキュメントで長く説明されているため、ここではあまり説明しませんが、基本的な考え方は、特定の Advertisement Filterを満たす近くのデバイスのBluetoothアドレスを見つけることです。 アドレスを取得したら、 BluetoothLEDevice.FromBluetoothAddressAsync を呼び出してデバイスへの参照を取得できます。

次に、DeviceWatcher メソッドに戻ります。 Bluetooth LE デバイスは、Windows の他のデバイスと同じように、 Enumeration 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 Device オブジェクトを取得します。

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 エラーが返されます。

Note

このメソッドを単独で呼び出し、BluetoothLEDevice オブジェクトを作成しても、接続が開始するとは限りません。 接続を開始するには、GattSession.MaintainConnectiontrue に設定するか、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
}

ヒント

DataReaderDataWriter は、多くのBluetooth API から取得する生バッファーを操作する際に不可欠です。

通知のサブスクライブ

特性が [指定] または [通知] をサポートしていることを確認します (特性プロパティを確認してください)。

値が変更された各イベントはクライアント デバイスからの受信確認と結合されるため、より信頼性が高いと見なされます。 ほとんどの GATT トランザクションでは、信頼性が非常に高くなく、電力を節約する必要があるため、通知の方が一般的です。 いずれの場合も、アプリが関与しないように、そのすべてがコントローラー レイヤーで処理されます。 これらを単なる "通知" と総称します。

通知を受け取る前に、次の 2 つの点に注意する必要があります。

  • クライアント特性構成記述子 (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.
}