c# - Windows UWP se conecta al dispositivo BLE después del descubrimiento
bluetooth-lowenergy win-universal-app (4)
Básicamente tienes la respuesta parcialmente incluida en las preguntas. En esencia, utiliza BluetoothLEAdvertisementWatcher para encontrar solo los dispositivos, básicamente funcionan como balizas.
Y no se supone que conecte estos dispositivos utilizando solo esta API. Para conectar los dispositivos, debe usar DeviceInformation.FindAllAsync (), y para que le muestre cualquier dispositivo, debe vincularlos primero.
De todos modos, si está interesado en obtener datos de algunas características específicas de BLE, puede intentar usar GattCharacteristicNotificationTrigger , para ver un ejemplo completo y un poco de explicaciones adicionales, consulte mi blog .
Estoy usando BluetoothLEAdvertisementWatcher
para encontrar dispositivos BLE cercanos y está funcionando bien. Después de encontrarlos quiero conectarme y leer / escribir datos a través de GATT. Pero no puedo descubrir cómo usar la API después de obtener el 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();
}
}
He encontrado muestras que utilizan DeviceInformation.FindAllAsync
lugar de BluetoothLEAdvertisementWatcher
pero estas no funcionan / encuentran ningún dispositivo.
ACTUALIZAR
Después de cavar un tiempo, encontré la siguiente manera. Pero desafortunadamente, el emparejamiento falla. El dispositivo es solo un Arduino con un escudo BLE. Definitivamente puedo conectarme con Android y iOS. Así que debe ser posible con UWP de alguna manera. : /
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);
}
ACTUALIZACIÓN # 2
Ahora puedo descubrir y emparejar (inestable, pero está bien por ahora), pero
var service = await GattDeviceService.FromIdAsync(args.Id);
lanza la siguiente excepción
System.IO.FileNotFoundException: el sistema no puede encontrar el archivo especificado. (Excepción de HRESULT: 0x80070002)
No tengo ni idea de por qué.
La respuesta de Gerard Wilkinson es correcta. Para hacer la vida más fácil, la convertí en un método que se puede esperar con las extensiones reactivas (). Cualquier comentario es bienvenido.
Entonces, una vez que haya encontrado el dispositivo utilizando BluetoothLEAdvertisementWatcher y se haya vinculado con él, puede usarlo para habilitar los servicios GATTS.
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;
}
ACTUALIZACIÓN (5/5/16) : El problema de error "Elemento no encontrado" parece ocurrir solo cuando la pantalla de configuración de Bluetooth no está abierta / escaneando. No recuerdo que ese fuera el caso antes del 10586.218, pero no lo he comprobado. Obviamente, no todos los problemas se solucionan en la actualización.
ACTUALIZACIÓN (29/04/16) : la actualización de Windows 10586.218 parece haber solucionado el problema de emparejamiento con un dispositivo que nunca se ha emparejado con la máquina (o el teléfono) anteriormente. El proceso que describí aquí y el código de muestra de Gerard Wilkinson en su respuesta deberían funcionar de manera más consistente ahora.
Si tiene la suerte de hacer que esto funcione, debe esperar un tiempo considerable para que se instale el controlador. Lo he hecho teniendo BluetoothLEAdvertisementWatcher y un DeviceWatcher funcionando simultáneamente.
Guarde la información del dispositivo del dispositivo BluetoothLED que obtiene de FromBluetoothAddressAsync () y luego deseche () el dispositivo BluetoothLED antes de iniciar el emparejamiento. Esto es importante. Si no lo hace, no verá los servicios de Gatt después de la vinculación.
Luego, espere a que DeviceWatcher vea el dispositivo vinculado. Puede tardar unos minutos, pero normalmente lo obtendrá antes de que la barra de progreso para la instalación del dispositivo (en el panel de control de Bluetooth) llegue al 100%. Si FromIdAsync aún falla, generalmente significa que hubo un error de instalación del controlador. Puedes desemparejar y luego hacer el proceso de emparejamiento otra vez. Eso suele funcionar para mí.
Sin embargo, es muy inestable y parece depender de qué chipset y controlador Bluetooth tenga la máquina. A menudo recibo un error de Elemento no encontrado con FromBluetoothAddress pero si se supera, el emparejamiento generalmente funciona en el primer o segundo intento.
PairAsync y UnpairAsync también deben publicarse en el hilo de la interfaz de usuario. Si no puede abrir un cuadro de diálogo azul que solicita autorización, obtendrá excepciones. Puede usar Post () desde un UI SynchronizationContext guardado o Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync () con un delegado asíncrono para hacer esto.
He visto varias publicaciones de empleados de MS en los foros que dicen que FromBluetoothAddressAsync () solo funciona para dispositivos emparejados. Este no es el caso, pero tiene errores y parece funcionar mejor si el dispositivo se ha sincronizado manualmente al menos una vez en el pasado.
ACTUALIZACIÓN 04/17 - ACTUALIZACIÓN DE LOS CREADORES
Microsoft acaba de actualizar sus API de Bluetooth. Ahora tenemos la comunicación del dispositivo BLE no emparejado!
Tienen muy poca documentación en este momento, pero aquí está la nueva estructura mucho más simplificada:
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);
};
Mucho mejor ahora. Como dije, casi no hay documentación en este momento, tengo un problema extraño en el que mi devolución de llamada ValueChanged deja de llamarse después de 30 segundos aproximadamente, aunque parece ser un problema de alcance por separado.
ACTUALIZACIÓN 2 - ALGUNA RIQUEZA
Después de jugar un poco más con la actualización de los nuevos creadores, hay algunas cosas más que se deben tener en cuenta al crear aplicaciones BLE.
- Ya no es necesario ejecutar el contenido de Bluetooth en el subproceso de la interfaz de usuario. No parece haber ninguna ventana de permisos para BLE sin emparejamiento, por lo que ya no es necesario ejecutar en el hilo de la interfaz de usuario.
- Es posible que su aplicación deje de recibir actualizaciones del dispositivo después de un período de tiempo. Este es un problema de alcance en el que se eliminan objetos que no deberían. En el código anterior, si escuchó a
ValueChanged
en el programa, puede encontrar este problema. Esto se debe a que elGattCharacteristic
se elimina antes de que deba ser, establezca la característica como global en lugar de confiar en que se copie en. - Desconectarse parece estar un poco roto. Salir de una aplicación no termina las conexiones. Como tal, asegúrese de usar la devolución de llamada de
App.xml.cs
OnSuspended
para terminar sus conexiones. De lo contrario, se encontrará en un estado un tanto extraño en el que Windows parece mantener (¡y seguir leyendo!) La conexión BLE.
Bueno, tiene sus peculiaridades pero funciona!
ANTIGUA RESPUESTA
A continuación de la respuesta correcta de Jason sobre los dispositivos que necesitan emparejarse para que se descubran sus servicios, aquí hay un código de ejemplo para solucionar esto:
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}");
}
Las cosas clave a tener en cuenta aquí son:
- DeviceWatcher necesita que las propiedades agregadas y actualizadas estén configuradas para funcionar.
- Debe detectar la excepción FileNotFound que se produce al intentar interrogar a un servicio que no está emparejado o que aún no está listo.