
BLE au microscope (ATTы GATTы…)
Partie 1, aperçu
Beaucoup de temps s'est déjà écoulé depuis la publication de la première spécification pour Bluetooth 4.0. Et, bien que le sujet BLE soit très intéressant, il rebute encore de nombreux développeurs en raison de sa complexité. Dans mes articles précédents, j'ai principalement examiné le niveau le plus bas, la couche liaison et la couche physique. Cela nous a permis d'éviter d'avoir à recourir à des concepts aussi complexes et déroutants que le Protocole d'Attributs (ATT) et le Profil d'Attributs Général (GATT). Cependant, il n'y a nulle part où aller, sans les comprendre, il est impossible de développer des appareils compatibles. Aujourd’hui, j’aimerais partager ces connaissances avec vous. Dans mon article, je m'appuierai sur pour les débutants sur le site Nordic. Alors, commençons.
Pourquoi tout est-il si difficile ?
À mon avis, il est immédiatement apparu que la gestion des appareils via les smartphones est un sujet très prometteur et de longue durée. Ils ont donc décidé de le structurer immédiatement et au maximum. Pour que les fabricants de divers gadgets n'inventent pas leurs propres protocoles, qui seraient alors incompatibles. D'où la difficulté. Dès la première étape, ils ont essayé d'intégrer tout leur possible dans le protocole BLE. Et peu importe que cela soit utile plus tard ou non. De plus, ils prévoyaient la possibilité d'élargir la liste des appareils pour l'avenir.
Jetons un coup d'œil à l'image où est dessiné le diagramme du protocole BLE. Il se compose de plusieurs couches. La couche physique la plus basse (PHY) est responsable du canal radio de l'appareil. Link Layer (LL) contient la séquence entière d’octets dans le message transmis. Dans les articles précédents, nous avons étudié exactement cela. L'interface du contrôleur hôte (HCI) est un protocole d'échange entre des couches ou des puces BLE si le contrôleur et l'hôte sont implémentés sur des puces différentes. Le protocole L2CAP (Logical Link Control and Adaptation Protocol) est responsable de la formation, du tramage, du contrôle des erreurs et de l'assemblage des paquets. Security Manager Protocol (SMP) est responsable du chiffrement des paquets. Le profil d'accès général (GAP) est responsable de l'échange initial de données entre les appareils afin de déterminer « Qui est qui ». Cela comprend également la numérisation et la publicité. Dans cet article, je me concentrerai sur les deux parties restantes du protocole : le GATT et le TCA. Le GATT est une superstructure du TCA, ils sont donc étroitement liés.

Pour simplifier l’histoire, je voudrais recourir à une analogie. Je l'ai entendu quelque part et j'aimerais le soutenir. Considérez un appareil BLE comme une bibliothèque avec plusieurs étagères. Chaque étagère est un thème distinct. Par exemple, nous avons des étagères contenant de la science-fiction, des mathématiques et des encyclopédies. Sur chaque étagère se trouvent des livres sur un sujet précis. Et certains livres contiennent même des marque-pages en papier avec des notes. De plus, nous disposons d'un petit catalogue papier de tous les livres. Si vous vous en souvenez bien, les bibliothèques scolaires sont une boîte étroite remplie de cartes papier. Avec cette analogie, le cabinet est le profil de notre appareil. Les étagères sont des services, les livres sont des caractéristiques et le catalogue est une table attributaire. Les signets dans les livres sont des descripteurs, dont je parlerai également plus en détail plus tard.
Quiconque a développé des appareils sait que de nombreux projets comportent des morceaux de code similaires. Le fait est que de nombreux appareils ont des fonctionnalités similaires. Par exemple, si les appareils sont alimentés par des batteries, le problème de la charge et de la surveillance de leur niveau sera le même. Il en va de même pour les capteurs. En fait, une approche de programmation orientée objet « offre la possibilité de créer des objets qui combinent des propriétés et des comportements dans une union autonome qui peut ensuite être réutilisée ». À mon avis, BLE a tenté une approche similaire. Les profils ont été développés par le Bluetooth Special Interest Group (SIG). Les appareils de différents fabricants ayant les mêmes profils devraient fonctionner les uns avec les autres sans difficulté. Les profils, quant à eux, sont constitués de services et de services de caractéristiques, complétés par des descripteurs. En général, cela pourrait ressembler à ceci :

Par exemple, considérons le schéma de profil d'un moniteur de fréquence cardiaque (bracelet de fitness). Il se compose de deux services et de plusieurs caractéristiques. La hiérarchie des profils devient immédiatement claire. La caractéristique du point de contrôle remet à zéro le nombre total de dépenses caloriques.
1. Le service de fréquence cardiaque comprend trois caractéristiques (0x180D) :
a) Caractéristique de fréquence cardiaque obligatoire (0x2A37)
b) Caractéristique de position du capteur corporel en option (0x2A38)
c) Caractéristiques conditionnelles du point de contrôle de la fréquence cardiaque (0x2A39)
2. Service d'entretien de la batterie (0x180F) :
a) Caractéristique obligatoire du niveau de charge de la batterie (0x2A19)
UUID
Afin que nous puissions accéder de manière unique aux éléments du profil (services, caractéristiques et descripteurs), nous devons tous les numéroter d'une manière ou d'une autre. À cette fin, un concept tel que Universally Unique ID (UUID) ou Universally Unique Identifier est introduit. L'UUID est indiqué entre parenthèses de chaque ligne. Et il y a ici une particularité. Pour l'UUID, nous avons décidé d'utiliser un code de 16 et 128 bits de longueur. Pourquoi demandes-tu? Dans le protocole BLE, tout est question d’économie d’énergie. La dimension de 16 bits est donc tout à fait raisonnable. Il est peu probable que plus de 65 XNUMX personnes soient créées dans un avenir proche. services et caractéristiques uniques. Pour le moment, tout ce qu'ils pouvaient déjà être compté (rappelez-vous d'où cela vient - "il t'a compté aussi" :-)) Éléments numérotés , , и vous pouvez regarder les liens.
Cependant, je pense que tout le monde se souvient de l’histoire des 4 octets d’adresses IP sur Internet. Au début, nous pensions que cela suffisait, mais maintenant nous ne pouvons toujours pas passer à une adresse à 6 octets. Afin de ne pas répéter cette erreur et de laisser libre cours aux mains ludiques des bricoleurs, SIG a immédiatement décidé d'introduire des UUID 128 bits. Cela me rappelle personnellement la bande 433 MHz sans licence, qui a été donnée à toutes sortes de Kulibins depuis le canal radio. Dans notre cas, un identifiant de 128 bits de services et de caractéristiques a été sous-traité. Cela signifie que nous pouvons, pour nos services et appareils, utiliser presque n'importe quelle valeur 128 bits. Néanmoins, la probabilité d'obtenir le même UUID tend vers zéro.
En fait, les UUID courts de 16 bits ont leur extension à une valeur de 128 bits. Dans la spécification, cette extension est appelée Bluetooth Base UUID et a la valeur 00000000-0000-1000-8000-00805F9B34FB. Si, par exemple, l'UUID d'attribut 16 bits a la valeur 0x1234, alors l'UUID équivalent 128 bits aura la valeur 00001234-0000-1000-8000-00805F9B34FB. Et même la formule correspondante est donnée :
128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID
Je ne sais pas d'où vient ce nombre magique. Si l'un des lecteurs le sait, laissez-le écrire dans les commentaires (un utilisateur avec le pseudo Sinopteek l'a déjà fait. Voir les commentaires). Quant à la création d'UUID 128 bits, vous pouvez en principe utiliser un qui le fera pour vous.
ATTy GATTy...
En fait, c'est alors que le plaisir commence. Je vous rappelle qu'ATT repose sur une relation client-serveur. Nous examinons maintenant le périphérique serveur. Il contient des informations telles que les valeurs des capteurs, l'état de l'interrupteur d'éclairage, les données de localisation, etc. Maintenant que tous les « participants à notre défilé » sont numérotés, nous devons les placer d'une manière ou d'une autre dans la mémoire de l'appareil. Pour ce faire, nous les mettons dans une table appelée table attributaire. Rappelez-vous bien cela. C'est le cœur même du BLE. C'est ce que nous considérerons plus loin. Nous allons maintenant appeler chaque ligne un attribut. Cette table est située au plus profond de la pile et, en règle générale, nous n'y avons pas d'accès direct. Nous l’initialisons et y accédons, mais ce qui se passe à l’intérieur nous est caché derrière sept sceaux.
Regardons l'image de la spécification, mais avant cela, je voudrais immédiatement attirer l'attention sur la confusion fréquente des termes, notamment des descripteurs. Le rôle du descripteur est de compléter la description de la caractéristique. Lorsqu'il est nécessaire d'étendre ses capacités, des descripteurs sont utilisés. Ce sont aussi des attributs, et tout comme les services et les caractéristiques, ils se trouvent dans la table attributaire. Nous les examinerons en détail dans la deuxième partie de l'article. Cependant, les descripteurs font parfois référence au numéro de ligne dans la table attributaire. Il faut garder cela à l'esprit. Pour éviter toute confusion, nous utiliserons le terme « pointeur d’attribut » à ces fins.

Un attribut est donc une valeur discrète à laquelle sont associées les propriétés suivantes :
1. Le handle d'attribut est l'index de table correspondant à l'attribut
2. Le type d'attribut est un UUID qui décrit son type
3. La valeur de l'attribut est les données indexées par le pointeur d'attribut
4. Les autorisations d'attribut sont la partie d'un attribut, les autorisations, qui ne peuvent pas être lues ou écrites à l'aide du protocole d'attribut.
Comment comprendre tout cela ? Le pointeur d'attribut est, relativement parlant, son numéro dans notre tableau.
Il permet à un client de référencer un attribut dans des requêtes de lecture ou d'écriture. Nous pouvons numéroter nos lignes (attributs) de 0x0001 à 0xFFFF. Dans notre association avec la bibliothèque, il s'agit du numéro de fiche dans le catalogue papier. De même, comme dans le catalogue de la bibliothèque, les fiches sont classées par ordre croissant de nombre. Le numéro de chaque ligne suivante doit être supérieur au précédent. Tout comme à la bibliothèque, il arrive parfois que certaines cartes se perdent, donc chez nous, il peut y avoir des trous dans la numérotation des lignes. Ceci est autorisé. L'essentiel est qu'ils avancent progressivement.
Le type d'attribut détermine ce que l'attribut représente. Par analogie avec le langage C,
où il y a des variables booléennes, numériques et des chaînes, c'est donc ici. Par type d'attribut nous reconnaissons
à quoi nous sommes confrontés et comment nous pouvons continuer à travailler avec cet attribut. Ci-dessous, nous examinerons certains types spécifiques d’attributs. Par exemple, « déclaration de service » (0x2800), « déclaration de caractéristique » (0x2803), « déclaration de descripteur » (0x2902).
La valeur d’un attribut est sa signification réelle, pardonnez la tautologie. Si le type d'attribut est une chaîne, alors la valeur de l'attribut peut être, par exemple, le slogan « Hello World !!! ». Si le type d'attribut est une « déclaration de service », alors sa valeur est le service lui-même. Et parfois, il s'agit d'informations sur l'endroit où trouver d'autres attributs et leurs propriétés.
Les autorisations d'attribut permettent au serveur de comprendre si l'accès en lecture ou en écriture est autorisé.
Notez que ces autorisations s'appliquent uniquement à la valeur de l'attribut, et non au pointeur, au type ou au champ d'autorisation lui-même. Ceux. si l'enregistrement des attributs est autorisé, nous pouvons alors modifier, par exemple, la ligne "Hello World !!!" à la ligne « Bonjour ». Mais nous ne pouvons pas interdire d'écrire une nouvelle ligne ou changer le type d'attribut et désigner la ligne comme une « déclaration de service ». Lorsqu'un client contacte un serveur, le client demande ses attributs. Cela permet au client de savoir ce que le serveur peut fournir. Bien qu'il ne soit pas nécessaire de lire et d'écrire les valeurs.
À quoi ça ressemble
Le concept du GATT est de regrouper les attributs dans une table d’attributs dans un ordre très spécifique et logique. Examinons de plus près le profil de fréquence cardiaque ci-dessous. La colonne la plus à gauche de ce tableau est facultative. Il nous décrit simplement ce qu'est cette ligne (attribut). Toutes les autres colonnes nous sont déjà familières.

En haut de chaque groupe, nous avons toujours un attribut de déclaration de service. Son type est toujours 0x2800 et le pointeur dépend du nombre d'attributs déjà présents dans la table. Ses autorisations sont toujours en lecture seule, sans aucune authentification ni autorisation. Nous parlerons de ces concepts un peu plus tard. La valeur est un autre UUID qui identifie le service. Dans le tableau, la valeur est 0x180D, définie par Bluetooth SIG comme service de fréquence cardiaque.
Suite à l'annonce du service, vient l'annonce de la caractéristique. Sa forme est similaire à celle d'une déclaration de service. Son UUID est toujours 0x2803 et ses autorisations sont toujours en lecture seule sans aucune authentification ni autorisation. Examinons le champ Valeur d'attribut, qui comprend certaines données. Il contient toujours un pointeur, un UUID et un ensemble de propriétés. Ces trois éléments décrivent la déclaration ultérieure de la valeur caractéristique. Le pointeur indique naturellement l'emplacement de la déclaration de la valeur caractéristique dans la table attributaire. L'UUID décrit le type d'informations ou de valeur auquel nous pouvons nous attendre. Par exemple, la valeur de la température, l'état de l'interrupteur d'éclairage ou toute autre valeur arbitraire. Et enfin les propriétés, qui décrivent comment interagir avec la valeur caractéristique.
Un autre écueil nous attend ici. Il est associé aux autorisations d'attribut et aux propriétés caractéristiques. Regardons l'image des propriétés du champ de bits de la spécification.

Comme vous pouvez le voir, il existe également ici des champs qui offrent des capacités de lecture et d'écriture. Vous vous demandez peut-être pourquoi nous avons des autorisations de lecture/écriture pour les attributs et les propriétés.
lire/écrire pour la valeur caractéristique ? Ne devraient-ils pas toujours être les mêmes ? Le fait est que les propriétés de la valeur caractéristique ne sont en réalité que des recommandations destinées au client utilisées dans les couches GATT et application. Ce ne sont que des indications sur ce que le client peut attendre de l'attribut de déclaration de caractéristique. Regardons cela plus en détail. De quels types d’autorisations dispose un attribut ?
1. Autorisations d'accès :
- en train de lire
- enregistrer
- lire et écrire
2. Autorisation d'authentification :
- Authentification requise
- aucune authentification requise
3. Autorisation d'autorisation :
- Autorisation requise
- aucune autorisation requise
La principale différence entre la résolution d'attribut et les propriétés caractéristiques est que la première s'applique aux serveurs et la seconde aux clients. Le serveur peut être autorisé à lire la valeur caractéristique, mais peut nécessiter une authentification ou une autorisation. Par conséquent, lorsque le client demande les propriétés de la caractéristique, nous recevrons que la lecture est autorisée. Mais lorsque nous essayons de lire, nous obtenons une erreur. Par conséquent, nous pouvons parler en toute sécurité de la priorité des autorisations sur les propriétés. Du côté client, nous ne pouvons pas connaître les autorisations dont dispose un attribut.
Descripteur
Revenons à notre table. Après avoir déclaré la valeur d'une caractéristique, les déclarations d'attributs suivantes sont possibles :
1. Nouvelle déclaration de caractéristiques (un service peut avoir plusieurs caractéristiques)
2. Nouvelle déclaration de service (il peut y en avoir plusieurs dans le tableau)
3. Déclarer un handle
Dans le cas de la caractéristique de mesure de la fréquence cardiaque, dans notre tableau, la déclaration de la valeur caractéristique est accompagnée de la déclaration du descripteur. Un descripteur est un attribut contenant des informations supplémentaires sur une caractéristique. Il existe plusieurs types de descripteurs. Nous en parlerons en détail dans la deuxième partie de cet article. Pour l'instant, nous n'aborderons que le descripteur de configuration des caractéristiques du client (CCCD). Son UUID est égal à 0x2902. Grâce à ce descripteur, le client a la possibilité d'activer l'indication ou la notification sur le serveur. La différence entre eux est minime, mais elle existe toujours. La notification ne nécessite pas d'accusé de réception de la part du client. L'indication l'exige, même si elle se produit au niveau du GATT et n'atteint pas le niveau des demandes. Pourquoi, demandez-vous ? Hélas, je ne le sais pas. Permettez-moi simplement de dire que les experts nordiques recommandent d'utiliser les notifications. De plus, la vérification de l'intégrité du package (à l'aide de CRC) a lieu dans les deux cas.
Conclusion
À la fin de l'article, je voudrais dire ceci. Le dernier tableau est un peu déroutant. Cependant, je l'ai choisi parce qu'il est donné en , sur lequel je compte. Dans la deuxième partie de mon article, j'ai l'intention d'approfondir la spécification BlueTooth 4.0. Des schémas et des dessins plus corrects nous y attendent. Dans la troisième partie, je voudrais analyser le journal obtenu à l'aide du programme Wireshark à partir de l'un des gadgets et voir « en direct » toute la théorie que nous étudions.
Employé du Groupe de Sociétés
Petcherskikh Vladimir
Source: habr.com
