Windows UWP se connecter à BLE de l'appareil après la découverte

j'utilise BluetoothLEAdvertisementWatcher pour trouver des appareils mobiles à proximité et ça marche bien. Après les avoir trouvées, je veux me connecter et lire/écrire des données via le GATT. Mais je n'arrive pas à trouver comment utiliser L'API après avoir obtenu le 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();
    }
}

j'ai trouvé des échantillons qui utilisent DeviceInformation.FindAllAsync au lieu de BluetoothLEAdvertisementWatcher mais ceux-ci ne fonctionnent pas / ne trouvent pas appareil.

UPDATE

après avoir creusé quelque temps, j'ai trouvé le chemin suivant. Mais malheureusement, l'appariement échoue. L'appareil est juste un Arduino avec un bouclier blable. Je peux me connecter avec Android et iOS. Donc ça doit être possible avec UWP d'une façon ou d'une autre. :/

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);
}

UPDATE #2

je suis maintenant capable de découvrir et de coupler (instable, mais ok pour l'instant), mais

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

lance ce qui suit Exception

Système.IO.FileNotFoundException: Le système ne peut pas trouver le fichier spécifié. (Exception de HRESULT: 0x80070002)

je n'ai aucune idée pourquoi.

16
demandé sur Sven-Michael Stübe 2016-02-16 02:21:49

4 réponses

UPDATE 04/17-CREATORS UPDATE

Microsoft vient de mettre à jour ses API Bluetooth. Nous avons maintenant un appareil de communication non-apparié!

ils ont très peu de documentation pour le moment mais voici la nouvelle structure très simplifiée:

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);
};

bien mieux maintenant. Comme je l'ai dit, il n'y a presque pas de documentation pour le moment, j'ai un problème étrange où mon rappel ValueChanged cesse d'être appelé après 30 secondes ou plus, bien que qui semble avoir un problème de délimitation de l'étendue.

UPDATE 2-SOME WEIRDNESS

après un peu plus de jouer avec la mise à jour des nouveaux créateurs, il y a quelques autres choses à prendre en considération lors de la création de BLEAPPS.

  • vous n'avez plus besoin d'exécuter le truc Bluetooth sur le fil UI. Il ne semble pas y avoir de fenêtres de permissions pour BLE sans appariement donc plus nécessaire de fonctionner sur le fil UI.
  • Vous pouvez constater que votre application cesse de recevoir des mises à jour de l'appareil après un certain temps. Il s'agit d'une question d'établissement de la portée où des objets sont éliminés qui ne devrait pas. Dans le code ci-dessus si vous écoutiez vous pouvez appuyer sur cette question. C'est parce que le GattCharacteristic est disposé avant il se doit, définir le caractère global plutôt que de compter sur la copie.
  • la déconnexion semble un peu cassée. Quitter une application ne met pas fin aux connexions. Comme de telles assurez-vous d'utiliser le App.xml.csOnSuspended callback pour résilier vos connexions. Sinon, vous obtenez dans un peu d'un état étrange où Windows semble maintenir (et continuez à lire!!) le BLE de connexion.

eh Bien, il a ses bizarreries, mais il fonctionne!

ANCIENNE RÉPONSE

suite à la réponse correcte de Jason sur les appareils devant être appariés pour que leurs services soient découverts, voici un exemple de code à adresser ceci:

    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}");
    }

La clé de choses à noter ici:

  • DeviceWatcher a besoin de propriétés ajoutées et mises à jour pour fonctionner.
  • vous devez attraper L'exception FileNotFound qui se produit lors de la tentative d'interroger un service qui n'est pas apparié, ou pas encore prêt.
13
répondu Gerard Wilkinson 2017-04-11 16:38:15

UPDATE (5/5/16): le problème d'erreur" Element Not Found " ne semble se produire que lorsque l'écran Bluetooth settings n'est pas ouvert/Scan. Je ne me souviens pas que ce soit le cas avant 10586.218 mais je n'ai pas vérifié. Évidemment, pas tout le problème est résolu dans la mise à jour.

UPDATE (4/29/16): la mise à jour de windows 10586.218 semble avoir corrigé le problème d'appariement avec un appareil qui n'a jamais été apparié à la machine (ou au téléphone) avant. Processus J'ai souligné ici et le code échantillon de Gerard Wilkinson dans sa réponse devrait fonctionner de façon plus cohérente maintenant.

Si vous êtes assez chanceux pour obtenir que cela fonctionne, il faut attendre un temps considérable pour le pilote à installer. Je l'ai fait en ayant à la fois BluetoothLEAdvertisementWatcher et un DeviceWatcher fonctionnant simultanément.

Enregistrer le DeviceInformation de la BluetoothLEDevice que vous obtenez à partir de FromBluetoothAddressAsync() puis les Disposer() la BluetoothLEDevice avant de commencer l'appariement. Ce qui est important. Si vous ne le faites pas, il ne verra pas les services du Gatt après le jumelage.

alors attendez que le DeviceWatcher voit le dispositif jumelé. Il peut prendre des minutes, mais vous l'obtiendrez généralement avant la barre de progression pour l'installation de l'appareil (dans le panneau de contrôle Bluetooth) obtient à 100%. Si FromIdAsync échoue toujours, cela signifie généralement qu'il y a eu une erreur d'installation du pilote. Vous pouvez dissocier puis effectuez le processus d'appariement encore. Que habituellement fonctionne pour moi.

il est très instable, cependant, et il semble dépendre de quel chipset Bluetooth et pilote la machine a. J'obtiens souvent un élément qui n'a pas trouvé d'erreur avec FromBluetoothAddress mais si il passe là-bas, appariement fonctionne habituellement sur le premier ou le deuxième essai.

PairAsync et UnpairAsync doivent aussi être postés sur le thread de L'interface utilisateur. S'il n'est pas capable de faire apparaître une boîte de dialogue bleue demandant l'autorisation, vous obtiendrez des exceptions. Vous pouvez utiliser Post () à partir d'un sauvegardé UI SynchronizationContext ou Windows.ApplicationModel.Core.CoreApplication.MainView.Répartiteur.RunAsync () avec un délégué async pour le faire.

j'ai vu plusieurs messages d'employés de MS sur les forums disant Frombluetoothadressasync() ne fonctionne que pour les appareils appariés. Ce n'est pas le cas, mais il est buggé et semble mieux fonctionner si le périphérique a été apparié manuellement au moins une fois dans le passé.

5
répondu Jason S. Clary 2016-05-05 21:12:22

la réponse de Gerard Wilkinson est correcte. Pour rendre la vie plus facile, je l'ai transformée en une méthode awaitable en utilisant des Extensions réactives (). Tous les commentaires sont les bienvenus.

ainsi, une fois que vous avez trouvé l'appareil en utilisant le BluetoothLEAdvertisementWatcher et jumelé avec lui, vous pouvez utiliser ceci pour activer 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;
}
3
répondu LanderV 2016-08-19 13:53:10

fondamentalement, vous avez la réponse partiellement inclus dans les questions. En substance, vous utilisez le BluetoothLEAdvertisementWatcher pour trouver les appareils seulement, fondamentalement, ils fonctionnent tout comme des balises.

et vous n'êtes pas censé connecter ces périphériques en utilisant seulement cette API. Pour connecter les appareils, vous devez utiliser DeviceInformation.FindAllAsync(), et de l'obtenir pour vous montrer tous les périphériques, vous devez apparier entre eux d'abord.

de toute façon, si vous êtes intéressé à obtenir des données de quelques caractéristiques spécifiques, vous pouvez essayer d'utiliser GattCharacteristicNotificationTrigger, par exemple complet et un peu d'explications supplémentaires voir mon blog.

0
répondu Dr.Jukka 2016-02-16 07:14:16