iBeaconをスキャンするWindowsデスクトップアプリ

#はじめに
iBeaconのアドバタイズパケットをスキャンするWindowsアプリを作ってみる。

#環境
- OS=Windows10
- 開発環境=Visual Studio 2015
	- C#
	- .Net Framework 4.6.1
	- WPFアプリケーション
- iBeaconデバイス
	- [MAMORIO](https://mamorio.jp/)
	- iPhone [iBeacon Scanner](https://itunes.apple.com/jp/app/ibeacon-scanner/id907396646?mt=8)
	- Android [Beacon Simulator](https://play.google.com/store/apps/details?id=net.alea.beaconsimulator&hl=ja)

#プログラムを作る
##プロジェクト作成
- iBeaconScannerというWPFプロジェクトを新規作成する。
- ~~NuGetからUwpDesktopをインストールする。参考:[WPFでBLEのアドバタイズパケットを拾ってみよう](http://blog.okazuki.jp/entry/2016/07/19/221226)~~
- NuGetから`Microsoft.Windows.SDK.Contracts`をインストールする

---

**2020/8/31追記**
`Microsoft.Windows.SDK.Contracts`について
UwpDesktopは現在開発が止まっており、代わりに`Microsoft.Windows.SDK.Contracts`で同じことができます。

- Windows 10 WinRT API PackがNuGetパッケージ`Microsoft.Windows.SDK.Contracts`として公開されています。
- 開発プロジェクトを`PackageReference`対応へ設定変更し、NuGetパッケージを追加するだけで利用可能。

詳細
- [Microsoft、Windows 10の機能を簡単にWinForms/WPFアプリへ組み込めるライブラリを公開](https://forest.watch.impress.co.jp/docs/news/1210634.html)
- [デスクトップアプリで UWP Api を呼び出す](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance)

---


##プログラミング①
- usingに**Windows.Devices.Bluetooth.Advertisement**の追加
- クラスメンバに**BluetoothLEAdvertisementWatcher advWatcher**を追加
- MainWindowのコンストラクタでBluetoothLEAdvertisementWatcherクラスを生成、アドバタイズパケット受信イベントを追加してStartする。

```C#:MainWindowコンストラクタ
public MainWindow()
{
    InitializeComponent();

    this.advWatcher = new BluetoothLEAdvertisementWatcher();
    this.advWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(1000);
    this.advWatcher.Received += this.Watcher_Received;
    this.advWatcher.Start();
}
```

- Watcher_Received()を追加。
- 非同期でイベントが発生するのでasync/awaitにする。
- アドバタイズパケットはargsに詰まっている。

```C#:Watcher_Received()
private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
    await this.Dispatcher.InvokeAsync(() => {
        // ここで処理
    });
}
```

###ひとやすみ・・・
- ここまでで、Bluetoothデバイスをスキャンするところまで完成。
- 実行すると、Bluetoothデバイスが近くにあればWatcher_Received()がCallされて、アドバタイズパケットがargsに詰まっている。
- iBeaconが発信するアドバタイズパケットもこの中に含まれているが、どれがiBeaconなのかチェックする必要がある。

##プログラミング②
- **BluetoothLEAdvertisementReceivedEventArgs**を解析する。
- [Windows10デバイスでiBeaconの全データを取得する方法](http://sonic.blue/it/605)が大変参考になりましたので、そのままコピペさせていただきました。

```C#:Watcher_Receivedの中身を実装する
private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
    await this.Dispatcher.InvokeAsync(() => {
        // 参考：Windows10デバイスでiBeaconの全データを取得する方法　http://sonic.blue/it/605
        iBeacon bcon = new iBeacon(args);
        if (bcon.UUID != null) {
            // iBeacon
            DateTimeOffset timestamp = args.Timestamp;
            string retBeaconData;
            retBeaconData = "{";
            retBeaconData += string.Format("uuid:'{0}',", bcon.UUID);//"00000000-0000-0000-0000-000000000000"
            retBeaconData += string.Format("major:{0},", bcon.Major.ToString("D"));
            retBeaconData += string.Format("minor:{0},", bcon.Minor.ToString("D"));
            retBeaconData += string.Format("measuredPower:{0},", bcon.MeasuredPower.ToString("D"));
            retBeaconData += string.Format("rssi:{0},", bcon.Rssi.ToString("D"));
            retBeaconData += string.Format("accuracy:{0},", bcon.Accuracy.ToString("F6"));
            retBeaconData += string.Format("proximity:'{0}'", bcon.Proximity);
            retBeaconData += "}";

            this.textBox.Text = this.textBox.Text + timestamp.ToString("HH\\:mm\\:ss\\.fff") + ":" + retBeaconData + "\r\n";
        }

    });
}
```

###プログラミング完了
これで、Bluetoothデバイスが発信するアドバタイズパケットからiBeaconだけを抽出して画面のテキストボックスに表示するようなアプリができた。

[**ソース　→　GitHub**](https://github.com/gebogebogebo/iBeaconScanner)

#検証

##MAMORIO
- [MAMORIOが届いたので解析したかった](http://shima-nigoro.hatenablog.jp/entry/2016/07/09/201953)によると、UUID = b9407f30-f5f8-466e-aff9-25556b57fe6e 。
- MAMORIOからは30秒に1回ほどアドバタイズパケットが取れた。

##iPhone iBeacon Scanner
- Virtual iBeacon機能でアドバタイズパケットを発信できる。
- UUID = 95f428b1-4a3a-4e39-b086-21bff38deb6d
- アプリを立ち上げている間はスキャンできるが、iphoneがロックされるとスキャンできない。これはiOSアプリの仕様だろう（バックグランド発信できない）。

##Android Beacon Simulator
- SIMULATOR機能でアドバタイズパケットを発信できる。
- UUIDは自由に設定できる。
- Androidをロックしていてもパケット発信するし、アプリが立ち上がっていなくてもパケット発信している。Androdアプリはそういうものなのか、驚。

##その他
- 検証していると、上記以外のパケットがたくさんとれる、iBeaconデバイスがそんなに周りにあるのか、それともノイズなのか、謎…

#最後に
- 今回はざっくり実装ですが、Microsoftのサイトをきちんと読めば正しく理解できるかもです。　→　[Bluetooth LE アドバタイズ](https://docs.microsoft.com/ja-jp/windows/uwp/devices-sensors/ble-beacon)
- Bluetooth、BLE、Beacon、iBeacon。これらの仕様は複雑でネットの情報も少ないです。誤り、アドバイスなどいただければありがたいです。