Xamarin.Forms Tips - Plugin.BLEを使う

#概要
今、温度センサーを使って、配達物の温度監視をするIoTシステムを作っています。
Xamarin.Formsで、まずはiPhoneアプリを作ってみようかと。
温度センサーには、Inkbird IBS-TH1 miniを使います。

ということで、iPhoneアプリでBLE通信を行うわけですが、
そのためにPlubin.BLEを使った話。

#開発環境など
Macbook Air（macOS Catalina 10.15.4）
Visual Studio for Mac（8.5.5）
Xamarin.Forms

#Plugin.BLEを入れる
1. プロジェクトメニューから、NuGetパッケージの管理を選択します。
![スクリーンショット 2020-05-17 15.49.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/a2d99069-294b-2376-e058-d6005604a520.png)

2. 「Plugin.BLE」を検索し、チェックを入れ、パッケージの追加をクリックします。
※全てのプロジェクトに追加してください。
![スクリーンショット 2020-05-17 15.49.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/4ac5c824-1f09-02d2-0545-b29c2eb0c5a9.png)

#Plugin.BLEを使う
共通プロジェクトで画面を作成し、開始ボタンがクリックされたら、
BLE通信の処理を開始するようにしています。
今回はタイマー処理で、１分間に１度温度センサーからデータを取得するようにしています。

まずはボタンがクリックされた時の処理

```MainPage.cs
        private IBluetoothLE _BluetoothLe;
        private IAdapter _Adapter;

        async void StartButton_Clicked(System.Object sender, System.EventArgs e)
        {

            _BluetoothLe = Plugin.BLE.CrossBluetoothLE.Current;
            _Adapter = _BluetoothLe.Adapter;

            // スキャン時間：５秒
            _Adapter.ScanTimeout = 5000;

            // スキャンでBLEデバイスが見つかった時の処理
            _Adapter.DeviceDiscovered += (s, ev) =>
            {
                // 温度計測処理
                TempMeasure(ev);
            };

            if (_BluetoothLe.State == BluetoothState.Off)
            {
                await DisplayAlert("Warning !", "Bluetoothが無効になっています。", "OK");
                return;
            }

            if (_Adapter.IsScanning)
            {
                await DisplayAlert("Warning !", "Bluetoothが別処理中です", "OK");
                return;
            }

            // タイマー処理開始
            _IsMeasure = true;
            Device.StartTimer(new TimeSpan(0, 1, 0), () =>
            {
                // BLEのスキャン開始
                _Adapter.StartScanningForDevicesAsync();
                return _IsMeasure;
            });
        }
```

StartScanningForDevicesAsyncを呼び出すと、BLEデバイスのスキャンが始まります。
BLEデバイスが見つかると、DeviceDiscoveredに登録した処理が呼び出されます。
ここでは、TempMeasureという関数を呼び出しています。

TempMeasure関数の処理はこんな感じ。

``` MainPage.cs
        private async void TempMeasure(DeviceEventArgs e)
        {
            // 接続対象デバイスのSystem ID
            byte[] targetSystemID = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };

            // Inkbird IBS-TH1シリーズは、デバイス名が「sps」となっている
            if (e.Device.Name != "sps")
            {
                return;
            }

            // デバイス接続
            await _Adapter.ConnectToKnownDeviceAsync(e.Device.Id);
            if (e.Device.State == DeviceState.Disconnected)
            {
                // 接続できなかった場合
                Debug.WriteLine("デバイスと接続できません。Device Id : " + e.Device.Id.ToString());
                return;
            }

            // Device Informationサービスの取得
            var service = await e.Device.GetServiceAsync(new Guid("0000180a-0000-1000-8000-00805f9b34fb"));

            // System IDの取得
            var characteristic = await service.GetCharacteristicAsync(new Guid("00002a23-0000-1000-8000-00805f9b34fb"));

            if (characteristic.CanRead)
            {
                // データ（System ID）の取得
                await characteristic.ReadAsync();

                // 取得対象デバイスかを確認
                if(targetSystemID.SequenceEqual(characteristic.Value))
                {
                    // AdvertisementRecordsから、温度データ取得
                    double Temp = BitConverter.ToInt16(e.Device.AdvertisementRecords[1].Data, 0) / 100.0;
                    Debug.WriteLine("Temp : " + Temp.ToString("0.00"));
                }
            }

            // 切断
            await _Adapter.DisconnectDeviceAsync(e.Device);
        }

```

#ハマったこと
■ Bluetoothを使用したタイミングでアプリが落ちる
iPadで動かした時に、何故かアプリが落ちる現象が・・・。
iPhoneでは動いていたから、コードの問題じゃないだろうと思い、調べてみると、
Plugin.BLEのgithubのissueに、同様の書き込みをしている人が。
[https://github.com/xabre/xamarin-bluetooth-le/issues/387](https://github.com/xabre/xamarin-bluetooth-le/issues/387)

アプリのデプロイ先がiOS 13以降の場合、info.plistに
・NSBluetoothAlwaysUsageDescription
・NSBluetoothPeripheralUsageDescription
を加えなければいけないとのこと。

ということで、こんな感じに追加して再度試すと、無事動きました。
→「プライバシー - Bluetooth 周辺機器の利用状況の説明」と「NSBluetoothAlwaysUsageDescription」の行
![スクリーンショット 2020-05-17 21.18.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/018699f5-3bb0-ef4e-e911-997e6aaa6b4f.png)

■ iOSでは、アドバタイズしてきたデバイスのMACアドレスが取得できない
これはどうやらPlugin.BLEの問題ではなくて、iOS側がMACアドレスを隠蔽しているらしいです。
デバイスの識別にMACアドレスを使用しようと思ってたんですが・・・。

もう一つハマったことがあって、デバイスの識別にMACアドレスが使えないから、
StartScanningForDevicesAsyncで検知された際のID（DeviceEventArgsのDevice.Id）で識別しようとしたら、
なぜか手持ちのiPhoneとiPadで、違うIDが渡ってくる・・・。
このID、どうやって生成されているものなんだろう・・・。

ということで、今回例示しているソースコードでは、
一旦デバイスと接続して、Device InformationサービスのSystem IDを取得し、
自分がターゲットとしているデバイスかどうかを判別しています。



なんか、内容がXamarin.FormsのTipsという感じじゃなくなっちゃったな（汗）。