ぷろじぇくと、みすじら。

アドバタイズしているBluetooth LEデバイスをUniversal Windows Platform APIでペアリングしたい

Created at:

Windows 10以降にはBluetooth LEを扱うAPIが備わっていて、そのAPIを使うことでアドバタイジングパケットを送受信したり、ペアリングしたりできます。

そんなわけでBLEデバイスをペアリングしたいと思って試してみたのですが意外と罠が多いです。

ペアリングの流れ

UWP APIを利用してペアリングして使う流れは次のようになっています。

  1. BluetoothLEAdvertisementWatcher クラスでアドバタイジングしているデバイスを探す
  2. 見つけたらUIスレッド内でペアリングを実行する
    1. BluetoothLEDevice.FromBluetoothAddressAsync メソッドでBLEデバイスを取得する
    2. 取得したオブジェクトの DeviceInformation.Pairing.PairAsync メソッドでペアリング実行
  3. DeviceWatcher クラスで追加/更新されたデバイスを監視
  4. GATTサービスを取得する

まずUIスレッド内でペアリングを実行する必要がある、という点に注意が必要です。

次にペアリングが完了してもすぐ使えると思ったらそんなことはなく、接続完了するまで待たなければなりません。ペアリングを待って(await)も接続を待ったわけではないので今度はDeviceWatcherでWindows側の準備が完了をするのを待ちます。めんどくさい…。
ちなみにBluetoothConnectionStatusは利用可能な接続状態とは違うようです。

ここからはUIにデバイスリストを表示してそこにデバイスを表示して選択するのとおおよそ同じだと思います。

デバイスを監視しているとペアリング後になにやらいろいろ追加されます。主にGATTのサービスなどがデバイスとしてAdded/Updatedイベントにやってきます。
なおデバイスがインストールされると途中で名前が変更される可能性があるのでAddedとUpdated両方見た方がよさそうです。

しかしやってきたデバイスIDは BluetoothLEDevice や GattDeviceService として開けるかどうかは開いてみないとわかりません。なんというめんどくささ…。
実際StackOverflowにもtry/catchしときみたいな回答がついてます。

…のでDeviceWatcherで監視するときにFilterで利用したいGATTのUUIDを指定することをお勧めします。

DeviceInformation.CreateWatcher(GattDeviceService.GetDeviceSelectorFromUuid(new Guid("f000aa00-0451-4000-b000-000000000000")));

これであればAdded/Updatedイベントで確実に指定したもののデバイスIDが来るので取得できます。

var gattService = await GattDeviceService.FromIdAsync(new Guid("f000aa00-0451-4000-b000-000000000000"));

ちなみに GattDeviceService.FromIdAsync メソッドはデバイスが存在しないとExceptionを投げますが、誰か別なプロセスが利用中の場合 null を返すので完全に罠です。

いずれにせよペアリングとデバイス列挙は別なので、ペアリングしてもそのままの流れで使い始めることはできないということのようです。

コンソールアプリケーションで動かしたい

ところでUWPのAPIはコンソールアプリケーションからも扱えて、BLEで通信も確かにできるのですがコンソールアプリケーションからはペアリングできません。

想像するにペアリング用のUIを提供している都合なのだと思いますが DeviceInformationPairing.PairAsync メソッドを呼ぶと失敗します(そもそも元からDispatcher経由で呼ばないといけない)。

DeviceInformationCustomPairing クラスなら?と思ったのですが現状対応していないようで、将来的には DeviceInformationCustomPairing クラスで対応するらしいので現状はあらかじめペアリングしておくとかが必要です。