Depuis sa sortie en 2010, le Bluetooth Low Energy s’affirme dans le domaine des protocoles de communications pour objets connectés. Cet article s’intéresse au fonctionnement de ce protocole et de son intégration dans un système embarqué sous Linux avec Bluez.
Sommaire
Introduction : le protocole Bluetooth
[caption id="attachment_4615" align="alignnone" width="150"] Logo Bluetooth[/caption]
Le Bluetooth est un standard de communication radio opérant à courte distance. Son objectif est d'unifier les appareils électroniques autour d'un protocole commun et sans-fil. Le nom "Bluetooth" et son logo font référence au roi danois Harald Bluetooth qui avait su unifier les tribus de son peuple au sein d'un unique royaume. Le fabricant Ericsson est à l'origine du standard créé en 1994 et embarqué sur téléphone en 1996. Plusieurs grandes sociétés ont ensuite rejoint Ericsson pour former le Bluetooth Special Interest Group (SIG). Ce groupe s'occupe de l'évolution de la norme et la distribution des licenses aux fabricants. Le standard a évolué en plusieurs versions listées ci-dessous :
Version | Date | Améliorations majeures |
---|---|---|
1.0 | 1999 | |
2.0 | 2004 | Enhanced Data Rate, débit jusqu'à 2.1 Mbps |
2.1 | 2007 | Appairage sécurisé |
3.0 | 2009 | |
4.0 | 2010 | Low Energy |
4.1 | 2013 | Amélioration du Low Energy |
4.2 | 2014 | Amélioration de la sécurtié du Low Energy |
5.0 | 2016 | Amélioration de la portée et débit radio |
La version 4.0 du standard dévoile la spécification Bluetooth Low Energy qui évoluera jusqu'à la version 5.0. Cette spécification implique des changements importants sur les différentes couches du standard. Ce document se focalisera sur cette nouvelle spécification. La version 5.0, sortie en décembre 2016, apporte des améliorations considérables sur la portée et le débit du Bluetooth. Les principales nouveautés sont :
- Slot Availability Mask (SAM) : Transmission entre deux appareils de leurs créneaux disponibles pour l'émission et la réception de données
- 2 Msymboles/s sur la couche physique du BLE (x2)
- Augmentation de la portée BLE
- High Duty Cycle Non-Connectable Advertising
- Extension de l'advertising BLE
- Nouvel algorithme de sélection de canaux BLE
Dernièrement, le 13 juillet 2017, la spécification Bluetooth Mesh a été dévoilée par la Bluetooth SIG et permettra de créer des réseaux maillés entre appareils Bluetooth. La spécification a un grand potentiel pour la domotique et l'industrie car elle pourra unifier tous les appareils sous un même protocole.
Le Bluetooth Low Energy
Comme expliqué plus haut, le Bluetooth Low Energy apparaît avec le version 4.0 des spécifications Bluetooth. Bien que s’inspirant de l’architecture du Bluetooth Classic, le Low Energy est à voir comme un nouveau protocole, adapté à des systèmes contraints en énergie et ne nécessitant pas un débit élevé. Il a été conçu pour faire communiquer un système contraint - que l’on nomme périphérique - avec un système moins ou non contraint en énergie et en puissance de calcul, nommé le central.
Principes de base
Le standard est basé sur la spécification IEEE 802.15.1 qui définit les couches physiques et liaisons de modèle OSI pour des communications WPAN/Bluetooth. Le Bluetooth opère dans la bande de fréquence ISM 2.4 GHz ouverte sans licence à l'industrie, à la science et au médical. La plupart des appareils Bluetooth utilisent une puissance radio de 1 mW ce qui leur donne une portée de 5 à 10 m. Le standard permet de mettre en place des topologies de réseau en étoile ou maillé. Il faut noter que le réseau maillé est une possibilité, mais qu’elle n’est pas encore spécifiée. Des liens s'établissent entre un maître et des esclaves. Ces liens forment un piconet, nom du réseau selon la spécification Bluetooth. Un appareil Bluetooth ne peut être le maître que d'un seul piconet mais peut être l'esclave de plusieurs. Généralement, avec le Bluetooth Low Energy, le maître est un ordinateur ou un smartphone - nommé central - et l’esclave un objet connecté nommé périphérique.
[caption id="attachment_4617" align="aligncenter" width="558"] Source : Spécification Bluetooth Core 5.0[/caption]
Le Bluetooth spécifie des profils qui effectuent des opérations haut niveau entre le maître et l'esclave. Les plus connus sont l'Advanced Audio Distribution Profile (A2DP) pour le streaming audio ou le Dial-up Networking Profile (DUN) pour le partage de connexion internet par Bluetooth. Une liste des profils standards est disponible sur ce lien. Le standard impose deux profils par défaut pour le Bluetooth Low Energy (BLE), le Generic Access Profile (GAP) et le Generic Attribute Profile (GATT), qui seront décrits plus loin. Les périphériques utilisent l’advertising afin de signaler leur existence : des paquets sont envoyés sur des fréquences spécifiques écoutées par un central. Ces paquets contiennent des données fournissant des informations comme les services apportés par le périphérique ou son nom. Dans le cas d’une communication sans connexion (broadcast), les données sont transmises par les paquets d’advertising. Dans le cas d’une communication avec connexion, le central va initier la connexion avec le périphérique en lui dirigeant une requête sur les mêmes fréquences. Les deux appareils vont alors se synchroniser pour communiquer.
Architecture du Bluetooth Low Energy
[caption id="attachment_4618" align="aligncenter" width="636"] Pile du Bluetooth Low Energy[/caption]
Le schéma ci-dessus représente l’architecture du Bluetooth Low Energy. La partie blanche, bas niveau, est généralement implémentée dans un contrôleur. La partie haut niveau en gris foncé est implémentée à part dans un système plus puissant. Le Host Controller Interface permet d’interfacer ces deux éléments. Il est à savoir que la pile entière peut être implémentée entièrement dans le même microcontrôleur sans utiliser cette interface.
Couche Physique
[caption id="attachment_4619" align="aligncenter" width="800"] Source : Argenox[/caption]
Le Bluetooth Low Energy opère sur la bande de fréquences 2.4 GHz et utilise une modulation Gaussian Frequency Shift Keying (GFSK) ainsi que des séquences de Frequency Hopping afin d'éviter les interférences. Cette bande est découpée en 40 canaux numérotés de largeur 1 MHz séparés de 2 MHz. Trois d'entre eux - les 37, 38 et 39 - servent de canaux d'advertising primaire. Ceux-ci ont été placés sur des bandes de fréquences peu utilisées par les autres protocoles comme le Wi-Fi. Cela réduit le risque d'interférence et permet donc de réduire la fréquence des collisions des données d'advertising avec d'autres émissions radio. Les autres canaux servent à la donnée ou en canaux secondaires pour l'advertising.
Couche Liaison
[caption id="attachment_4624" align="aligncenter" width="336"] Machine d’état de la couche Liaison[/caption]
Comme expliqué brièvement plus haut, le processus de connexion se base sur un périphérique en mode advertising et un central en mode scanning. Le central envoie alors une requête de connexion par les canaux d’advertising qui lui donne un “rendez-vous” sur un canal de données spécifié et à un instant donné. Le périphérique et le central vont alors pouvoir communiquer durant ce qui est appelé une fenêtre de transmission (Transmit window) où la prochaine fenêtre sera définie et des données optionnellement transmises et ainsi de suite jusqu'à la déconnexion.
Host Controller Interface
[caption id="attachment_4623" align="aligncenter" width="433"] Séparation de l’hôte et du contrôleur[/caption]
Le Host Controller Interface (HCI) est un format de communication standardisé entre un système hôte et un contrôleur Bluetooth. En effet, la spécification Bluetooth permet de scinder son implémentation en un contrôleur qui gère les couches Physique et Liaison et un hôte qui gère les couches supérieures. Cela permet d’avoir une interopérabilité entre les différents composants Bluetooth. Cette communication peut être effectuée entre quatre types de transport :
- USB : Permet par exemple l’existence des Dongle Bluetooth
- UART 1 Wire
- UART 3 Wires
- SDIO
Le HCI permet à la fois la configuration de son contrôleur, la création d’une connexion, la réception et la transmission de données sur la couche physique.
La couche Transport
Le contrôle de flux, la fragmentation et le réassemblage des données sont assurés par le Logical Link Control and Adaptation Layer Protocol (L2CAP). Celui-ci utilise un système de canaux associés à une application : GATT ou IP sur BLE par exemple. Après l'établissement d'une connexion par la couche liaison, le maître peut se connecter aux canaux écoutés par l'esclave. Chaque canal gère indépendamment le flux, la fragmentation et le réassemblage. Le L2CAP gère aussi la qualité du service (QoS) afin de prioriser certaines applications sur d’autres. Le L2CAP peut être considéré comme la base pour la création de nouvelles fonctionnalités pour le Bluetooth. L’IETF a, par exemple, décrit la possibilité de transmettre des paquets en 6LoWPAN à travers le canal IP du L2CAP (cf. Sources en fin d'article).
Generic Access Profile
Le GAP est le profil de base autant pour le Bluetooth Classic que pour le Low Energy. Il définit les prérequis minimum d’un appareil Bluetooth. Avec le Low Energy, il permet d’accéder aux fonctionnalités de paramétrage, d’advertising, de scanning et connexion du contrôleur. Il définit aussi le rôle de l’appareil : Broadcaster, Observer, Peripheral ou Central. En BLE, le GAP impose l’existence du GATT dans la pile logicielle de l’appareil Bluetooth. Ainsi, tout dispositif dit Bluetooth Low Energy implémente obligatoirement ce profil.
Generic Attribute Protocol
[caption id="attachment_4620" align="aligncenter" width="481"] Hiérarchie du GATT[/caption]
Le GATT se base sur le format de données de l’Attribute Protocol (ATT) pour fournir une sorte de base de donnée. Le GATT utilise un système d'interaction serveur-client entre les deux appareils connectés. Le serveur est hébergé sur le périphérique qui met à disposition du central des informations (température, humidité, etc) rangées en services et caractéristiques. Certains services et certaines caractéristiques sont prédéfinis par la spécification Bluetooth comme les services Battery Service ou Heart Rate. Les caractéristiques sont des valeurs représentant une donnée quelconque associée à des métadonnées, dites descripteurs, définissant des permissions et d'autres informations. Celles-ci sont regroupées en services dont l’identifiant peut être intégré à la donnée d’advertising. Ainsi, un smartphone pourra se connecter automatiquement à un compteur de pulsation cardiaque juste en reconnaissant l'identifiant de ce service dans la trame d'advertising. Les caractéristiques peuvent, en fonction des permissions, être lues, écrites ou peuvent notifier ou indiquer au central un changement de valeur. La notification permet l’envoi direct de la nouvelle valeur au central alors que l’indication lui signale que celle-ci est prête à être lue. Ce système permet une économie d’énergie, car le central n’a plus besoin de requêter régulièrement le serveur GATT.
Intégration sur Linux
[caption id="attachment_4625" align="aligncenter" width="320"] Architecture de bluez[/caption]
BlueZ est la pile Bluetooth officielle de Linux sous licence GNU GPL. Elle s’appuie sur des composants logiciels en espace noyau et utilisateur. BlueZ est intégré au noyau Linux à partir de la version 2.4. La partie noyau contient les pilotes HCI et la couche Transport de la spécification Bluetooth : le L2CAP, expliqué plus haut, et le RFCOMM, un protocole émulant une liaison série via Bluetooth. Le noyau met à disposition des sockets pour interagir avec le bus HCI, le L2CAP ou le RFCOMM. Les profils Bluetooth sont implémentés en espace utilisateur dans un service appelé bluetoothd à l’exception du profil OBEX (transfert de fichiers) qui a son propre service obexd. Le service bluetoothd fournit une interface D-Bus pour créer ou interagir avec des profils.
Utilisation de bluetoothd
Le service bluetoothd implémente tous les profils de la spécification : A2DP, GATT, etc. Il est possible de désactiver certains profils inutilisés par le système avant de compiler pour alléger l’empreinte mémoire de bluetoothd. L’interaction avec le service s’effectue via une interface D-Bus. Il est possible, à l’aide de cette interface, d’accéder à toutes les fonctionnalités nécessaires pour ajouter des profils, découvrir des périphériques, interagir avec le profil GATT, etc.
Ce qui nous intéresse dans le cas du Bluetooth Low Energy, c’est la capacité à scanner, se connecter, s’appairer et à utiliser le profil GATT. Cette section explique comment utiliser l’interface D-Bus pour du Bluetooth Low Energy. La connaissance de D-Bus est prérequise pour la bonne compréhension des explications qui suivent. Ces explications se basent sur la documentation de BlueZ.
Vue générale
[caption id="attachment_4621" align="aligncenter" width="310"] Capture sur d-feet du bus org.bluez[/caption]
L’interface D-Bus est accessible via le bus org.bluez. BlueZ présente plusieurs types d’objets accessibles via les chemins d’accès suivants :
- / : L’objet root permet l’accès aux interfaces standards ObjetsManager et Introspectable.
- /org/bluez : Permet d’ajouter un profil Bluetooth ou configurer le système d’appairage de BlueZ.
- /org/bluez/adapter : Représente un contrôleur Bluetooth nommé sous la forme hci0, hci1, etc.
- /org/bluez/adapter/device : Représente un périphérique Bluetooth découvert nommé sous la forme dev_XX_XX_XX_XX_XX
- /org/bluez/adapter/device/service : Service GATT
- /org/bluez/adapter/device/service/characteristic : Caractéristique GATT
- /org/bluez/adapter/device/service/characteristic/descriptor : Descripteur GATT
BlueZ permet d’utiliser le Bluetooth Classic et Low Energy. L’API peut prêter à confusion car elle est homogénéisée et il est donc difficile de savoir si l’on utilise l’un ou l’autre. Afin de clarifier les choses, les sections suivantes mettent en avant les fonctionnalités utilisées pour le Low Energy.
Adaptateurs
Dans les objets adapters, on retrouve plusieurs interfaces :
- Adapter1 : Fournit les contrôles pour lancer/arrêter la découverte de périphériques. Présente aussi les propriétés du contrôleur.
- GattManager1 : Permet soit une connexion automatisée du contrôleur à des périphériques en fonction des services fournis, soit de générer un client GATT.
- Media1 : Utilisé pour l’A2DP ou les kits mains libres.
- NetworkServer1 : Utilisé pour se connecter à internet via le Bluetooth.
- Introspectable : Permet une introspection de l’objet.
- Properties : Permet de lire et d’écrire des propriétés.
L’interface Adapter1 est essentielle pour l’utilisation du Low Energy et en voici les éléments principaux :
- Powered : Cette propriété est utilisée pour activer/désactiver le contrôleur. La méthode Set de l’interface Properties permet de changer l’état du contrôleur. Le signal PropertiesChanged sera envoyé par bluetoothd lorsque cet état aura été changé.
- StartDiscovery() : Cette méthode permet d’activer la découverte de périphérique. La propriété Discovering passe à true lorsque la découverte a été activée. Lorsqu’un périphérique est découvert, l’interface ObjetsManager envoie le signal InterfacesAdded. Il faut garder en tête que certains périphériques ont pu être découverts auparavant ou enregistrés par BlueZ. Dans ce cas, ces périphériques sont retrouvés grâce à la méthode GetManagedObjets() qui permet d’obtenir toute l’arborescence du bus org.bluez.
- SetDiscoveryFilters(filters) : Il est possible d’appliquer des filtres sur les périphériques à découvrir. Quatre paramètres sont disponibles : Services fournis, transport, RSSI et pathloss. Dans notre cas, on peut être intéressé pour restreindre le transport au Low Energy.
- StopDiscovery() : Cette méthode stoppe la découverte de périphérique.
Périphériques
Dans les objets périphériques, on retrouve ces interfaces :
- Device1 : Représente le périphérique.
- Introspectable : Permet une introspection de l’objet.
- Properties : Permet de lire et d’écrire des propriétés.
Dans l’interface Device1, ces éléments sont utilisés :
- Connect() : Le contrôleur lance une connexion sur ce périphérique. Le signal PropertiesChanged est envoyé sur la propriété Connected si la connexion a abouti. Les périphériques connectés sont enregistrés par bluetoothd dans la liste des objets, qu’ils soient disponibles ou non. Il est possible de retirer un périphérique avec la méthode RemoveDevice().
- Disconnect() : Le contrôleur se déconnecte du périphérique.
- Pair() : Le contrôleur lance un appairage. La connexion se sécurise alors en fonction les capacités du master et du slave. BlueZ utilise un système d’agent que l’on peut configurer dans /org/bluez en utilisant l’interface AgentManager1. Un agent par défaut est fourni et répond à la plupart des cas d’application. Utiliser un agent personnalisé peut servir si l’on souhaite créer son propre wizard d’appairage. Enfin, la propriété Paired passe à true si l’appairage a réussi.
- ServicesResolved : La connexion à un périphérique Low Energy est automatiquement suivie par les découvertes de sa hierachie GATT. Lorsque cette propriété passe à true, après que la connexion soit établie, tous les services, caractéristiques et descripteurs du périphérique ont été découverts et ajoutés au bus. Il faut savoir que ceux-ci sont placés en cache pour les prochaines connexions.
- Name : Nom du périphérique.
- AdvertisingFlags: Flags d’advertising du périphérique.
- UUIDS : Services fournis par le périphérique.
Profil GATT
bluetoothd implémente le profil GATT client et serveur. L’utilisation d’un système Linux en tant que serveur GATT est moins courant qu’en tant que client, mais celle-ci est nécessaire pour la certification du Bluetooth SIG. Lorsque le contrôleur se connecte à un périphérique, il découvre tous ses services, caractéristiques et descripteurs. Les objets associés sont créés sur le bus, fournissant les interfaces GattService1, GattCharacteristic1 et GattDescriptor1
- GattService1 : Contient l’UUID du service ainsi que la liste des services qu’il inclus
- GattCharacteristic1 : L’interface de caractéristique est plus complexe. Elle permet d’écrire (WriteValue()), de lire (ReadValue()), ou de souscrire aux notifications et indications (StartNotify()/StopNotify()). Les propriétés donnent accès à la liste des usages avec Flags. Value représente la valeur de la caractérisque qui change une fois l’écriture effectuée ou une indication/notification reçue. Ce changement de valeur peut bien évidemment être récupéré par le signal PropertiesChanged.
- GattDescriptor1 : Cette interface permet de la même façon que la caractéristique, de lire et d’écrire la valeur en respectant les propriétés du descripteur données dans Flags.
Utilisation des sockets
bluetoothd peut être considéré comme trop volumineux pour certains cas d’applications simples. Il est important de rappeler que ce service est dépendant de la glib et de libdbus. Il est alors possible d’utiliser les sockets qui permettent de s’interfacer avec la pile Bluetooth du noyau en espace utilisateur. Il est alors nécessaire d’implémenter soi-même ses propres profils et applications. Le noyau fournit trois sockets :
- Socket HCI : Permet d’interagir avec le contrôleur en utilisation directement le protocole HCI.
- Socket MGMT : Permet le contrôle par plusieurs processus du contrôleur Bluetooth.
- Socket L2CAP : Permet l’envoi de données via le L2CAP implémenté dans le noyau.
Le dossier src/shared du dépôt Git de BlueZ donne accès aux composants logiciels développés pour bluetoothd. Le code est sous licence LGPL (et non GPL) ce qui permet l'utilisation dans une application sous licence non libre. Certaines bibliothèque en NodeJs, Go ou d’autres langages implémentent directement toute la pile logicielle du Bluetooth Low Energy sur le HCI pour le portage multiplateforme. Le choix du socket est à associer au nombre de processus utilisant le contrôleur et à la volonté ou non de porter la solution en dehors de Linux.
Socket HCI
Les sockets HCI sont utilisés pour interagir directement avec le contrôleur. Le noyau s’occupe de la gestion du transport HCI et échange les paquets HCI entre le socket et ce transport. Il existe deux types de canaux pour ce socket : raw channel et user channel. Le mode "user channel" permet un accès exclusif au bus HCI alors que le "raw channel" permet à toute application en espace utilisateur d’accéder en écriture.
En C, les fonctions socket et bind sont utilisées pour ouvrir la socket. Le code suivant montre comment :
int hci_create(void) { struct sockaddr_hci addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) return -errno; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; // HCI_CHANNEL_RAW or HCI_CHANNEL_USER addr.hci_channel = HCI_CHANNEL_USER; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int err = -errno; close(fd); return err; } return fd; }
Socket MGMT
Le MGMT est une interface de management du Bluetooth propre au noyau Linux depuis la version 3.4. Il permet d’effectuer des opérations de pilotage du contrôleur : des fonctionnalités du GAP comme l’appairage ou la connexion. Sa création a été motivée par la volonté que plusieurs applications en espace utilisateur puissent utiliser le contrôleur en même temps. L’interface est documentée dans le fichier doc/mgmt-api.txt du dépôt Git de bluez.
int mgmt_create(void) { struct sockaddr_hci addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) return -errno; memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = HCI_DEV_NONE; addr.hci_channel = HCI_CHANNEL_CONTROL; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int err = -errno; close(fd); return err; } return fd; }
Socket L2CAP
La couche L2CAP est implémentée dans le noyau. Le code ci-dessous montre comment connecter deux L2CAP, l'un local (ex : hci0) et l'autre distant (ex: un périphérique découvert). Il faut bien comprendre que ce sont les L2CAP qui se connectent et non pas les contrôleurs. Si la connexion au niveau de la couche liaison n'est pas établie, la connexion L2CAP échouera. Les sockets L2CAP s’utilisent donc en complément du MGMT. Des exemples d'utilisation du L2CAP peuvent être trouvés dans le dossier tools du dépôt Git comme l2ping ou l2test.
// localdevice : local device (hci0, hci1, ...) bluetooth address as string. ex : "AA:DE:AD:BE:EF" // remotedevice : remote device bluetooth address int l2cap_create(const char* localdevice, const char* remotedevice) { struct sockaddr_l2 addr; int sk; /* Create socket */ sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); if (sk < 0) return -errno; /* Bind to local address */ memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; str2ba(localdevice, &addr.l2_bdaddr); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) return -errno; /* Connect to remote device */ memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; str2ba(remotedevice, &addr.l2_bdaddr); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) return -errno; return sk; }
Conclusion
Le Bluetooth Low Energy est un protocole complexe qui évolue constamment pour apporter des solutions au plus grand nombre de cas d'usage possible. La présence du standard dans les différents secteurs de l'industrie démontre son intérêt. Son implémentation logicielle dans Linux est complète malgré une architecture logicielle qui peut prêter à confusion. Les ressources pour comprendre le fonctionnement de BlueZ sont présentes mais peu explicites. Cet article avait pour objectif de réduire la barrière d'appréhension de BlueZ.