Cliente de GATT de BluetoothBluetooth GATT Client

En este artículo se muestra el uso de las API de cliente de atributo genérico (GATT) de Bluetooth para aplicaciones Plataforma universal de Windows (UWP), junto con código de ejemplo para las tareas de cliente de GATT comunes:This article demonstrates usage of the Bluetooth Generic Attribute (GATT) Client APIs for Universal Windows Platform (UWP) apps, along with sample code for common GATT client tasks:

  • Consultar dispositivos cercanosQuery for nearby devices
  • Conectar con el dispositivoConnect to device
  • Enumerar los servicios y las características admitidos del dispositivoEnumerate the supported services and characteristics of the device
  • Leer y escribir en una característicaRead and write to a characteristic
  • Suscribirse a las notificaciones cuando cambia el valor de característicaSubscribe for notifications when characteristic value changes

Importante

Debe declarar la funcionalidad "Bluetooth" en Package. appxmanifest.You must declare the "bluetooth" capability in Package.appxmanifest.

<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>

API importantesImportant APIs

IntroducciónOverview

Los desarrolladores pueden usar las API del espacio de nombres Windows. Devices. Bluetooth. GenericAttributeProfile para tener acceso a dispositivos Bluetooth le.Developers can use the APIs in the Windows.Devices.Bluetooth.GenericAttributeProfile namespace to access Bluetooth LE devices. Los dispositivos Bluetooth LE exponen su funcionalidad a través de una colección de:Bluetooth LE devices expose their functionality through a collection of:

  • ServiciosServices
  • CaracterísticasCharacteristics
  • Descriptores deDescriptors

Los servicios definen el contrato funcional del dispositivo LE y contienen una colección de características que definen el servicio.Services define the functional contract of the LE device and contain a collection of characteristics that define the service. Dichas características, a su vez, contienen descriptores que las describen.Those characteristics, in turn, contain descriptors that describe the characteristics. Estos 3 términos se conocen genéricamente como atributos de un dispositivo.These 3 terms are generically known as the attributes of a device.

Las API de Bluetooth LE GATT exponen objetos y funciones, en lugar de tener acceso al transporte sin procesar.The Bluetooth LE GATT APIs expose objects and functions, rather than access to the raw transport. Las API de GATT también permiten a los desarrolladores trabajar con dispositivos Bluetooth LE con la capacidad de realizar las siguientes tareas:The GATT APIs also enable developers to work with Bluetooth LE devices with the ability to perform the following tasks:

  • Realizar la detección de atributosPerform attribute discovery
  • Leer y escribir valores de atributoRead and Write attribute values
  • Registrar una devolución de llamada para el evento de característica ValueChangedRegister a callback for Characteristic ValueChanged event

Para crear una implementación útil, un desarrollador debe tener un conocimiento previo de los servicios y características de GATT que la aplicación piensa consumir y procesar los valores característicos específicos, de modo que los datos binarios proporcionados por la API se transformen en datos útiles antes de que se presenten al usuario.To create a useful implementation a developer must have prior knowledge of the GATT services and characteristics the application intends to consume and to process the specific characteristic values such that the binary data provided by the API is transformed into useful data before being presented to the user. Las API de Bluetooth GATT exponen solo los primitivos básicos requeridos para comunicarse con un dispositivo Bluetooth LE.The Bluetooth GATT APIs expose only the basic primitives required to communicate with a Bluetooth LE device. Para interpretar los datos, debe definirse un perfil de aplicación, ya sea mediante un perfil estándar de un SIG de Bluetooth o mediante un perfil personalizado implementado por un proveedor de dispositivos.To interpret the data, an application profile must be defined, either by a Bluetooth SIG standard profile, or a custom profile implemented by a device vendor. Un perfil crea un contrato vinculante entre la aplicación y el dispositivo, que indica qué representan los datos intercambiados y cómo interpretarlos.A profile creates a binding contract between the application and the device, as to what the exchanged data represents and how to interpret it.

Para mayor comodidad, el SIG de Bluetooth ofrece una lista de perfiles públicos.For convenience the Bluetooth SIG maintains a list of public profiles available.

Consultar dispositivos cercanosQuery for nearby devices

Hay dos métodos principales para consultar los dispositivos cercanos:There are two main methods to query for nearby devices:

  • DeviceWatcher en Windows. Devices. EnumerationDeviceWatcher in Windows.Devices.Enumeration
  • AdvertisementWatcher en Windows. Devices. Bluetooth. AdvertisementAdvertisementWatcher in Windows.Devices.Bluetooth.Advertisement

El segundo método se describe a largo plazo en la documentación de los anuncios , por lo que no se tratará en gran medida, pero la idea básica es encontrar la dirección de Bluetooth de los dispositivos cercanos que satisfacen el filtro de anunciosdeterminado.The 2nd method is discussed at length in the Advertisement documentation so it won't be discussed much here but the basic idea is to find the Bluetooth address of nearby devices that satisfy the particular Advertisement Filter. Una vez que tenga la dirección, puede llamar a BluetoothLEDevice. FromBluetoothAddressAsync para obtener una referencia al dispositivo.Once you have the address, you can call BluetoothLEDevice.FromBluetoothAddressAsync to get a reference to the device.

Ahora, vuelva al método DeviceWatcher.Now, back to the DeviceWatcher method. Un dispositivo Bluetooth LE es igual que cualquier otro dispositivo de Windows y se puede consultar mediante las API de enumeración.A Bluetooth LE device is just like any other device in Windows and can be queried using the Enumeration APIs. Use la clase DeviceWatcher y pase una cadena de consulta que especifique los dispositivos que se van a buscar:Use the DeviceWatcher class and pass a query string specifying the devices to look for:

// Query for extra properties you want returned
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };

DeviceWatcher deviceWatcher =
            DeviceInformation.CreateWatcher(
                    BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
                    requestedProperties,
                    DeviceInformationKind.AssociationEndpoint);

// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;

// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;

// Start the watcher.
deviceWatcher.Start();

Una vez que haya iniciado la DeviceWatcher, recibirá DeviceInformation para cada dispositivo que cumpla la consulta del controlador para el evento agregado para los dispositivos en cuestión.Once you've started the DeviceWatcher, you will receive DeviceInformation for each device that satisfies the query in the handler for the Added event for the devices in question. Para obtener una visión más detallada de la DeviceWatcher, vea el ejemplo completo en github.For a more detailed look at DeviceWatcher see the complete sample on Github.

Conexión al dispositivoConnecting to the device

Una vez que se detecta un dispositivo deseado, use DeviceInformation.ID para obtener el objeto de dispositivo Bluetooth le para el dispositivo en cuestión:Once a desired device is discovered, use the DeviceInformation.Id to get the Bluetooth LE Device object for the device in question:

async void ConnectDevice(DeviceInformation deviceInfo)
{
    // Note: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent.
    BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
    // ...
}

Por otro lado, la eliminación de todas las referencias a un objeto BluetoothLEDevice para un dispositivo (y si ninguna otra aplicación del sistema tiene una referencia al dispositivo) desencadenará una desconexión automática después de un período de tiempo de espera pequeño.On the other hand, disposing of all references to a BluetoothLEDevice object for a device (and if no other app on the system has a reference to the device) will trigger an automatic disconnect after a small timeout period.

bluetoothLeDevice.Dispose();

Si la aplicación necesita acceder de nuevo al dispositivo, basta con volver a crear el objeto de dispositivo y obtener acceso a una característica (descrita en la sección siguiente) para que el sistema operativo se vuelva a conectar cuando sea necesario.If the app needs to access the device again, simply re-creating the device object and accessing a characteristic (discussed in the next section) will trigger the OS to re-connect when necessary. Si el dispositivo está cerca, obtendrá acceso al dispositivo; de lo contrario, devolverá un error DeviceUnreachable.If the device is nearby, you'll get access to the device otherwise it will return w/ a DeviceUnreachable error.

Nota

La creación de un objeto BluetoothLEDevice llamando solo a este método no inicia (necesariamente) una conexión.Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. Para iniciar una conexión, establezca GattSession. MaintainConnection en true , o llame a un método de detección de servicios no almacenado en caché en BluetoothLEDeviceo realice una operación de lectura/escritura en el dispositivo.To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery method on BluetoothLEDevice, or perform a read/write operation against the device.

  • Si GattSession. MaintainConnection está establecido en true, el sistema espera indefinidamente una conexión y se conecta cuando el dispositivo está disponible.If GattSession.MaintainConnection is set to true, then the system waits indefinitely for a connection, and it will connect when the device is available. No hay nada para que la aplicación espere, ya que GattSession. MaintainConnection es una propiedad.There's nothing for your application to wait on, since GattSession.MaintainConnection is a property.
  • En el caso de las operaciones de detección de servicios y de lectura y escritura en GATT, el sistema espera una hora finita y variable.For service discovery and read/write operations in GATT, the system waits a finite but variable time. Cualquier cosa, desde el instante hasta cuestión de minutos.Anything from instantaneous to a matter of minutes. Factores que informan sobre el tráfico en la pila y cómo se pone en cola la solicitud.Factors inclue the traffic on the stack, and how queued up the request is. Si no hay ninguna otra solicitud pendiente y el dispositivo remoto es inaccesible, el sistema esperará siete (7) segundos antes de que se agote el tiempo de espera. Si hay otras solicitudes pendientes, cada una de las solicitudes de la cola puede tardar siete (7) segundos en procesarse, por lo que la suya más larga es hacia la parte posterior de la cola, lo que más tiempo esperará.If there are no other pending request, and the remote device is unreachable, then the system will wait for seven (7) seconds before it times out. If there are other pending requests, then each of the requests in the queue can take seven (7) seconds to process, so the further yours is toward the back of the queue, the longer you'll wait.

Actualmente, no se puede cancelar el proceso de conexión.Currently, you can't cancel the connection process.

Enumerar los servicios y las características admitidosEnumerating supported services and characteristics

Ahora que tiene un objeto BluetoothLEDevice, el siguiente paso es detectar qué datos expone el dispositivo.Now that you have a BluetoothLEDevice object, the next step is to discover what data the device exposes. El primer paso para hacerlo es consultar los servicios:The first step to do this is to query for services:

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var services = result.Services;
    // ...
}

Una vez identificado el servicio de interés, el siguiente paso es consultar las características.Once the service of interest has been identified, the next step is to query for characteristics.

GattCharacteristicsResult result = await service.GetCharacteristicsAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var characteristics = result.Characteristics;
    // ...
}

El sistema operativo devuelve una lista de solo lectura de objetos GattCharacteristic en los que puede realizar operaciones.The OS returns a ReadOnly list of GattCharacteristic objects that you can then perform operations on.

Realizar operaciones de lectura y escritura en una característicaPerform Read/Write operations on a characteristic

La característica es la unidad fundamental de la comunicación basada en GATT.The characteristic is the fundamental unit of GATT based communication. Contiene un valor que representa un fragmento de datos distinto en el dispositivo.It contains a value that represents a distinct piece of data on the device. Por ejemplo, la característica de nivel de batería tiene un valor que representa el nivel de batería del dispositivo.For example, the battery level characteristic has a value that represents the battery level of the device.

Lea las propiedades de características para determinar qué operaciones se admiten:Read the characteristic properties to determine what operations are supported:

GattCharacteristicProperties properties = characteristic.CharacteristicProperties

if(properties.HasFlag(GattCharacteristicProperties.Read))
{
    // This characteristic supports reading from it.
}
if(properties.HasFlag(GattCharacteristicProperties.Write))
{
    // This characteristic supports writing to it.
}
if(properties.HasFlag(GattCharacteristicProperties.Notify))
{
    // This characteristic supports subscribing to notifications.
}

Si se admite la lectura, puede leer el valor:If read is supported, you can read the value:

GattReadResult result = await selectedCharacteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
    var reader = DataReader.FromBuffer(result.Value);
    byte[] input = new byte[reader.UnconsumedBufferLength];
    reader.ReadBytes(input);
    // Utilize the data as needed
}

La escritura en una característica sigue un patrón similar:Writing to a characteristic follows a similar pattern:

var writer = new DataWriter();
// WriteByte used for simplicity. Other common functions - WriteInt16 and WriteSingle
writer.WriteByte(0x01);

GattCommunicationStatus result = await selectedCharacteristic.WriteValueAsync(writer.DetachBuffer());
if (result == GattCommunicationStatus.Success)
{
    // Successfully wrote to device
}

Sugerencia: DataReader y DataReader son indispensables cuando se trabaja con los búferes sin procesar que se obtienen de muchas de las API de Bluetooth.Tip: DataReader and DataWriter are indispensible when working with the raw buffers you get from many of the Bluetooth APIs.

Suscripción a notificacionesSubscribing for notifications

Asegúrese de que la característica admite indicar o notificar (Compruebe las propiedades de característica para asegurarse).Make sure the characteristic supports either Indicate or Notify (check the characteristic properties to make sure).

Además: indica que se considera más confiable porque cada evento de valor cambiado se acopla con una confirmación del dispositivo cliente.Aside: Indicate is considered more reliable because each value changed event is coupled with an acknowledgement from the client device. La notificación es más frecuente, ya que la mayoría de las transacciones GATT en lugar de ahorrar energía en lugar de ser extremadamente confiable.Notify is more prevalent because most GATT transactions would rather conserve power rather than be extremely reliable. En cualquier caso, todo esto se controla en el nivel de controlador para que la aplicación no se vea afectada.In any case, all of that is handled at the controller layer so the app does not get involved. Nos referiremos colectivamente a ellos simplemente como "notificaciones", pero ahora sabe.We'll collectively refer to them as simply "notifications" but now you know.

Hay dos cosas que debe tener en cuenta antes de recibir las notificaciones:There are two things to take care of before getting notifications:

  • Escribir en el descriptor de configuración de características de cliente (CCCD)Write to Client Characteristic Configuration Descriptor (CCCD)
  • Controlar el evento característico. ValueChangedHandle the Characteristic.ValueChanged event

Al escribir en el CCCD se indica al dispositivo del servidor que este cliente desea conocer cada vez que cambia el valor específico de la característica.Writing to the CCCD tells the Server device that this client wants to know each time that particular characteristic value changes. Para ello, siga estos pasos:To do this:

GattCommunicationStatus status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                        GattClientCharacteristicConfigurationDescriptorValue.Notify);
if(status == GattCommunicationStatus.Success)
{
    // Server has been informed of clients interest.
}

Ahora, se llamará al evento ValueChanged de GattCharacteristic cada vez que se cambie el valor en el dispositivo remoto.Now, the GattCharacteristic's ValueChanged event will get called each time the value gets changed on the remote device. Lo único que queda es implementar el controlador:All that's left is to implement the handler:

characteristic.ValueChanged += Characteristic_ValueChanged;

...

void Characteristic_ValueChanged(GattCharacteristic sender,
                                    GattValueChangedEventArgs args)
{
    // An Indicate or Notify reported that the value has changed.
    var reader = DataReader.FromBuffer(args.CharacteristicValue)
    // Parse the data however required.
}