Windows UWP подключается к устройству BLE после обнаружения

Я использую BluetoothLEAdvertisementWatcher чтобы найти близлежащие устройства BLE, и он работает хорошо. После их поиска я хочу подключиться и читать/записывать данные через GATT. Но я не могу понять, как использовать API после получения BluetoothLEAdvertisement (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 но они не работают / не находят устройство.

обновление

покопавшись некоторое время, я нашел следующий способ. Но, к сожалению, спаривание не удается. Устройство - это просто Arduino с экраном BLE. Я определенно могу подключиться к 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);

выдает следующую Исключение

4 ответов


ОБНОВЛЕНИЕ 04/17-CREATORS UPDATE

Microsoft только что обновили свои API Bluetooth. Теперь у нас есть непарная связь устройства 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-НЕКОТОРЫЕ СТРАННОСТИ

после еще нескольких игр с новым обновлением creators есть еще несколько вещей, которые следует учитывать при создании приложений BLE.

  • вам больше не нужно запускать материал Bluetooth в потоке пользовательского интерфейса. Кажется, что нет никаких окон разрешений для BLE без сопряжения, поэтому больше не нужно запускать в потоке пользовательского интерфейса.
  • вы можете обнаружить, что ваш приложение перестает получать обновления с устройства по истечении определенного периода времени. Это проблема области, где объекты удаляются, которые не должны. В коде выше, если вы слушали ValueChanged на charac вы можете нажать эту проблему. Это потому что GattCharacteristic утилизируется до того, как он должен быть, установите характеристику как глобальную, а не полагаться на ее копирование.
  • отключение, кажется, немного сломанный. Выход из приложения не прекращает соединения. Как такие убедитесь, что вы используете App.xml.cs OnSuspended обратный вызов для прекращения ваших соединений. В противном случае вы попадаете в странное состояние, где Windows сохраняет (и продолжаю читать!!) в Бле связи.

Ну, у него есть свои причуды, но он работает!

ОТВЕТ

после правильного ответа Джейсона об устройствах, нуждающихся в сопряжении, чтобы их службы были обнаружены, вот пример кода для адреса это:

    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 должен добавленные и обновленные свойства приступил к работе.
  • вам нужно поймать исключение FileNotFound, которое возникает при попытке опросить службу, которая не сопряжена или еще не готова.

обновление (5/5/16): проблема с ошибкой "элемент не найден" возникает только тогда, когда экран настроек bluetooth не открыт/сканирование. Я не помню, чтобы это было до 10586.218, но я не проверял. Очевидно, что не каждая проблема исправлена в обновлении.

обновление (4/29/16): Центр обновления 10586.218 Windows, по-видимому, исправил проблему сопряжения с устройством, которое никогда не было сопряжено с машиной (или телефоном) раньше. Процесс Я изложил здесь, и пример кода Джерарда Уилкинсона в его ответе должен работать более последовательно.

Если вам посчастливилось заставить это работать,для установки драйвера требуется значительное время. Я сделал это, имея как BluetoothLEAdvertisementWatcher и DeviceWatcher работает одновременно.

сохраните DeviceInformation из BluetoothLEDevice, который вы получаете от FromBluetoothAddressAsync (), затем Dispose() BluetoothLEDevice перед началом сопряжения. Это важно. Если вы этого не сделаете, он не увидит службы Gatt после сопряжения.

затем дождитесь DeviceWatcher, чтобы увидеть сопряженное устройство. Это может занять несколько минут, но вы обычно получите его до того, как индикатор выполнения установки устройства (на панели управления Bluetooth) достигнет 100%. Если FromIdAsync все еще терпит неудачу, это обычно означает, что произошла ошибка установки драйвера. Вы можете отменить сопряжение, а затем выполнить процесс сопряжения снова. Что обычно работает на меня.

Это очень неустойчиво, хотя, и, похоже, зависит от того, какой набор микросхем Bluetooth и драйвер имеет машина. Я часто получаю элемент не найден ошибка с FromBluetoothAddress, но если он проходит там, сопряжение обычно работает с первой или второй попытки.

PairAsync и UnpairAsync также должны быть размещены в потоке пользовательского интерфейса. Если он не может открыть синий диалог с запросом авторизации, вы получите исключения. Вы можете использовать Post () из a сохраненный UI SynchronizationContext или Windows.ApplicationModel.Ядро.Метод coreapplication.Представлении MainView.Диспетчер.RunAsync () с асинхронным делегатом для этого.

Я видел несколько сообщений от сотрудников MS на форумах, говорящих, что FromBluetoothAddressAsync () работает только для сопряженных устройств. Это не так, но он глючит и, кажется, работает лучше, если устройство было сопряжено вручную хотя бы один раз в прошлом.


ответ Джерарда Уилкинсона правильный. Чтобы облегчить жизнь, я превратил его в ожидаемый метод, используя Reactive Extensions (). Любые комментарии приветствуются.

таким образом, как только вы нашли устройство с помощью BluetoothLEAdvertisementWatcher и в паре с ним, вы можете использовать это для включения GATTServices.

private async Task<GattDeviceService> 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, для полного примера и немного дополнительных объяснений смотрите мой блог.