かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

WPFでBLEのアドバタイズパケットを拾ってみよう

UWPのAPIを使ってできるかやってみます。

まず、WpfBleSampleAppという名前でWPFアプリケーションを作成してNuGetからUwpDesktopをインストールします。こいつは、UWPを使うために必要な設定をやってくれる便利な奴です。

f:id:okazuki:20160719220359p:plain

次に、以下の記事を参考にアドバタイズパケットを拾うコードを書きます。

blog.okazuki.jp

まず、画面のXAMLです。TextBlockを置いて、BLEの受信を開始・停止するためのLoadedとClosedイベントを紐づけてます。

<Window x:Class="WpfBleSampleApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfBleSampleApp"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="350"
        Width="525"
        Loaded="Window_Loaded"
        Closed="Window_Closed">
    <Grid>
        <TextBlock x:Name="TextBlockRSSI" />
    </Grid>
</Window>

コードビハインドは、ほぼほぼ上記Blog記事のコードのコピペになります。

using System;
using System.Linq;
using System.Windows;
using Windows.Devices.Bluetooth.Advertisement;

namespace WpfBleSampleApp
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private BluetoothLEAdvertisementWatcher watcher;

        public MainWindow()
        {
            InitializeComponent();
            this.watcher = new BluetoothLEAdvertisementWatcher();

            // CompanyIDとかDataでフィルタリングしたいとき
            //var md = new BluetoothLEManufacturerData();
            //// company id 0xFFFF (多分これ https://www.bluetooth.com/specifications/assigned-numbers/company-Identifiers)
            //md.CompanyId = 0xFFFF; 

            //// data 0x1234
            //var w = new DataWriter();
            //w.WriteUInt16(0x1234);
            //md.Data = w.DetachBuffer();
            //this.watcher.AdvertisementFilter.Advertisement.ManufacturerData.Add(md);

            // rssi >= -60のとき受信開始するっぽい
            this.watcher.SignalStrengthFilter.InRangeThresholdInDBm = -60;
            // rssi <= -65が2秒続いたら受信終わるっぽい
            this.watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -65;
            this.watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(2000);
            this.watcher.Received += this.Watcher_Received;
        }

        private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            await this.Dispatcher.InvokeAsync(() =>
            {
                var md = args.Advertisement.ManufacturerData.FirstOrDefault();
                if (md != null)
                {
                    // ManufactureDataをもとにCompanyIDとったりできる
                }
                this.TextBlockRSSI.Text = $"{args.Timestamp:HH\\:mm\\:ss}, RSSI: {args.RawSignalStrengthInDBm}, Address: {args.BluetoothAddress.ToString("X")}, Type: {args.AdvertisementType}";
            });
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            this.watcher.Stop();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.watcher.Start();
        }
    }
}

実行してみましょう。

f:id:okazuki:20160719221215p:plain

ちゃんと取れてますね。

コード

コードはGitHubに上げています。

github.com