Concevoir une interface BLE GATT : UUID, formats, notifications et compatibilité
Une connexion BLE peut réussir alors que l’intégration du produit échoue. L’application peut découvrir la mauvaise caractéristique, décoder incorrectement une mesure signée ou ne plus recevoir de mises à jour après une évolution du firmware. Une interface GATT exploitable exige un contrat écrit couvrant l’identité, les octets, la sémantique de livraison, la sécurité et les changements de version. Ce guide établit ce contrat pour une interface personnalisée de mesure et de commande.
Définir l’interface avant de développer l’application
Documentez, pour chaque service et caractéristique, l’UUID, les propriétés, les permissions, la longueur maximale et les réponses d’erreur. Réutilisez un service Bluetooth SIG uniquement lorsque sa signification et son format correspondent au produit. Pour une sémantique propriétaire, attribuez un UUID stable sur 128 bits plutôt que d’inventer un UUID court non attribué. La spécification des types de données du SIG réserve les UUID de service courts aux valeurs attribuées par le SIG.
Conservez les UUID dans un registre sous gestion de versions, partagé entre firmware, application et outils de test. Ne prenez pas le nom du périphérique comme seul identifiant : il peut changer et plusieurs unités peuvent annoncer le même nom. De même, ne codez pas en dur les handles d’attributs d’une version du firmware à l’autre. Découvrez le service, sélectionnez l’instance voulue, puis recherchez les caractéristiques dans ce service.
| Caractéristique | Opération suggérée | Contrat à documenter |
|---|---|---|
| Informations de protocole | Lecture | Versions majeure et mineure du format, capacités et trame applicative maximale |
| Flux de mesures | Notification | Unités, compteur de séquence, référence temporelle et politique de débordement |
| Configuration | Lecture et écriture avec réponse | Plages, autorisation, validation et moment de la mémorisation persistante |
| Résultat de commande | Indication ou notification avec accusé de réception applicatif | Identifiant de transaction, code résultat et sens de l’achèvement |
Spécifier un format d’octets testable
Voici un exemple de trame de mesure de 12 octets, et non un profil Bluetooth normalisé. Tous les champs multioctets sont en petit-boutiste. Une température en virgule fixe évite de dépendre de la sérialisation des nombres flottants d’un langage. Une lecture séparée des informations de protocole indique la version mineure et les capacités prises en charge.
| Décalage | Champ | Définition |
|---|---|---|
| 0 | major | Version majeure du format sur 8 bits non signés, initialement 1 |
| 1 | flags | Bit 0 : température valide ; autres bits réservés et à zéro |
| 2–3 | sequence | Compteur non signé sur 16 bits, modulo 65536 |
| 4–7 | uptime_ms | Instant d’acquisition depuis le démarrage sur 32 bits non signés ; rebouclage modulo 2³² |
| 8–9 | temperature | 16 bits signés, 0,01 °C par unité ; ignoré si invalide |
| 10–11 | battery_mV | Millivolts sur 16 bits non signés ; 65535 signifie indisponible |
Dans cet exemple, le numéro de séquence et la durée depuis le démarrage sont remis à zéro au redémarrage. Un identifiant de démarrage lu à l’établissement de la session permet de distinguer un redémarrage d’un rebouclage du compteur. Températures négatives, indicateurs invalides, trames tronquées et rebouclages doivent figurer dans les vecteurs de référence communs. Ce décodeur Python illustre une validation explicite :
import struct
def decode_v1(payload):
if len(payload) != 12:
raise ValueError("expected 12 bytes")
major, flags, seq, ms, temp, mv = struct.unpack("<BBHIhH", payload)
if major != 1 or flags & 0xFE:
raise ValueError("unsupported schema or flags")
return {
"sequence": seq, "uptime_ms": ms,
"temperature_C": temp / 100 if flags & 1 else None,
"battery_mV": None if mv == 65535 else mv,
}
Un vecteur de référence est 01 01 2A 00 E8 03 00 00 2E FB E4 0C : version majeure 1, température valide, séquence 42, durée 1000 ms, −12,34 °C et 3300 mV. Décodez-le des deux côtés avant de connecter le matériel réel.
La règle stricte sur les bits réservés signifie que ce décodeur ne peut pas accepter silencieusement de nouvelles significations des indicateurs. Conservez la trame v1, négociez une extension prise en charge ou introduisez un nouveau format majeur. Ajouter des octets n’est rétrocompatible que si l’analyseur existant a été expressément conçu pour les accepter.

Distinguer l’abonnement de la livraison applicative fiable
Les notifications n’ont pas de confirmation ATT ; les indications en ont une. Aucune ne prouve qu’une mesure a atteint une base de données cloud ou qu’un moteur a terminé une commande. La liaison BLE connectée possède ses propres mécanismes de retransmission, mais déconnexions, files logicielles pleines et redémarrages de l’application exigent toujours une politique de bout en bout. Utilisez des numéros de séquence pour détecter les échantillons manquants et des identifiants de transaction pour associer les résultats aux commandes. La spécification ATT définit les procédures sous-jacentes.
Abonnez-vous par l’API de la plateforme et vérifiez l’achèvement avant de déclarer le flux prêt. Au niveau GATT, le descripteur Client Characteristic Configuration, UUID 0x2902, active les notifications avec 0x0001 et les indications avec 0x0002. L’état d’abonnement est propre à chaque client. La persistance diffère selon que les appareils ont conservé une association de sécurité ou non : la reconnexion doit donc rétablir le callback local et vérifier que l’abonnement est actif. Voir la spécification GATT.
Définissez une file bornée et une réponse documentée à la surcharge : supprimer les échantillons les plus anciens, ne conserver que le plus récent ou suspendre l’acquisition si le produit le permet. Exposez un compteur d’échantillons supprimés. Pour les commandes, distinguez « acceptée », « en cours » et « terminée ». Réessayer la même transaction après une déconnexion ne doit pas déclencher accidentellement un actionneur deux fois.
Rendre explicites la MTU et les différences entre plateformes
Pour une notification classique à un seul handle, la valeur doit tenir dans ATT_MTU moins 3 octets ; la limite de la valeur d’attribut est par ailleurs de 512 octets. Une MTU ATT LE par défaut de 23 laisse donc 20 octets pour cette notification. Les valeurs plus longues nécessitent une fragmentation applicative convenue ou une autre procédure de transfert adaptée. Augmenter la MTU ne sélectionne pas automatiquement un PHY plus rapide et n’augmente pas la longueur de données de la couche liaison. Voir les formats de paquets ATT.
À partir d’Android 14, la première demande de MTU d’un client GATT conduit la pile à demander 517, et les demandes ultérieures sont ignorées. Dimensionnez les trames à partir du résultat négocié reçu dans le callback, jamais de la valeur demandée. Sur les plateformes Apple, interrogez maximumWriteValueLength(for:) pour le type d’écriture choisi au lieu de reprendre les hypothèses Android. Consultez la référence Android BluetoothGatt et la référence Apple sur la longueur d’écriture.
Exécutez les étapes de configuration asynchrones en série, sauf si la pile choisie prend explicitement en charge la concurrence souhaitée. Un appel réussi signifie souvent que la demande a été mise en file ; le résultat arrive dans le callback. Journalisez ensemble la MTU négociée, l’UUID de la caractéristique sélectionnée, l’état de sécurité, le résultat de l’abonnement et la version de l’analyseur.
Prévoir la compatibilité entre versions du firmware et de l’application
La version du format de protocole, celle du firmware et la structure de la base GATT sont des sujets distincts. Maintenez le sens des champs existants dans chaque format pris en charge. Exposez des capacités plutôt que d’obliger le client à déduire les fonctions d’une chaîne de version du firmware. Refusez une version majeure non prise en charge avec un diagnostic utile avant d’envoyer des commandes de contrôle.
Si une mise à jour modifie la base de services, implémentez le comportement Service Changed et les règles de cache applicables. Database Hash permet de détecter les changements lorsqu’il est disponible. Ces mécanismes ne traduisent pas les formats applicatifs modifiés. Testez mise à jour et retour arrière avec des clients déjà associés ainsi qu’avec des installations neuves. Les règles de cache GATT expliquent les exigences au niveau de la base.
Utiliser une matrice de validation qui révèle les défauts
| Test | Défaut à provoquer | Preuve à conserver |
|---|---|---|
| Ancienne application avec nouveau firmware ; nouvelle application avec ancien firmware | Version majeure inconnue, champ facultatif absent ou structure modifiée | Lecture des capacités et acceptation ou rejet explicite |
| MTU 23 et MTU négociée supérieure | Charge trop longue, longueur de fragment incorrecte ou troncature | Longueur reçue et vecteur de référence décodé |
| Déconnexion pendant une commande et un abonnement | Double actionnement ou flux silencieux | Identifiant de transaction, achèvement de l’abonnement et premier échantillon valide |
| Mise à jour et retour arrière avec associations conservées | Cache de handles obsolète ou accès à la mauvaise caractéristique | Transition découverte/cache et correspondance des UUID |
| Consommateur lent et file pleine | Croissance mémoire ou pertes inexpliquées | Niveau maximal de la file et comptage des pertes |
| Client non authentifié ou non autorisé | Configuration acceptée au mauvais niveau de sécurité | Erreur attendue et configuration inchangée |
Si le flux reste silencieux, vérifiez dans l’ordre la propriété Notify, l’achèvement de l’abonnement, les permissions et l’activité du producteur. Si les valeurs sont incohérentes, comparez les octets bruts au signe, au facteur d’échelle et à l’ordre des octets avant de modifier les paramètres radio. Si seuls les appareils mis à jour échouent, examinez d’abord le cache de la base et la négociation du format.
Préparer un dossier d’intégration vérifiable
Pour la revue du firmware, apportez le registre des UUID, la spécification des paquets, les vecteurs de référence, la liste des téléphones et systèmes pris en charge, ainsi que la matrice de compatibilité. Le service de diagnostic firmware et BSP d’Obeita constitue un point de départ adapté. La cas d’intégration livré du module radio WS63 illustre le contexte de définition du matériel et du mode radio ; elle ne démontre pas que cet exemple de contrat GATT a été implémenté ou validé sur ce module.