Une introduction à WebBluetooth
Publié: 2022-03-10Avec les Progressive Web Apps, le Web se rapproche de plus en plus des applications natives. Cependant, avec les avantages supplémentaires inhérents au Web, tels que la confidentialité et la compatibilité multiplateforme.
Le Web a traditionnellement été fantastique pour parler aux serveurs du réseau, et aux serveurs sur Internet en particulier. Maintenant que le Web évolue vers les applications, nous avons également besoin des mêmes fonctionnalités que les applications natives.
La quantité de nouvelles spécifications et fonctionnalités qui ont été implémentées au cours des dernières années dans les navigateurs est stupéfiante. Nous avons des spécifications pour traiter la 3D comme WebGL et le futur WebGPU. Nous pouvons diffuser et générer de l'audio, regarder des vidéos et utiliser la webcam comme périphérique d'entrée. Nous pouvons également exécuter du code à des vitesses presque natives en utilisant WebAssembly. De plus, bien qu'il s'agisse initialement d'un média uniquement réseau, le Web s'est déplacé vers un support hors ligne avec des travailleurs de service.
C'est formidable et tout, mais un domaine a été presque le domaine exclusif des applications natives : la communication avec les appareils. C'est un problème que nous essayons de résoudre depuis longtemps, et c'est quelque chose que tout le monde a probablement rencontré à un moment donné. Le Web est excellent pour parler aux serveurs, mais pas pour parler aux appareils . Pensez, par exemple, à essayer de configurer un routeur dans votre réseau. Il y a de fortes chances que vous deviez entrer une adresse IP et utiliser une interface Web via une connexion HTTP simple sans aucune sécurité. C'est juste une mauvaise expérience et une mauvaise sécurité. En plus de cela, comment savez-vous quelle est la bonne adresse IP ?
HTTP est également le premier problème que nous rencontrons lorsque nous essayons de créer une application Web progressive qui essaie de parler à un appareil. Les PWA sont uniquement HTTPS et les périphériques locaux sont toujours uniquement HTTP. Vous avez besoin d'un certificat pour HTTPS, et pour obtenir un certificat, vous avez besoin d'un serveur accessible au public avec un nom de domaine (je parle d'appareils sur notre réseau local qui sont hors de portée).
Ainsi, pour de nombreux appareils, vous avez besoin d'applications natives pour configurer les appareils et les utiliser, car les applications natives ne sont pas liées aux limites de la plate-forme Web et peuvent offrir une expérience agréable à ses utilisateurs. Cependant, je ne veux pas télécharger une application de 500 Mo pour le faire. Peut-être que l'appareil que vous possédez a déjà quelques années et que l'application n'a jamais été mise à jour pour fonctionner sur votre nouveau téléphone. Vous souhaitez peut-être utiliser un ordinateur de bureau ou un ordinateur portable, et le fabricant n'a créé qu'une application mobile. Pas non plus une expérience idéale.
WebBluetooth est une nouvelle spécification qui a été implémentée dans Chrome et Samsung Internet qui nous permet de communiquer directement avec les appareils Bluetooth Low Energy à partir du navigateur. Les applications Web progressives en combinaison avec WebBluetooth offrent la sécurité et la commodité d'une application Web avec le pouvoir de parler directement aux appareils.
Bluetooth a une assez mauvaise réputation en raison de sa portée limitée, de sa mauvaise qualité audio et de ses problèmes de couplage. Mais, à peu près tous ces problèmes appartiennent au passé. Bluetooth Low Energy est une spécification moderne qui n'a pas grand-chose à voir avec les anciennes spécifications Bluetooth , à part l'utilisation du même spectre de fréquences. Plus de 10 millions d'appareils sont livrés avec le support Bluetooth chaque jour. Cela inclut les ordinateurs et les téléphones, mais aussi une variété d'appareils comme les moniteurs de fréquence cardiaque et de glucose, les appareils IoT comme les ampoules et les jouets comme les voitures et les drones contrôlables à distance.
Lecture recommandée : Comprendre les plates-formes basées sur des API : un guide pour les chefs de produit
La partie théorique ennuyeuse
Le Bluetooth n'étant pas en lui-même une technologie Web, il utilise un vocabulaire qui peut nous sembler peu familier. Passons donc en revue le fonctionnement de Bluetooth et une partie de la terminologie.
Chaque appareil Bluetooth est soit un « appareil central », soit un « périphérique ». Seuls les appareils centraux peuvent initier la communication et ne peuvent parler qu'aux périphériques. Un exemple d'appareil central serait un ordinateur ou un téléphone portable.
Un périphérique ne peut pas initier de communication et ne peut parler qu'à un appareil central. De plus, un périphérique ne peut parler qu'à un seul appareil central à la fois. Un périphérique ne peut pas communiquer avec un autre périphérique.

Un dispositif central peut parler à plusieurs périphériques en même temps et peut relayer des messages s'il le souhaite. Ainsi, un moniteur de fréquence cardiaque ne pourrait pas parler à vos ampoules, cependant, vous pourriez écrire un programme qui s'exécute sur un appareil central qui reçoit votre fréquence cardiaque et allume les lumières rouges si la fréquence cardiaque dépasse un certain seuil.
Lorsque nous parlons de WebBluetooth, nous parlons d'une partie spécifique de la spécification Bluetooth appelée Generic Attribute Profile, qui a l'abréviation très évidente GATT. (Apparemment, GAP a déjà été pris.)
Dans le cadre du GATT, on ne parle plus d'appareils centraux et de périphériques, mais de clients et de serveurs. Vos ampoules sont des serveurs. Cela peut sembler contre-intuitif, mais cela a du sens si vous y réfléchissez. L'ampoule offre un service, c'est-à-dire la lumière. Tout comme lorsque le navigateur se connecte à un serveur sur Internet, votre téléphone ou votre ordinateur est un client qui se connecte au serveur GATT dans l'ampoule.
Chaque serveur offre un ou plusieurs services. Certains de ces services font officiellement partie de la norme, mais vous pouvez également définir les vôtres. Dans le cas du moniteur de fréquence cardiaque, il existe un service officiel défini dans le cahier des charges. Dans le cas de l'ampoule, il n'y en a pas, et presque tous les fabricants essaient de réinventer la roue. Chaque service a une ou plusieurs caractéristiques. Chaque caractéristique a une valeur qui peut être lue ou écrite. Pour l'instant, il serait préférable de le considérer comme un tableau d'objets, chaque objet ayant des propriétés qui ont des valeurs.

Contrairement aux propriétés des objets, les services et les caractéristiques ne sont pas identifiés par une chaîne. Chaque service et caractéristique a un UUID unique qui peut avoir une longueur de 16 ou 128 bits. Officiellement, l'UUID 16 bits est réservé aux normes officielles, mais pratiquement personne ne suit cette règle. Enfin, chaque valeur est un tableau d'octets. Il n'y a pas de types de données fantaisistes dans Bluetooth.
Un examen plus approfondi d'une ampoule Bluetooth
Regardons donc un appareil Bluetooth réel : une Mipow Playbulb Sphere. Vous pouvez utiliser une application comme BLE Scanner ou nRF Connect pour vous connecter à l'appareil et voir tous les services et caractéristiques. Dans ce cas, j'utilise l'application BLE Scanner pour iOS.

La première chose que vous voyez lorsque vous vous connectez à l'ampoule est une liste de services. Il existe des services standardisés comme le service d'information sur les appareils et le service de batterie. Mais il existe aussi des services personnalisés. Je suis particulièrement intéressé par le service avec l'UUID 16 bits de 0xff0f
. Si vous ouvrez ce service, vous pouvez voir une longue liste de caractéristiques. Je n'ai aucune idée de ce que font la plupart de ces caractéristiques, car elles ne sont identifiées que par un UUID et parce qu'elles font malheureusement partie d'un service personnalisé ; ils ne sont pas normalisés et le fabricant n'a fourni aucune documentation.
La première caractéristique avec l'UUID de 0xfffc
semble particulièrement intéressante. Il a une valeur de quatre octets. Si nous changeons la valeur de ces octets de 0x00000000
à 0x00ff0000
, l'ampoule devient rouge. Le changer en 0x0000ff00
rend l'ampoule verte et 0x000000ff
bleue. Ce sont des couleurs RVB et correspondent exactement aux couleurs hexadécimales que nous utilisons en HTML et CSS.
A quoi sert ce premier octet ? Eh bien, si nous changeons la valeur en 0xff000000
, l'ampoule devient blanche. L'ampoule contient quatre LED différentes, et en changeant la valeur de chacun des quatre octets, nous pouvons créer chaque couleur que nous voulons.
L'API WebBluetooth
C'est fantastique que nous puissions utiliser une application native pour changer la couleur d'une ampoule, mais comment faisons-nous cela depuis le navigateur ? Il s'avère qu'avec les connaissances sur Bluetooth et GATT que nous venons d'apprendre, c'est relativement simple grâce à l'API WebBluetooth. Il suffit de quelques lignes de JavaScript pour changer la couleur d'une ampoule.
Passons en revue l'API WebBluetooth.
Connexion à un appareil
La première chose que nous devons faire est de nous connecter du navigateur à l'appareil. Nous appelons la fonction navigator.bluetooth.requestDevice()
et fournissons à la fonction un objet de configuration. Cet objet contient des informations sur l'appareil que nous voulons utiliser et sur les services qui doivent être disponibles pour notre API.
Dans l'exemple suivant, nous filtrons sur le nom de l'appareil, car nous ne voulons voir que les appareils dont le nom contient le préfixe PLAYBULB
. Nous spécifions également 0xff0f
comme service que nous voulons utiliser. Puisque la fonction requestDevice()
renvoie une promesse, nous pouvons attendre le résultat.
let device = await navigator.bluetooth.requestDevice({ filters: [ { namePrefix: 'PLAYBULB' } ], optionalServices: [ 0xff0f ] });
Lorsque nous appelons cette fonction, une fenêtre apparaît avec la liste des appareils conformes aux filtres que nous avons spécifiés. Nous devons maintenant sélectionner manuellement l'appareil auquel nous voulons nous connecter. C'est une étape essentielle pour la sécurité et la confidentialité et donne le contrôle à l'utilisateur. L'utilisateur décide si l'application Web est autorisée à se connecter et, bien sûr, à quel appareil elle est autorisée à se connecter. L'application Web ne peut pas obtenir une liste d'appareils ou se connecter sans que l'utilisateur ne sélectionne manuellement un appareil.

Après avoir accédé à l'appareil, nous pouvons nous connecter au serveur GATT en appelant la fonction connect()
sur la propriété gatt
de l'appareil et attendre le résultat.
let server = await device.gatt.connect();
Une fois que nous avons le serveur, nous pouvons appeler getPrimaryService()
sur le serveur avec l'UUID du service que nous voulons utiliser comme paramètre et attendre le résultat.
let service = await server.getPrimaryService(0xff0f);
Appelez ensuite getCharacteristic()
sur le service avec l'UUID de la caractéristique en paramètre et attendez à nouveau le résultat.
Nous avons maintenant nos caractéristiques que nous pouvons utiliser pour écrire et lire des données :
let characteristic = await service.getCharacteristic(0xfffc);
Écrire des données
Pour écrire des données, nous pouvons appeler la fonction writeValue()
sur la caractéristique avec la valeur que nous voulons écrire en tant que ArrayBuffer, qui est une méthode de stockage des données binaires. La raison pour laquelle nous ne pouvons pas utiliser un tableau régulier est que les tableaux réguliers peuvent contenir des données de différents types et peuvent même avoir des trous vides.
Comme nous ne pouvons pas créer ou modifier directement un ArrayBuffer, nous utilisons plutôt un "tableau typé". Chaque élément d'un tableau typé est toujours du même type et n'a pas de trous. Dans notre cas, nous allons utiliser un Uint8Array
, qui n'est pas signé et ne peut donc pas contenir de nombres négatifs ; un entier, donc il ne peut pas contenir de fractions ; et il est de 8 bits et ne peut contenir que des valeurs de 0 à 255. En d'autres termes : un tableau d'octets.
characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );
Nous savons déjà comment fonctionne cette ampoule particulière. Nous devons fournir quatre octets, un pour chaque LED. Chaque octet a une valeur comprise entre 0 et 255, et dans ce cas, nous ne voulons utiliser que les LED rouge, verte et bleue, nous laissons donc la LED blanche éteinte, en utilisant la valeur 0.
Lecture de données
Pour lire la couleur actuelle de l'ampoule, nous pouvons utiliser la fonction readValue()
et attendre le résultat.
let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);
La valeur que nous récupérons est une DataView d'un ArrayBuffer, et elle offre un moyen d'extraire les données de l'ArrayBuffer. Dans notre cas, nous pouvons utiliser la fonction getUint8()
avec un index comme paramètre pour extraire les octets individuels du tableau.
Être informé des modifications
Enfin, il existe également un moyen d'être averti lorsque la valeur d'un appareil change. Ce n'est pas vraiment utile pour une ampoule, mais pour notre moniteur de fréquence cardiaque, nous avons des valeurs qui changent constamment, et nous ne voulons pas interroger manuellement la valeur actuelle à chaque seconde.
characteristic.addEventListener( 'characteristicvaluechanged', e => { let r = e.target.value.getUint8(1); let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); } ); characteristic.startNotifications();
Pour obtenir un rappel chaque fois qu'une valeur change, nous devons appeler la fonction addEventListener()
sur la caractéristique avec le paramètre characteristicvaluechanged
et une fonction de rappel. Chaque fois que la valeur change, la fonction de rappel sera appelée avec un objet événement comme paramètre, et nous pouvons obtenir les données de la propriété value de la cible de l'événement. Et, enfin, extrayez à nouveau les octets individuels du DataView du ArrayBuffer.
La bande passante sur le réseau Bluetooth étant limitée, nous devons démarrer manuellement ce mécanisme de notification en appelant startNotifications()
sur la caractéristique. Sinon, le réseau va être inondé de données inutiles. De plus, comme ces appareils utilisent généralement une batterie, chaque octet que nous n'avons pas à envoyer améliorera définitivement la durée de vie de la batterie de l'appareil car la radio interne n'a pas besoin d'être allumée aussi souvent.
Conclusion
Nous avons maintenant dépassé 90 % de l'API WebBluetooth. Avec seulement quelques appels de fonction et l'envoi de 4 octets, vous pouvez créer une application Web qui contrôle les couleurs de vos ampoules. Si vous ajoutez quelques lignes supplémentaires, vous pouvez même contrôler une petite voiture ou piloter un drone. Avec de plus en plus d'appareils Bluetooth qui arrivent sur le marché, les possibilités sont infinies.
Autres ressources
- Bluetooth.rocks ! Démos | (Code source sur GitHub)
- « Spécification Web Bluetooth », Groupe de la communauté Web Bluetooth
- Open GATT Registry Une collection non officielle de documentation pour les services d'attributs génériques pour les appareils Bluetooth Low Energy.