Client GATT Bluetooth

Cet article montre comment utiliser les API clientes d’attribut générique Bluetooth (GATT) pour les applications plateforme Windows universelle (UWP).

Important

Vous devez déclarer la fonctionnalité « bluetooth » dans Package.appxmanifest.

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

API importantes

Vue d’ensemble

Les développeurs peuvent utiliser les API de l’espace de noms Windows.Devices.Bluetooth.GenericAttributeProfile pour accéder aux appareils Bluetooth LE. Les appareils Bluetooth LE exposent leurs fonctionnalités par le biais d’une collection de :

  • Services
  • Caractéristiques
  • Descripteurs

Les services définissent le contrat fonctionnel de l’appareil LE et contiennent une collection de caractéristiques qui définissent le service. Ces caractéristiques, à leur tour, contiennent des descripteurs qui décrivent les caractéristiques. Ces 3 termes sont génériques appelés attributs d’un appareil.

Les API GATT Bluetooth LE exposent des objets et des fonctions, plutôt que l’accès au transport brut. Les API GATT permettent également aux développeurs d’utiliser des appareils Bluetooth LE avec la possibilité d’effectuer les tâches suivantes :

  • Effectuer la découverte d’attributs
  • Valeurs d’attribut Lecture et Écriture
  • Inscrire un rappel pour l’événement Characteristic ValueChanged

Pour créer une implémentation utile, un développeur doit avoir une connaissance préalable des services et caractéristiques GATT que l’application a l’intention d’utiliser et de traiter les valeurs caractéristiques spécifiques de sorte que les données binaires fournies par l’API soient transformées en données utiles avant d’être présentées à l’utilisateur. Les API GATT Bluetooth exposent uniquement les primitives de base nécessaires pour communiquer avec un périphérique Bluetooth LE. Pour interpréter les données, un profil d’application doit être défini, soit par un profil Bluetooth SIG standard, soit par un profil personnalisé implémenté par un fournisseur de périphérique. Un profil crée un contrat de liaison entre l’application et le périphérique, qui définit ce que représentent les données échangées et la façon de les interpréter.

Pour des raisons pratiques, le Bluetooth SIG gère une liste de profils publics disponibles.

Exemples

Pour obtenir un exemple complet, consultez Exemple Bluetooth Low Energy.

Requête pour les appareils à proximité

Il existe deux méthodes main pour interroger les appareils à proximité :

La 2e méthode est longuement abordée dans la documentation de la publicité , donc elle ne sera pas beaucoup abordée ici, mais l’idée de base est de trouver l’adresse Bluetooth des appareils à proximité qui répondent au filtre de publicité particulier. Une fois que vous avez l’adresse, vous pouvez appeler BluetoothLEDevice.FromBluetoothAddressAsync pour obtenir une référence à l’appareil.

Revenez maintenant à la méthode DeviceWatcher. Un appareil Bluetooth LE est comme n’importe quel autre appareil dans Windows et peut être interrogé à l’aide des API Énumération. Utilisez la classe DeviceWatcher et transmettez une chaîne de requête spécifiant les appareils à rechercher :

// 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();

Une fois que vous avez démarré DeviceWatcher, vous recevez DeviceInformation pour chaque appareil qui répond à la requête dans le gestionnaire pour l’événement Added pour les appareils en question. Pour plus d’informations sur DeviceWatcher, consultez l’exemple complet sur Github.

Connexion à l’appareil

Une fois qu’un appareil souhaité est découvert, utilisez le DeviceInformation.Id pour obtenir l’objet Appareil Bluetooth LE pour l’appareil en 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);
    // ...
}

D’autre part, la suppression de toutes les références à un objet BluetoothLEDevice pour un appareil (et si aucune autre application sur le système n’a de référence à l’appareil) déclenche une déconnexion automatique après un petit délai d’expiration.

bluetoothLeDevice.Dispose();

Si l’application doit à nouveau accéder à l’appareil, il suffit de recréer l’objet d’appareil et d’accéder à une caractéristique (décrite dans la section suivante) pour déclencher la connexion du système d’exploitation si nécessaire. Si l’appareil se trouve à proximité, vous aurez accès à l’appareil, sinon il retournera une erreur DeviceUnreachable.

Notes

La création d’un objet BluetoothLEDevice en appelant cette méthode seule n’initie pas (nécessairement) une connexion. Pour lancer une connexion, définissez GattSession.MaintainConnectiontruesur , ou appelez une méthode de découverte de service non mise en cache sur BluetoothLEDevice, ou effectuez une opération de lecture/écriture sur l’appareil.

  • Si GattSession.MaintainConnection a la valeur true, le système attend indéfiniment une connexion et se connecte lorsque l’appareil est disponible. Votre application n’a rien à attendre, car GattSession.MaintainConnection est une propriété.
  • Pour les opérations de découverte de service et de lecture/écriture dans GATT, le système attend une durée limitée mais variable. Tout ce qui va de l’instantané à quelques minutes. Les facteurs incluent le trafic sur la pile et la façon dont la demande est mise en file d’attente. S’il n’y a aucune autre demande en attente et que l’appareil distant est inaccessible, le système attendra sept (7) secondes avant d’expirer. S’il y a d’autres demandes en attente, le traitement de chacune des demandes dans la file d’attente peut prendre sept (7) secondes, donc plus vous êtes loin à l’arrière de la file d’attente, plus vous attendrez.

Actuellement, vous ne pouvez pas annuler le processus de connexion.

Énumération des services et caractéristiques pris en charge

Maintenant que vous disposez d’un objet BluetoothLEDevice, l’étape suivante consiste à découvrir les données exposées par l’appareil. La première étape consiste à interroger les services :

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

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

Une fois le service d’intérêt identifié, l’étape suivante consiste à rechercher des caractéristiques.

GattCharacteristicsResult result = await service.GetCharacteristicsAsync();

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

Le système d’exploitation retourne une liste ReadOnly d’objets GattCharacteristic sur lesquels vous pouvez ensuite effectuer des opérations.

Effectuer des opérations de lecture/écriture sur une caractéristique

La caractéristique est l’unité fondamentale de la communication basée sur le GATT. Il contient une valeur qui représente un élément de données distinct sur l’appareil. Par exemple, la caractéristique de niveau de batterie a une valeur qui représente le niveau de batterie de l’appareil.

Lisez les propriétés caractéristiques pour déterminer les opérations prises en charge :

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 la lecture est prise en charge, vous pouvez lire la valeur :

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
}

L’écriture dans une caractéristique suit un modèle similaire :

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
}

Conseil

DataReader et DataWriter sont indispensables lors de l’utilisation des mémoires tampons brutes que vous obtenez de la plupart des API Bluetooth.

Abonnement aux notifications

Assurez-vous que la caractéristique prend en charge l’option Indiquer ou Notifier (case activée les propriétés caractéristiques à vérifier).

Indique est considéré comme plus fiable, car chaque événement de modification de valeur est couplé à un accusé de réception de l’appareil client. La notification est plus répandue, car la plupart des transactions du GATT préféreraient conserver l’énergie plutôt que d’être extrêmement fiables. Dans tous les cas, tout cela est géré au niveau de la couche contrôleur afin que l’application ne soit pas impliquée. Nous les appellerons collectivement simplement « notifications ».

Il y a deux choses à prendre en charge avant d’obtenir des notifications :

  • Écrire dans le descripteur CCCD (Client Characteristic Configuration)
  • Gérer l’événement Characteristic.ValueChanged

L’écriture dans le CCCD indique à l’appareil serveur que ce client souhaite savoir chaque fois que la valeur de caractéristique particulière change. Pour ce faire :

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

À présent, l’événement ValueChanged de GattCharacteristic est appelé chaque fois que la valeur est modifiée sur l’appareil distant. Il ne reste plus qu’à implémenter le gestionnaire :

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.
}