Partager via


Client GATT Bluetooth

Cet article montre comment utiliser les API cliente 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 dans l’espace de noms Windows.Devices.Bluetooth.GenericAttributeProfile pour accéder aux appareils Bluetooth LE. Les appareils Bluetooth LE exposent leurs fonctionnalités via 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 contiennent à leur tour des descripteurs qui décrivent les caractéristiques. Ces 3 termes sont appelés génériques les attributs d’un appareil.

Les API BLUETOOTH LE GATT exposent des objets et des fonctions, plutôt que d’accéder 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 un événement ValueChanged caractéristique

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 de consommer 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 BLUETOOTH GATT exposent uniquement les primitives de base requises pour communiquer avec un appareil Bluetooth LE. Pour interpréter les données, un profil d’application doit être défini, soit par un profil standard Bluetooth SIG, soit par un profil personnalisé implémenté par un fournisseur d’appareils. Un profil crée un contrat de liaison entre l’application et l’appareil, quant à ce que les données échangées représentent et comment l’interpréter.

Pour plus de commodité, bluetooth SIG gère une liste de profils publics disponibles.

Exemples

Pour obtenir un exemple complet, consultez l’exemple Bluetooth Low Energy.

Rechercher des appareils à proximité

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

La 2e méthode est abordée en longueur dans la documentation de la publication afin qu’elle ne soit pas abordée beaucoup ici, mais l’idée de base est de trouver l’adresse Bluetooth des appareils proches 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.

À présent, revenez à 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 d’é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 recevrez DeviceInformation pour chaque appareil qui satisfait la requête dans le gestionnaire pour l’événement Ajouté pour les appareils en question. Pour obtenir un aperçu plus détaillé de DeviceWatcher, consultez l’exemple complet sur Github.

Connexion à l’appareil

Une fois qu’un appareil souhaité est découvert, utilisez l’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);
    // ...
}

En revanche, 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 une petite période d’expiration.

bluetoothLeDevice.Dispose();

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

Remarque

La création d’un objet BluetoothLEDevice en appelant cette méthode seul n’initie pas (nécessairement) une connexion. Pour lancer une connexion, définissez GattSession.MaintainConnection true sur , 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. Il n’y a rien pour que votre application attende, 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 un temps fini mais variable. Quoi que ce soit d’instantané à une question de 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’existe aucune autre demande en attente et que l’appareil distant est inaccessible, le système attend sept (7) secondes avant qu’il expire. S’il existe d’autres demandes en attente, chacune des demandes de la file d’attente peut prendre sept (7) secondes pour traiter. Par conséquent, plus la vôtre est vers l’arrière de la file d’attente, plus vous attendez.

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 à suivre consiste à rechercher des services :

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

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

Une fois que le service d’intérêt a é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 utilisant ensuite des opérations.

Effectuer des opérations en 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 distinct de données sur l’appareil. Par exemple, la caractéristique de niveau de la batterie a une valeur qui représente le niveau de batterie de l’appareil.

Lisez les propriétés caractéristiques pour déterminer quelles opérations sont 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 à partir de nombreuses API Bluetooth.

Abonnement aux notifications

Assurez-vous que la caractéristique prend en charge l’option Indiquer ou Notifier (vérifiez les propriétés des caractéristiques pour vous assurer).

Indiquer 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. Notifier est plus répandu, car la plupart des transactions GATT préfèrent conserver la puissance plutôt que d’être extrêmement fiable. Dans tous les cas, tous ceux-ci sont gérés au niveau de la couche contrôleur afin que l’application ne soit pas impliquée. Nous allons les désigner collectivement comme simplement des « notifications ».

Il existe deux éléments à prendre en charge avant d’obtenir des notifications :

  • Écrire dans le descripteur de configuration des caractéristiques du client (CCCD)
  • Gérer l’événement Characteristic.ValueChanged

L’écriture dans le CCCD indique à l’appareil serveur que ce client souhaite connaître chaque fois que cette valeur 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. Tout ce qui reste consiste à 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.
}