Windows UWP在发现后连接到BLE设备

我正在使用BluetoothLEAdvertisementWatcher查找附近的BLE设备,它运行良好。 找到它们后,我想通过GATT连接和读/写数据。 但在获取BluetoothLEAdvertisement后,我无法弄清楚如何使用API​​( https://msdn.microsoft.com/de-de/library/windows/apps/windows.devices.bluetooth.genericattributeprofile )。

 public class Adapter { private readonly BluetoothLEAdvertisementWatcher _bleWatcher = new BluetoothLEAdvertisementWatcher(); public Adapter() { _bleWatcher.Received += BleWatcherOnReceived; } private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args) { // how to connect? // I know, it's the wrong place to to this, but this is just an example } public void StartScanningForDevices(Guid[] serviceUuids) { _blewatcher.advertisementfilter.advertisement.serviceuuids.clear(); foreach (var uuid in serviceuuids) { _blewatcher.advertisementfilter.advertisement.serviceuuids.add(uuid); } _blewatcher.start(); } } 

我发现使用DeviceInformation.FindAllAsync而不是BluetoothLEAdvertisementWatcher样本,但这些样本无法正常工作/查找任何设备。

UPDATE

经过一段时间的挖掘,我找到了以下方法。 但不幸的是,配对失败了。 该设备只是一个带有BLE屏蔽的Arduino。 我绝对可以与Android和iOS连接。 所以UWP必须以某种方式实现。 :/

 private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args) { var dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress); // dev.DeviceInformation.Pairing.CanPair is true // dpr.Status is Failed DevicePairingResult dpr = await dev.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None); var service = await GattDeviceService.FromIdAsync(dev.DeviceInformation.Id); } 

更新#2

我现在能够发现并配对(不稳定,但现在还可以),但是

 var service = await GattDeviceService.FromIdAsync(args.Id); 

抛出以下exception

System.IO.FileNotFoundException:系统找不到指定的文件。 (HRESULTexception:0x80070002)

我不知道为什么。

更新04/17 – 创建者更新

微软刚刚更新了他们的蓝牙API。 我们现在有未配对的BLE设备通讯!

他们目前只有很少的文档,但这是一个简化的新结构:

 BleWatcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active }; BleWatcher.Start(); BleWatcher.Received += async (w, btAdv) => { var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress); Debug.WriteLine($"BLEWATCHER Found: {device.name}"); // SERVICES!! var gatt = await device.GetGattServicesAsync(); Debug.WriteLine($"{device.Name} Services: {gatt.Services.Count}, {gatt.Status}, {gatt.ProtocolError}"); // CHARACTERISTICS!! var characs = await gatt.Services.Single(s => s.Uuid == SAMPLESERVICEUUID).GetCharacteristicsAsync(); var charac = characs.Single(c => c.Uuid == SAMPLECHARACUUID); await charac.WriteValueAsync(SOMEDATA); }; 

现在好多了。 正如我所说,目前没有文档,我有一个奇怪的问题,我的ValueChanged回调在30秒左右后停止被调用,尽管这似乎是一个单独的范围问题。

更新2 – 一些不确定性

在更多关于新创建者更新的内容之后,在构建BLE应用程序时还需要考虑更多的事情。

  • 您不再需要在UI线程上运行蓝牙function。 如果没有配对,BLE似乎没有任何权限窗口,因此不再需要在UI线程上运行。
  • 您可能会发现应用程序在一段时间后停止从设备接收更新。 这是一个范围问题,其中对象被处理,不应该。 在上面的代码中,如果你在ValueChanged上听ValueChanged ,你可能会遇到这个问题。 这是因为GattCharacteristic在应该被处理之前被处理掉,将特征设置为全局而不是依赖于它被复制。
  • 断开连接似乎有点破碎。 退出应用程序不会终止连接。 因此,请确保使用App.xml.cs OnSuspended回调来终止连接。 否则你会进入一个奇怪的状态,Windows似乎维护(并继续阅读!!)BLE连接。

它有它的怪癖,但它的工作原理!

老答复

继Jason关于需要配对以便发现其服务的设备的正确答案之后,这里有一些示例代码来解决这个问题:

  private void SetupBluetooth() { Watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active }; Watcher.Received += DeviceFound; DeviceWatcher = DeviceInformation.CreateWatcher(); DeviceWatcher.Added += DeviceAdded; DeviceWatcher.Updated += DeviceUpdated; StartScanning(); } private void StartScanning() { Watcher.Start(); DeviceWatcher.Start(); } private void StopScanning() { Watcher.Stop(); DeviceWatcher.Stop(); } private async void DeviceFound(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv) { if (_devices.Contains(btAdv.Advertisement.LocalName)) { await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () => { Debug.WriteLine($"---------------------- {btAdv.Advertisement.LocalName} ----------------------"); Debug.WriteLine($"Advertisement Data: {btAdv.Advertisement.ServiceUuids.Count}"); var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress); var result = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None); Debug.WriteLine($"Pairing Result: {result.Status}"); Debug.WriteLine($"Connected Data: {device.GattServices.Count}"); }); } } private async void DeviceAdded(DeviceWatcher watcher, DeviceInformation device) { if (_devices.Contains(device.Name)) { try { var service = await GattDeviceService.FromIdAsync(device.Id); Debug.WriteLine("Opened Service!!"); } catch { Debug.WriteLine("Failed to open service."); } } } private void DeviceUpdated(DeviceWatcher watcher, DeviceInformationUpdate update) { Debug.WriteLine($"Device updated: {update.Id}"); } 

这里要注意的关键事项是:

  • DeviceWatcher需要添加和更新属性才能生效。
  • 您需要捕获尝试询问未配对或尚未准备好的服务时发生的exceptionFileNotFound。

更新(5/5/16) :“找不到元素”错误问题似乎只发生在蓝牙设置屏幕未打开/扫描时。 我不记得10586.218之前的情况,但我没有检查过。 显然,并非每个问题都在更新中得到修复。

更新(2016年4月29日) :10586.218 Windows更新似乎解决了与之前从未与机器(或电话)配对的设备配对的问题。 我在这里概述的过程和Gerard Wilkinson在他的答案中的示例代码现在应该更加一致。

如果您足够幸运能够使用它,则需要等待相当长的时间才能安装驱动程序。 我通过同时运行BluetoothLEAdvertisementWatcher和DeviceWatcher来完成它。

从您从BlueBluetoothAddressAsync()获取的BluetoothLEDevice中保存DeviceInformation,然后在启动配对之前Dispose()BluetoothLEDevice。 这个很重要。 如果不这样做,配对后就不会看到Gatt服务。

然后等待DeviceWatcher查看配对设备。 它可能需要几分钟,但您通常会在设备安装(在蓝牙控制面板中)的进度条达到100%之前得到它。 如果FromIdAsync仍然失败,通常意味着存在驱动程序安装错误。 您可以取消配对,然后重新进行配对过程。 这通常对我有用。

但它非常不稳定,而且似乎取决于机器所具有的蓝牙芯片组和驱动程序。 我经常使用FromBluetoothAddress获得Element Not Found错误,但如果它通过那里,配对通常适用于第一次或第二次尝试。

PairAsync和UnpairAsync也需要发布到UI线程。 如果无法弹出要求授权的蓝色对话框,您将获得例外。 您可以使用已保存的UI SynchronizationContext中的Post()或带有异步委托的Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync()来执行此操作。

我在论坛上看过来自MS员工的多篇post说FromBluetoothAddressAsync()仅适用于配对设备。 情况并非如此,但如果设备已经过去至少手动配对一次,那么它似乎最有效。

Gerard Wilkinson的回答是正确的。 为了让生活更轻松,我使用Reactive Extensions()将其转换为一种等待的方法。 欢迎任何评论。

因此,一旦您找到使用BluetoothLEAdvertisementWatcher并与之配对的设备,您就可以使用它来启用GATTServices。

 private async Task GetGATTServiceAsync(string deviceName) { //devicewatcher is abused to trigger connection var deviceWatcher = DeviceInformation.CreateWatcher(); //trick to enable GATT var addedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Added)) .Select(pattern => ((DeviceInformation)pattern.EventArgs)); var updatedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Updated)) .Select(pattern => { var update = ((DeviceInformationUpdate)pattern.EventArgs); return Observable.FromAsync(() => DeviceInformation.CreateFromIdAsync(update.Id).AsTask()); }).Concat(); var source = addedSource.Merge(updatedSource); source.Publish().Connect(); //make sure the event handlers are attached before starting the device watcher deviceWatcher.Start(); var result = await source.Where(di => di.Name == deviceName) //find the relevant device .Select(di => Observable.FromAsync(() => GattDeviceService.FromIdAsync(di.Id).AsTask())) //get all services from the device .Concat() //necessary because of the async method in the previous statement .Where(service => service.Uuid == SERVICE_UUID) //get the service with the right UUID .Retry() //GattDeviceService.FromIdAsync can throw exceptions .FirstAsync(); deviceWatcher.Stop(); return result; } 

基本上你的答案部分包含在问题中。 实质上,您只使用BluetoothLEAdvertisementWatcher来查找设备,基本上它们就像信标一样工作。

并且您不会仅使用此API来连接这些设备。 要连接设备,您必须使用DeviceInformation.FindAllAsync(),并让它向您显示任何设备,您需要先将它们配对。

无论如何,如果您对从某些特定BLE特征中获取数据感兴趣,可以尝试使用GattCharacteristicNotificationTrigger ,完整示例和一些其他解释请参阅我的博客 。