概要
今、温度センサーを使って、配達物の温度監視をする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を入れる
Plugin.BLEを使う
共通プロジェクトで画面を作成し、開始ボタンがクリックされたら、
BLE通信の処理を開始するようにしています。
今回はタイマー処理で、1分間に1度温度センサーからデータを取得するようにしています。
まずはボタンがクリックされた時の処理
private IBluetoothLE _BluetoothLe;
private IAdapter _Adapter;
async void StartButton_Clicked(System.Object sender, System.EventArgs e)
{
_BluetoothLe = Plugin.BLE.CrossBluetoothLE.Current;
_Adapter = _BluetoothLe.Adapter;
// スキャン時間:5秒
_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関数の処理はこんな感じ。
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
アプリのデプロイ先がiOS 13以降の場合、info.plistに
・NSBluetoothAlwaysUsageDescription
・NSBluetoothPeripheralUsageDescription
を加えなければいけないとのこと。
ということで、こんな感じに追加して再度試すと、無事動きました。
→「プライバシー - Bluetooth 周辺機器の利用状況の説明」と「NSBluetoothAlwaysUsageDescription」の行
■ iOSでは、アドバタイズしてきたデバイスのMACアドレスが取得できない
これはどうやらPlugin.BLEの問題ではなくて、iOS側がMACアドレスを隠蔽しているらしいです。
デバイスの識別にMACアドレスを使用しようと思ってたんですが・・・。
もう一つハマったことがあって、デバイスの識別にMACアドレスが使えないから、
StartScanningForDevicesAsyncで検知された際のID(DeviceEventArgsのDevice.Id)で識別しようとしたら、
なぜか手持ちのiPhoneとiPadで、違うIDが渡ってくる・・・。
このID、どうやって生成されているものなんだろう・・・。
ということで、今回例示しているソースコードでは、
一旦デバイスと接続して、Device InformationサービスのSystem IDを取得し、
自分がターゲットとしているデバイスかどうかを判別しています。
なんか、内容がXamarin.FormsのTipsという感じじゃなくなっちゃったな(汗)。