フェンリル

Developer's Blog

【連載】Bluetooth LE (4) Windows 8.1 の Windows ストアアプリで BLE を使う

WinRT-Gatt

こんにちは。共同開発部 開発担当の伊藤です。

Bluetooth LE (以降 BLE)の連載、第4回です。今回は Windows 8.1 で BLE 機器を使う方法をご紹介します。
連載の第1回でもご紹介しましたが、Windows 8 では BLE のサポートはかなり限定的でしたが、Windows 8.1 になってから正式に WinRT のクラスライブラリに BLE アクセス用のクラス群が追加されました。

ちょうど本日10月17日午後8時(日本時間)に Windows 8.1 が一般向けにリリースされ、Windows Store からダウンロードできるようになります
連載をはじめる時はまったく想定に入れてなかったのですが、Windows 8.1 の新機能を使う方法を公開日にご紹介できるとはなんというナイスタイミング!

今回は、Windows 8.1 RTM と Visual Studio 2013 Professional RC を使って、XAML ベース Windows ストアアプリから BLE デバイスにアクセスして値の読み書きを行う方法をご紹介します。

権限の付与

Windows ストアアプリで Bluetooth にアクセスするには、DeviceCapability を Package.appxmanifest に宣言する必要があります。
この宣言は Visual Studio のアプリケーション マニフェスト デザイナーを使用することはできないため、テキストエディタで直接開いて編集する必要があります。Visual Studio 上で編集するには、Package.appxmanifest を右クリックして、「コードの表示」で開くと、XML を直接編集できるようになります。

DeviceCapability は <Capabilities> タグの中に、下記のように定義します。Function タグに書いてある serviceId: はアクセスしたいサービスの UUID を指定します。また、基本的なものは name: で指定可能です。詳しくは How to specify device capabilities for Bluetooth を参照してください。

<Capabilities>
  <m2:DeviceCapability Name="bluetooth.genericAttributeProfile">
    <m2:Device Id="any">
      <m2:Function Type="serviceId:E18F21CA-63DE-4BBB-8881-B12CF538D25A" />
    </m2:Device>
  </m2:DeviceCapability> 
</Capabilities>

機器の検索

まず、DeviceInformation.FindAllAsync でデバイス・サービスを検索します。このメソッドは検索条件次第で BLE 以外のデバイスも列挙できるのですが、特定のサービスを公開している BLE 機器を検索する条件を GattDeviceService.GetDeviceSelectorFromUuid で作成できます。BLE デバイスの場合はサービス自体が個々のデバイスとして列挙されます。

次に、取得したデバイスからIDを取得し、そのIDをもとに GattDeviceService.FromIdAsync でサービスを扱う GattDeviceService のインスタンスを取得できます。GattDeviceService.GetCharacteristics でキャラクタリスティックを扱う GattCharacteristic のインスタンスを取得できます。

ここまでの説明を実装した処理が下記の通りです。async/await のおかげでかなりシンプルですね。

using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;

// -- 中略 --

private GattDeviceService service;
private GattCharacteristic characteristic;

private async void Button_Click(object sender, RoutedEventArgs e)
{
    // デバイスを検索
    var devices = await DeviceInformation.FindAllAsync(
        GattDeviceService.GetDeviceSelectorFromUuid(new Guid("2AC94B65-C8F4-48A4-804A-C03BC6960B80")));
    if (devices.Count > 0)
    {
        // サービスを作成
        this.service = await GattDeviceService.FromIdAsync(devices.First().Id);

        // キャラクタリスティックを取得
        var characteristics = service.GetCharacteristics(new Guid("4FD800F8-D3B6-48F9-B232-29E95984F76D"));
        if (characteristics.Count > 0)
        {
            this.characteristic = characteristics.First();

            // 通知イベントを登録
            characteristic.ValueChanged += characteristic_ValueChanged;

            var dialog = new MessageDialog("Connected!");
            await dialog.ShowAsync();
        }
    }
    else
    {
        var dialog = new MessageDialog("Device not found");
        await dialog.ShowAsync();
    }
}

値の取得

値の取得は GattCharacteristic の ReadValueAsync メソッドを呼ぶことで取得できます。また、Notify があるキャラクタリスティックの場合は ValueChanged イベントにイベントハンドラを登録しておくことで、Notify 受信時にイベントハンドラが実行されるようになります。

今回は ValueChanged イベントを使って値を取得しました。値は IBuffer でとれるので、byte の配列にして使用しました。

void characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
    var buffer = args.CharacteristicValue.ToArray();
    if (buffer.Last() == 1)
    {
        leftTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        rightTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
    }
    else if (buffer.Last() == 2)
    {
        leftTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
        rightTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    }
    else
    {
        leftTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        rightTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    }
}

値の書き込み

キャラクタリスティックに値を書き込むには WriteValueAsync を使用します。

private async void Button_Click_1(object sender, RoutedEventArgs e)
{
    if (this.characteristic != null)
    {
        var buffer = Encoding.UTF8.GetBytes(this.characteristicValueTextBox.Text).AsBuffer();
        await this.characteristic.WriteValueAsync(buffer);
    }
}

実際には考慮すべき事項

DeviceInformation.FindAllAsync は「周辺にデバイスがない状況」も「BLEが使えない状況」のどちらも空のコレクションを返しますので、エラーメッセージ等はそれを見越したものにする必要があります。特に、Windows は iOS デバイスに比べて環境が多種多様なので、Windows 8.1 の端末でも必ず使用できる状態にはないことが重要です。

また、各メソッドは例外を返す可能性がありますので、適切に例外処理を記述する必要があります。

使える環境について

今回試してみようと思い、BLE 対応の USB ドングルを Windows 8.1 のマシンに差し込んだところ、BLEをうまく動かすことができませんでした。USB ドングルを使用した場合だと BLE の使用に必要な「Microsoft Bluetooth LE Enumerator」がデバイスマネージャーに追加されないようで、オンボードでBLEアダプタが搭載されている機種に限定されているのかもしれません。

いかがでしたでしょうか。Core Bluetooth に比べると Windows ストアアプリの BLE は簡単に処理を記述できます。Surface などでは問題なく動かせるはずなので、BLE 機器をお持ちの方は今夜 Windows 8.1 がリリースされたら Visual Studio 2013 Express を入れて試してみてはいかがでしょうか。

追記 (2014/11/27)

その後、Bluetooth LE に対応したデバイスがオンボードで搭載されている Windows 8.1 Update 端末(Surface Pro3)が入手できたので、実機で検証することができました。その結果以下のことがわかっています。そのため、記事内の一部の表現についても変更させていただきました。

・Windows ストアアプリからはペアリング操作はできず、事前に「PC設定」のデバイスから、通常の Bluetooth 機器と同様にペアリングを実施しておく必要があります。
・iOS の CBPeripheralManager で実装したデバイスに対しては接続を確認することができていません。Windows の標準 Bluetooth スタックでは Bluetooth LE のみを実装したデバイスに対しては接続できるものの、Bluetooth SMART Ready であるデバイスに対してペアリングできないのではないかと予想しています。(SensorTag や .NET Gadgeteer の Bluetooth SMART モジュールなどとは接続できました)

フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!

Facebook コメント

コメント

鈴木律雄2013年10月24日 18:53

フェンルリ様

このサイトをみつけ非常に早く情報を公開し感銘を受けました
そこでKonashiを接続しようと奮闘したのですが、
DeviceInformation.FindAllAsync
でデバイスが帰ってきません。

マニフェストやUUIDなどは何度も確かめ、
デバイスマネージャにMicrosoft Bluetooth LE Enumeratorは表示されています。

BLEはペアリングはいらないと思うのですが、
アプリをで取得させるようにするには、ペアリングなどOSがわで何か設定する必要があるのでしょうか?

ご教授いただけますでしょうか。

植木大輔2014年11月26日 9:30

fenrir様

こちらのサイト参考にさせて頂いております。
iOS8のCBPeripheralManagerで開発したappをWindows8.1と接続しようとしてまずはWindowsのペアリングをしようとしましたが、「追加できませんでした」「もう一度やり直して、デバイスが見つかることを確かめてください。」というメッセージが出力されてどうも接続できません。DeviceInfomation::FindAllAsyncを利用するには初めにWindowsOSとのペアリングが必要だと思いますが間違いでしょうか?
接続するにはiOS8側で何らかの設定変更が必要なのでしょうか?

伊藤2014年11月27日 16:12

コメントありがとうございます。

この記事を書いた当時は環境が揃わずコードの紹介のみとなっていましたが、環境を用意できましたので検証した結果を追記いたしました。
CBPeripheralManager への接続の要望はきっと多いはずなので、そのうち Windows 側で対応されないかなと期待するばかりです。

名前(必須)

メールアドレス(必須)

URL

スタイル用のタグが使えます

このコメント欄でのご質問、ご要望には、開発チームから回答できない場合があります。ご質問、ご要望は「User Community」内のフォーラムまでお寄せください。