J'utilise BluetoothLEAdvertisementWatcher
pour rechercher des périphériques BLE à proximité et cela fonctionne bien. Après les avoir trouvées, je souhaite connecter et lire/écrire des données via le GATT. Mais je ne peux pas comprendre comment utiliser l'API après avoir obtenu la 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 exemples qui utilisent DeviceInformation.FindAllAsync
au lieu de BluetoothLEAdvertisementWatcher
mais ceux-ci ne fonctionnent pas/ne trouvent aucun périphérique.
METTRE À JOUR
Après avoir creusé quelque temps, j'ai trouvé le chemin suivant. Mais malheureusement, le jumelage échoue. L'appareil est juste un Arduino avec un bouclier BLE. Je peux certainement me connecter avec Android et iOS. Cela doit donc être possible avec 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);
}
UPDATE # 2
Je suis maintenant capable de découvrir et d'appairer (instable, mais ok pour l'instant), mais
var service = await GattDeviceService.FromIdAsync(args.Id);
lève l'exception suivante
System.IO.FileNotFoundException: Le système ne peut pas trouver le fichier spécifié. (Exception de HRESULT: 0x80070002)
Je ne sais pas pourquoi.
UPDATE 04/17 - CREATORS UPDATE
Microsoft vient de mettre à jour ses API Bluetooth. Nous avons maintenant une communication de périphérique BLE non appariée!
Ils ont très peu de documentation pour le moment, mais voici la nouvelle structure beaucoup plus 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 aucune documentation pour le moment, j'ai un problème étrange dans lequel mon rappel ValueChanged cesse d'être appelé après 30 secondes environ, bien que cela semble être un problème de portée distinct.
MISE À JOUR 2 - QUELQUES ESPRITS
Après quelques essais supplémentaires avec la mise à jour des nouveaux créateurs, vous devez prendre en compte d'autres éléments lors de la création d'applications BLE.
ValueChanged
sur la charac
, vous pouvez vous heurter à ce problème. En effet, la GattCharacteristic
étant supprimée avant qu'elle ne le soit, définissez la caractéristique comme globale plutôt que de la copier.App.xml.cs
OnSuspended
pour mettre fin à vos connexions. Sinon, vous entrez dans un état un peu étrange où Windows semble maintenir (et continuer à lire !!) la connexion BLE.Et bien ça a ses bizarreries mais ça marche!
ANCIENNE REPONSE
Pour faire suite à la réponse correcte de Jason sur les périphériques devant être associés pour que leurs services soient découverts, voici un exemple de code permettant de résoudre ce problème:
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}");
}
Les points clés à noter ici sont:
UPDATE (05/05/16) : Le problème d'erreur "Élément introuvable" ne semble se produire que lorsque l'écran des paramètres Bluetooth n'est pas ouvert/en cours d'analyse. Je ne me souviens pas que c'était le cas avant 10586.218 mais je n'ai pas vérifié. Évidemment, tous les problèmes ne sont pas résolus dans la mise à jour.
UPDATE (4/29/16) : La mise à jour Windows 10586.218 semble avoir résolu le problème de couplage avec un périphérique qui n'avait encore jamais été couplé à la machine (ou au téléphone). Le processus que j'ai décrit ici et l'exemple de code de Gerard Wilkinson dans sa réponse devraient fonctionner de manière plus cohérente maintenant.
Si vous avez la chance de le faire fonctionner, l'installation du pilote nécessite un temps considérable. Je l'ai fait en ayant BluetoothLEAdvertisementWatcher et un DeviceWatcher exécutés simultanément.
Sauvegardez les informations DeviceInformation de BluetoothLEDevice que vous obtenez de FromBluetoothAddressAsync (), puis de Dispose () de BluetoothLEDevice avant de lancer le couplage. C'est important. Sinon, il ne verra pas les services Gatt après le couplage.
Attendez ensuite que DeviceWatcher voit le périphérique couplé. Cela peut prendre quelques minutes, mais vous l'obtiendrez généralement avant que la barre de progression de l'installation du périphérique (dans le panneau de configuration Bluetooth) atteigne 100%. Si FromIdAsync échoue toujours, cela signifie généralement qu’il ya eu une erreur d’installation du pilote. Vous pouvez dissocier, puis recommencer le processus de couplage. Cela fonctionne généralement pour moi.
C'est très instable, cependant, et cela semble dépendre du chipset Bluetooth et du pilote dont dispose la machine. Je reçois souvent une erreur Element Not Found avec FromBluetoothAddress, mais si elle passe au-delà, l'appariement fonctionne généralement au premier ou au deuxième essai.
PairAsync et UnpairAsync doivent également être publiés sur le fil de l'interface utilisateur. S'il ne parvient pas à afficher une boîte de dialogue bleue demandant une autorisation, vous obtiendrez des exceptions. Vous pouvez utiliser Post () à partir d'une interface utilisateur enregistrée SynchronizationContext ou Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync () avec un délégué asynchrone pour le faire.
J'ai vu plusieurs messages d'employés de MS sur les forums disant que FromBluetoothAddressAsync () ne fonctionne que pour les appareils couplés. Ce n’est pas le cas, mais c’est un bug qui semble fonctionner mieux si l’appareil a été couplé manuellement au moins une fois dans le passé.
La réponse de Gerard Wilkinson est correcte. Pour rendre la vie plus facile, je l’ai transformée en une méthode attendue en utilisant Reactive Extensions (). Tous les commentaires sont les bienvenus.
Ainsi, une fois que vous avez trouvé le périphérique utilisant BluetoothLEAdvertisementWatcher et associé à celui-ci, vous pouvez l'utiliser 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;
}
En gros, la réponse est en partie incluse dans les questions. En substance, vous utilisez BluetoothLEAdvertisementWatcher pour rechercher les périphériques uniquement. En gros, ils fonctionnent comme des balises.
Et vous n'êtes pas censé connecter ces périphériques en utilisant uniquement cette API. Pour connecter les périphériques, vous devez utiliser DeviceInformation.FindAllAsync () et pour le faire afficher tous les périphériques, vous devez d'abord les apparier.
Quoi qu'il en soit, si vous souhaitez obtenir des données de certaines caractéristiques BLE spécifiques, vous pouvez essayer d'utiliser GattCharacteristicNotificationTrigger , pour un exemple complet et quelques explications supplémentaires voir mon blog .