Site : tvaira.free.fr

Mise en oeuvre du Bluetooth

Définition

Bluetooth est une norme de communications permettant l’échange bidirectionnel de données à très courte distance en utilisant des ondes radio UHF sur une bande de fréquence de 2,4 GHz.

Liens :

Description

Dans les versions commercialisées en 2015 (4.0 et 4.1), largement utilisées, essentiellement dans les appareils mobiles comme les téléphones portables, la liaison Bluetooth présente les caractéristiques suivantes :

  • très faible consommation d’énergie
  • très faible portée (sur un rayon de l’ordre d’une dizaine de mètres)
  • faible débit
  • très bon marché et peu encombrant.

En conséquence, il est présent sur des appareils fonctionnant souvent sur batterie, désirant échanger une faible quantité de données sur une courte distance :

  • téléphones portables (presque généralisé), où il sert essentiellement à la liaison avec une oreillette ou à l’échange de fichiers, ou encore comme modem
  • ordinateurs portables, systèmes embarqués essentiellement pour communiquer avec les téléphones portables
  • périphériques divers, comme des claviers, pour faciliter la saisie sur les appareils qui en sont dépourvus
  • périphériques spécialisés, comme des appareils à électrocardiogramme, qui peuvent communiquer sans fil avec l’ordinateur du médecin.

Picoréseau

Un picoréseau (piconet) est un mini-réseau qui se crée de manière instantanée et automatique quand plusieurs périphériques Bluetooth sont dans un même rayon. Un picoréseau est organisé selon une topologie en étoile : il y a un « maître » et plusieurs « esclaves ».

Un périphérique « maître » peut administrer jusqu’à :

  • 7 esclaves « actifs » ;
  • 255 esclaves en mode « parked ».

La communication est directe entre le « maître » et un « esclave ». Les « esclaves » ne peuvent pas communiquer entre eux.

Tous les « esclaves » du picoréseau sont synchronisés sur l’horloge du « maître ». C’est le « maître » qui détermine la fréquence de saut pour tout le picoréseau.

Les périphériques « esclaves » peuvent avoir plusieurs « maîtres » : les différents piconets peuvent donc être reliés entre eux. Le réseau ainsi formé est appelé un scatternet (littéralement « réseau dispersé »).

Les couches

Les couches matérielles

Les éléments fondamentaux d’un produit Bluetooth sont définis dans les deux premières couches protocolaires :

  • la couche radio (1) ;
  • et la couche bande de base ou baseband (2).

Ces couches prennent en charge les tâches matérielles comme le contrôle du saut de fréquence et la synchronisation des horloges.

La couche radio

La couche radio (la couche la plus basse) est gérée au niveau matériel. C’est elle qui s’occupe de l’émission et de la réception des ondes radio. Elle définit les caractéristiques telles que la bande de fréquence et l’arrangement des canaux, les caractéristiques du transmetteur, de la modulation, du récepteur, etc.

Le système Bluetooth opère dans la bande de fréquences comprise entre 2 400 et 2 483,5 MHz. Un transceiver à sauts de fréquences est utilisé pour limiter les interférences et l’atténuation.

Les 79 canaux RF sont numérotés de 0 à 78 et séparés de 1 MHz en commençant par 2 402 MHz. Le codage de l’information se fait par sauts de fréquences et la période est de 625 µs, ce qui permet 1 600 sauts par seconde.

Le système Bluetooth utilise une modulation de fréquence avec une rapidité de modulation est de 1 Mbaud. La transmission duplex est basé sur un multipexage temporel.

Il existe trois classes de modules radio Bluetooth sur le marché :

Classe Puissance Portée
1 100 mW (20 dBm) 100 mètres
2 2,5 mW (4 dBm) 10 à 20 mètres
3 1 mW (0 dBm) Quelques mètres

La plupart des fabricants d’appareils électroniques utilisent des modules classe 2.

La couche bande de base

Cette couche matérielle permet de définir les adresses physiques des périphériques (équivalentes à l’adresse MAC d’une carte réseau). Cette adresse est nommée BD_ADDR (Bluetooth Device Address) et est codée sur 48 bits. Ces adresses sont gérées par la IEEE Registration Authority.

La bande de base peut donc gérer deux types majeurs de liens logiques :

  • les liens SCO (Synchronous Connection-Oriented) : pour la transmission voix temps réel (point à point et bidirectionnel).

  • les liens ACL (Asynchronous Connection-Less) : conçu pour l’échange de données. Le broadcast est possible.

Les données transportées sur ces liens logiques sont sous forme de paquets. Il existe divers types de paquets et peuvent être utilisés par les deux liens logiques ou seulement par un seul type de lien. Chaque paquet est composé globalement de la même manière.

On retrouve trois parties essentielles :

  • Le code d’accès Access code (72 ou 68 bits) : identifie le piconet maître et il est utilisé pour la synchronisation, le « paging » et la recherche.
  • L’entête Header (54 bits, en fait une séquence de 18 bits répétés trois fois) : composé de six champs dont L’adresse actif de l’esclave. 0 pour le broadcast et 1 à 6 pour le périphérique, le type de lien, …
  • La charge utile Payload (de 0 à 2 745 bits) : les données utiles.

Les couches logicielles

Le gestionnaire de liaisons

Cette couche gère les liens entre les périphériques « maîtres » et « esclaves » ainsi que les types de liaisons (synchrones ou asynchrones).

C’est le gestionnaire de liaisons qui implémente les mécanismes de sécurité comme :

  • l’authentification ;
  • l’appairage (l’association) ;
  • la création et la modification des clés ;
  • et le chiffrement.

Il utilise le protocole L2CAP pour interagir avec son homologue sur les équipements distants.

L’interface de contrôle de l’hôte

Cette couche fournit une méthode uniforme pour accéder aux couches matérielles. Son rôle de séparation permet un développement indépendant du matériel et du logiciel.

Les protocoles de transport supportés sont HCI (Host Controller Interface) USB, RS-232, UART, …

La couche L2CAP

La couche L2CAP (Logical Link Control & Adaptation Protocol) fournit les services de multiplexage des protocoles de niveau supérieur et la segmentation et le réassemblage des paquets ainsi que le transport des informations de qualité de service. Les protocoles de haut niveau peuvent ainsi transmettre et recevoir des paquets jusqu’à 64 Ko. Elle autorise un contrôle de flux par canal de communication.

La couche L2CAP utilise des canaux logiques.

Les services

Il existe plusieurs services : RFCOMM, SDP (Service Discovery Protocol) et OBEX (OBject EXchange).

Le service RFCOMM (Radio frequency communication) est basé sur les spécifications RS-232, qui émule des liaisons séries. Il peut notamment servir à faire passer une communication IP par Bluetooth. RFCOMM est utilisé lorsque le débit des données n’atteint pas plus de 360 kbit/s.

Les profils

Un profil correspond à une spécification fonctionnelle d’un usage particulier. Les profils peuvent également correspondre à différents types de périphériques. Les profils ont pour but d’assurer une interopérabilité entre tous les appareils Bluetooth. Ils définissent notamment la manière d’implémenter un usage défini et les protocoles spécifiques à utiliser.

Il existe une hiérarchie entre profils et donc des dépendances entre eux. Pour chaque profil, il existe plusieurs points qui sont redéfinis ou non : rôle, scénario, principes de base …

Le profil d’accès générique GAP (Generic Access Profile) est le profil de base dont tous les autres profils héritent. Il définit les procédures génériques de recherche d’appareils, de connexion et de sécurité.

Les rôles dans une communication seront les suivants :

  • Initiateur : celui qui pour une procédure donnée, est à l’origine de l’établissement d’un lien ou d’une transaction sur un lien existant.
  • Accepteur

Remarque : on est assez proche des rôles Client/Serveur.

Le profil d’accès générique GAP expose l’ensemble des caractéristiques de tous les équipements Bluetooth :

  • les spécifications sur la représentation des propriétés Bluetooth : l’adresse Bluetooth, le nom d’un équipement, son type, le PIN number utilisé pour authentifier 2 périphériques ;
  • les « modes » génériques à tous les profiles : discoverability mode (on peut le détecter), connectability mode (on peut s’y connecter), pairing mode (on peut créer un lien avec) ;
  • les procédures générales qui peuvent être utilisées pour « découvrir » les propriétés basiques des équipements Bluetooth (nom, type…) qui sont « découvrables »;
  • les procédures générales de connexions à d’autres dispositifs Bluetooth ;
  • la procédure générale de création de liens entre des dispositifs Bluetooth ;

L’appairage

Afin d’échanger des données, les appareils doivent être appairés. L’appairage se fait en lançant la découverte à partir d’un appareil et en échangeant un code.

Remarques : Dans certains cas, le code est libre, et il suffit aux deux appareils de saisir le même code. Dans d’autres cas, le code est fixé par l’un des deux appareils (appareil dépourvu de clavier, par exemple), et l’autre doit le connaître pour s’y raccorder. Par la suite, les codes sont mémorisés, et il suffit qu’un appareil demande le raccordement et que l’autre l’accepte pour que les données puissent être échangées. Afin de limiter les risques d’intrusion, les appareils qui utilisent un code préprogrammé (souvent 0000 ou 1234) doivent être activés manuellement, et l’appairage ne peut se faire que durant une courte période.

Le module Bluetooth HC-05

Il existe de nombreux modules Bluetooth très similaires (www.martyncurrey.com/bluetooth-modules/) dont le HC-05 qui peut fonctionner en maître ou esclave.

Ce type de module s’interface par une liaison série TTL. Il existe généralement deux modes d’exploitation : la communication (souvent par défaut 9600/8/N/1) et la configuration par commandes AT (38400/8/N/1) suivies de \r\n.

AT
OK

AT+ADDR?
+ADDR:2015:11:167604 ---- BD_ADDR
OK

AT+NAME?
+NAME: HC-05 ---- Nom
OK

AT+ROLE?
+ROLE:0 ---- Esclave
OK

...

Remarque : Le module HC05 possède également une broche key permettant de basculer du mode configuration AT (key au niveau haut) au mode communication (key au niveau bas).

Programmation : Mise en oeuvre d’un port série sous Qt

Raspberry Pi 3 et Bluetooth

Le service Bluetooth

$ sudo systemctl status bluetooth.service
bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-02-16 18:01:03 CET; 21min ago
     Docs: man:bluetoothd(8)
Main PID: 558 (bluetoothd)
   Status: "Running"
   CGroup: /system.slice/bluetooth.service
           └─558 /usr/lib/bluetooth/bluetoothd

févr. 16 18:01:03 raspberrypi systemd[1]: Starting Bluetooth service...
févr. 16 18:01:03 raspberrypi bluetoothd[558]: Bluetooth daemon 5.43
févr. 16 18:01:03 raspberrypi systemd[1]: Started Bluetooth service.
févr. 16 18:01:03 raspberrypi bluetoothd[558]: Starting SDP server
févr. 16 18:01:03 raspberrypi bluetoothd[558]: Bluetooth management interface 1.14 initialized
...

Les modules noyau

$ dmesg | grep -i bluetooth
[   10.678749] Bluetooth: Core ver 2.22
[   10.678813] Bluetooth: HCI device and connection manager initialized
[   10.678826] Bluetooth: HCI socket layer initialized
[   10.678834] Bluetooth: L2CAP socket layer initialized
[   10.678853] Bluetooth: SCO socket layer initialized
[   10.732670] Bluetooth: HCI UART driver ver 2.3
[   10.732682] Bluetooth: HCI UART protocol H4 registered
[   10.732686] Bluetooth: HCI UART protocol Three-wire (H5) registered
[   10.732820] Bluetooth: HCI UART protocol Broadcom registered
[   11.177154] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[   11.177166] Bluetooth: BNEP filters: protocol multicast
[   11.177185] Bluetooth: BNEP socket layer initialized
[   11.230865] Bluetooth: RFCOMM TTY layer initialized
[   11.230894] Bluetooth: RFCOMM socket layer initialized
[   11.230913] Bluetooth: RFCOMM ver 1.11

$ lsmod | grep -i -E "bluetooth|bnep|rfcomm"
rfcomm                 37723  6
bnep                   12051  2
bluetooth             365780  29 hci_uart,bnep,btbcm,rfcomm
rfkill                 20851  6 bluetooth,cfg80211

$ modinfo bluetooth
filename:       /lib/modules/4.9.59-v7+/kernel/net/bluetooth/bluetooth.ko
alias:          net-pf-31
license:        GPL
version:        2.22
description:    Bluetooth Core ver 2.22
author:         Marcel Holtmann <marcel@holtmann.org>
srcversion:     CEEDA16781A58CDD168D597
depends:        rfkill
intree:         Y
vermagic:       4.9.59-v7+ SMP mod_unload modversions ARMv7 p2v8 
parm:           disable_esco:Disable eSCO connection creation (bool)
parm:           disable_ertm:Disable enhanced retransmission mode (bool)
...

Les paquetages

$ dpkg -l | grep -iE "bluez|bluetooth"
ii  bluealsa                              0.6                                  armhf        Bluetooth ALSA Audio backend
ii  bluez                                 5.43-2+rpt2+deb9u2                   armhf        Bluetooth tools and daemons
ii  bluez-firmware                        1.2-3+rpt1                           all          Firmware for Bluetooth devices
ii  bluez-obexd                           5.43-2+rpt2+deb9u2                   armhf        bluez obex daemon
ii  bluez-test-tools                      5.43-2+rpt2+deb9u2                   armhf        test tools of bluez
ii  bluez-tools                           0.2.0~20140808-5                     armhf        Set of tools to manage Bluetooth devices for linux
ii  libbluetooth3:armhf                   5.43-2+rpt2+deb9u2                   armhf        Library to use the BlueZ Linux Bluetooth stack
ii  lxplug-bluetooth                      0.4                                  armhf        Bluetooth plugin for lxpanel
ii  pi-bluetooth                          0.1.6                                armhf        Raspberry Pi 3 bluetooth

Les outils

Liste des outils du paquetage BlueZ :

$ dpkg -L bluez | grep "/usr/bin"
/usr/bin/bccmd
/usr/bin/bluemoon
/usr/bin/bluetoothctl
/usr/bin/btattach
/usr/bin/btmgmt
/usr/bin/btmon
/usr/bin/ciptool
/usr/bin/gatttool
/usr/bin/hciattach
/usr/bin/hcitool
/usr/bin/hex2hcd
/usr/bin/l2ping
/usr/bin/l2test
/usr/bin/mpris-proxy
/usr/bin/obexctl
/usr/bin/rctest
/usr/bin/rfcomm
/usr/bin/sdptool

bluetoothctl est un outil de contrôle du Bluetooth (il remplace les anciennes commandes bluez-xxx-xxx) :

$ bluetoothctl
[NEW] Controller B8:27:EB:01:98:7E raspberrypi [default]
[bluetooth]# list
Controller B8:27:EB:01:98:7E raspberrypi [default]
[bluetooth]# show
Controller B8:27:EB:01:98:7E
    Name: raspberrypi
    Alias: raspberrypi
    Class: 0x6c0000
    Powered: yes
    Discoverable: no
    Pairable: yes
    UUID: Headset AG                (00001112-0000-1000-8000-00805f9b34fb)
    UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
    UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
    UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
    UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)
    UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
    UUID: Audio Sink                (0000110b-0000-1000-8000-00805f9b34fb)
    UUID: Audio Source              (0000110a-0000-1000-8000-00805f9b34fb)
    UUID: Handsfree                 (0000111e-0000-1000-8000-00805f9b34fb)
    UUID: Handsfree Audio Gateway   (0000111f-0000-1000-8000-00805f9b34fb)
    UUID: Headset                   (00001108-0000-1000-8000-00805f9b34fb)
    Modalias: usb:v1D6Bp0246d052B
    Discovering: no
[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:01:98:7E Discovering: yes
[NEW] Device 20:15:11:16:76:04 HC-05
[bluetooth]# devices
Device 20:15:11:16:76:04 HC-05
[bluetooth]# agent on
Agent registered
[bluetooth]# pair 20:15:11:16:76:04
Attempting to pair with 20:15:11:16:76:04
[CHG] Device 20:15:11:16:76:04 Connected: yes
Request PIN code
[agent] Enter PIN code: 1234
[CHG] Device 20:15:11:16:76:04 UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device 20:15:11:16:76:04 ServicesResolved: yes
[CHG] Device 20:15:11:16:76:04 Paired: yes
Pairing successful
[CHG] Device 20:15:11:16:76:04 ServicesResolved: no
[CHG] Device 20:15:11:16:76:04 Connected: no
[bluetooth]# paired-devices
Device 20:15:11:16:76:04 HC-05
[bluetooth]# trust 20:15:11:16:76:04
[CHG] Device 20:15:11:16:76:04 Trusted: yes
Changing 20:15:11:16:76:04 trust succeeded

[bluetooth]# quit

hciconfig est utilisé pour configurer les périphériques Bluetooth (hciX est le nom d’un périphérique Bluetooth installé dans le système) :

$ hciconfig
hci0:   Type: Primary  Bus: UART
    BD Address: B8:27:EB:01:98:7E  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING 
    RX bytes:822 acl:0 sco:0 events:57 errors:0
    TX bytes:4231 acl:0 sco:0 commands:57 errors:0

$ hciconfig hci0 ptype
...
    Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
$ hciconfig hci0 lm
...
    Link mode: SLAVE ACCEPT 

$ hciconfig hci0 class
...
    Class: 0x6c0000
    Service Classes: Rendering, Capturing, Audio, Telephony
    Device Class: Miscellaneous, 
$ hciconfig hci0 iac
...
    IAC: 0x9e8b33 

hcitool est utilisé pour configurer les connexions Bluetooth et envoyer une commande spéciale aux périphériques Bluetooth :

$ hcitool dev
Devices:
    hci0    B8:27:EB:01:98:7E

$ hcitool scan
Scanning ...
    20:15:11:16:76:04   HC-05

$ hcitool inq
Inquiring ...
    20:15:11:16:76:04   clock offset: 0x248f    class: 0x001f00

$ sudo hcitool info 20:15:11:16:76:04
Requesting information ...
    BD Address:  20:15:11:16:76:04
    Device Name: HC-05
    LMP Version: 2.1 (0x4) LMP Subversion: 0x1735
    Manufacturer: Cambridge Silicon Radio (10)
    Features page 0: 0xff 0xff 0x8f 0xfe 0x9b 0xff 0x59 0x83
        <3-slot packets> <5-slot packets> <encryption> <slot offset>
        ...

l2ping envoie une requête d’écho L2CAP à l’adresse MAC Bluetooth BD_ADDR donnée en notation hexadécimale :

$ sudo l2ping -c 1 20:15:11:16:76:04
Ping: 20:15:11:16:76:04 from B8:27:EB:01:98:7E (data size 44) ...
4 bytes from 20:15:11:16:76:04 id 0 time 9.69ms
1 sent, 1 received, 0% loss

rfcomm est utilisé pour configurer, maintenir et inspecter la configuration RFCOMM du sous-système Bluetooth dans le noyau Linux. Si aucune commande n’est donnée, ou si l’option -a est utilisée, rfcomm imprime des informations sur les périphériques RFCOMM configurés.

$ rfcomm -a

sdptool permet d’effectuer des requêtes SDP sur les périphériques Bluetooth.

$ sdptool browse local
$ sdptool browse 34:14:5F:D2:50:D8

bluez-simple-agent est un programme qui permet de gérer l’appairage de périphériques Bluetooth :

$ bluez-simple-agent hci0 20:15:11:16:76:04
RequestPinCode (/org/bluez/1525/hci0/dev_20_15_11_16_76_04)
Enter PIN Code: 1234
Release
New device (/org/bluez/1525/hci0/dev_20_15_11_16_76_04)

$ bluez-simple-agent hci0 20:15:11:16:76:04 remove

hcidump lit et affiche les données HCI brutes d’une communication Bluetooth.

$ hcidump
HCI sniffer - Bluetooth packet analyzer ver 2.2
device: hci0 snap_len: 1028 filter: 0xffffffffffffffff
> HCI Event: Command Status (0x0f) plen 4
    Exit Sniff Mode (0x02|0x0004) status 0x00 ncmd 1
> HCI Event: Command Status (0x0f) plen 4
    Exit Sniff Mode (0x02|0x0004) status 0x00 ncmd 1
> HCI Event: Command Status (0x0f) plen 4
    Disconnect (0x01|0x0006) status 0x00 ncmd 1
> HCI Event: Disconn Complete (0x05) plen 4
    status 0x00 handle 43 reason 0x16
    Reason: Connection Terminated by Local Host
...

Tests

Recherche de périphériques à proximité :

$ hcitool scan
Scanning ...
    34:14:5F:D2:50:D8   Galaxy Tab S2
    20:15:11:16:76:04   HC-05

Appairage :

  • RPI3 :
$ bluetoothctl
[NEW] Controller B8:27:EB:01:98:7E raspberrypi [default]
[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:01:98:7E Discovering: yes
[NEW] Device 20:15:11:16:76:04 HC-05
[bluetooth]# agent on
Agent registered
[bluetooth]# pair 20:15:11:16:76:04
Attempting to pair with 20:15:11:16:76:04
[CHG] Device 20:15:11:16:76:04 Connected: yes
Request PIN code
[agent] Enter PIN code: 1234
[CHG] Device 20:15:11:16:76:04 UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device 20:15:11:16:76:04 ServicesResolved: yes
[CHG] Device 20:15:11:16:76:04 Paired: yes
Pairing successful
[CHG] Device 20:15:11:16:76:04 ServicesResolved: no
[CHG] Device 20:15:11:16:76:04 Connected: no
[bluetooth]# paired-devices
Device 20:15:11:16:76:04 HC-05
[bluetooth]# trust 20:15:11:16:76:04
[CHG] Device 20:15:11:16:76:04 Trusted: yes
Changing 20:15:11:16:76:04 trust succeeded
[bluetooth]# quit
  • Ubuntu 12.04 (avec un dongle Bluetooth) :
$ bluez-simple-agent hci0 20:15:11:16:76:04
RequestPinCode (/org/bluez/1525/hci0/dev_20_15_11_16_76_04)
Enter PIN Code: 1234
Release
New device (/org/bluez/1525/hci0/dev_20_15_11_16_76_04)

Connexion :

$ sudo rfcomm bind 0 20:15:11:16:76:04
$ rfcomm -a
rfcomm0: 20:15:11:16:76:04 channel 1 clean
$ ls -l /dev/rfcomm0
crw-rw---- 1 root dialout 216, 0 févr. 23 16:26 /dev/rfcomm0

Remarque : bind lie le périphérique RFCOMM à un périphérique Bluetooth distant. La commande n’établit pas une connexion avec le périphérique distant, elle crée uniquement la liaison. La connexion sera établie juste après qu’une application essaie d’ouvrir le périphérique RFCOMM (/dev/rfcomm0).

Communication :

Simple test d’écriture et de lecture avec la console sur le fichier spécial /dev/rfcomm0 :

$ echo "hello wordl" > /dev/rfcomm0

$ sudo cat /dev/rfcomm0

En utilisant un utilitaire type cutecom ou putty sur le fichier spécial /dev/rfcomm0, on peut ensuite lire et écrire via le Bluetooth.

$ rfcomm -a
rfcomm0: 20:15:11:16:76:04 channel 1 connected [tty-attached]

Déconnexion :

$ rfcomm -a
rfcomm0: 34:14:5F:D2:50:D8 channel 1 closed
$ sudo rfcomm release 0

Configuration

Il est possible de configurer une liaison automatique avec un périphérique Bluetooth qui sera accessible via le fichier spécial /dev/rfcomm0 :

$ sudo vim /etc/bluetooth/rfcomm.conf

# RFCOMM configuration file.

rfcomm0 {
#   # Automatically bind the device at startup
    bind yes;

#   # Bluetooth address of the device
    device 20:15:11:16:76:04;

#   # RFCOMM channel for the connection
    channel 1;

#   # Description of the connection
    comment "Exemple Bluetooth device";
}

$ sudo /etc/init.d/bluetooth restart
bluetooth stop/waiting
bluetooth start/running, process 4995

$ ls -l /dev/rfcomm0
crw-rw---- 1 root dialout 216, 0 févr. 23 16:53 /dev/rfcomm0

Qt et Bluetooth

Qt et Bluetooth : état actuel et améliorations à venir

Qt 5.2 a apporté le module Bluetooth, initialement très limité : il n’était utilisable que sur Linux par BlueZ 4 et sur BlackBerry 10. Chaque version a amélioré l’état du module : avec Qt 5.3, il devient compatible avec Android ; avec Qt 5.4, il devient compatible avec BlueZ 5 sous Linux et la version basse énergie Bluetooth LE (pour Low Energy) est accessible (uniquement pour BlueZ, en préversion). Avec Qt 5.5, il devrait être compatible avec OS X (y compris la variante LE), WinRT (ARM) pour la 5.6.

L’objectif principal de ce module est de fournir une couche d’abstraction des fonctionnalités Bluetooth de base, utilisable peu importe la plateforme : chercher des périphériques Bluetooth accessibles, leur envoyer des données par OPP (object push profile), s’y connecter par un canal RFCOMM en série (SPP, serial port profile), etc. La déclinaison LE devient de plus importante, dans Qt mais aussi dans ses applications : avec l’habitronique, les divers capteurs connectés, tant pour la santé que l’environnement, les périphériques doivent limiter au maximum leur consommation ; Qt reste utile pour ces applications, grâce à ses fonctionnalités Bluetooth LE.

De nouvelles fonctionnalités sont prévues pour les prochaines versions de Qt : une compatibilité avec Windows (x86), des interfaces Qt Quick, des améliorations pour la détection et la communication des fonctionnalités disponibles, par exemple pour l’interaction avec des périphériques iBeacon (Apple).

Le module Bluetooth de Qt5

Installation Qt5 sur une Raspberry PI 3 (Raspbian GNU/Linux 9 (stretch) Linux 4.9.59-v7+) :

$ sudo apt-get install qt5-default
$ sudo apt-get install libbluetooth-dev qtconnectivity5-dev qtconnectivity5-examples

$ qmake -v
QMake version 3.0
Using Qt version 5.7.1 in /usr/lib/arm-linux-gnueabihf

Documentations :

Pour utiliser l’API Qt Bluetooth dans votre application, il faut commencer par ajouter l’option de configuration suivante à votre fichier de projet .pro :

QT += bluetooth

Avec l’API Qt Bluetooth, les cas d’utilisation qui nous intéressent sont :

  • Récupérer des informations sur le périphérique Bluetooth local.
  • Rechercher d’autres appareils Bluetooth à portée et récupérer les informations les concernant.
  • Connecter des périphériques distants via un canal RFCOMM en utilisant le profil de port série (SPP).
  • Créer un serveur RFCOMM qui autorise les connexions entrantes à l’aide de SPP.

Le module QtMobility de Qt4

Remarques : Pour Qt4

Il faut installer le paquetage qtmobility-dev :

$ sudo apt-get install qtmobility-dev

Pour utiliser l’API Qt Bluetooth dans votre application Qt4, il faut commencer par ajouter l’option de configuration suivante à votre fichier de projet .pro et enlever le module bluetooth de la variable QT :

CONFIG += mobility
MOBILITY = connectivity

Pour accéder aux classes de l’API Qt Bluetooth, il faut ajouter l’espace de noms QtMobility :

Récupération des informations

L’API Qt Bluetooth permet d’obtenir des informations sur les périphériques locaux et distants. Les premières étapes de la récupération des informations sur le périphérique consistent à vérifier si Bluetooth est disponible sur l’appareil et à lire l’adresse et le nom de l’appareil local. QBluetoothLocalDevice est la classe qui fournit toutes ces informations. En outre, vous pouvez l’utiliser pour activer / désactiver Bluetooth, définir la visibilité de l’appareil et lister les connexions actuelles.

Principe :

Recherche de périphériques Bluetooth

L’API propose QBluetoothDeviceInfo qui fournit des informations pour les périphériques distants. Bien qu’il est possible de créer ses objets QBluetoothDeviceInfo, le moyen le plus simple consiste à utiliser QBluetoothDeviceDiscoveryAgent pour démarrer une recherche automatique des périphériques Bluetooth visibles dans la plage connectable.

Principe :

Échange de données entre périphériques

On va voir :

  • côté client : se connecter à un périphérique distant via un canal RFCOMM à l’aide du profil de port série (SPP)
  • côté serveur : créer un serveur RFCOMM qui autorise les connexions entrantes à l’aide de SPP

Pour dialoguer entre périphériques Bluetooth, on utilisera un QBluetoothSocket. On utilisera le signal readyRead() pour lire les données disponibles avec readAll() par exemple.

Remarque : QBluetoothSocket ne supporte pas les opération de lecture/écriture synchrones. Les méthodes waitForReadyRead() et waitForBytesWritten() ne sont pas utilisables. les opération de lecture/écriture seraont réalisées read() et write().

  • Côté client, on créera une QBluetoothSocket puis on réalisera la connexion au service RFCOMM :

Principe :

  • Côté serveur, on créera un QBluetoothServer que l’on placera en écoute de connexions entrantes SPP. Si un client se connecte au service RFCOMM, on récupérera un QBluetoothSocket pour assurer le dialogue.

Pour placer le QBluetoothServer en écoute, il est possible d’utiliser une des deux méthodes suivantes :

  • listen(const QBluetoothAddress &address = QBluetoothAddress(), quint16 port = 0) : on utilise ici l’adresse du périphérique Bluetooth local qur lequel on écoutera les demandes de connexion

  • listen(const QBluetoothUuid &uuid, const QString &serviceName = QString()) : fonction pratique pour enregistrer un service SPP avec un UUID et se mettre en écoute

L’état du périphérique Bluetooth local peut être défini avec setHostMode(). On distinguera ces différents états :

  • QBluetoothLocalDevice::HostPoweredOff : le périphérique Bluetooth est éteint. Il peut être allumé en appelant powerOn().

  • QBluetoothLocalDevice::HostConnectable : les périphériques Bluetooth distants pourront se connecter s’ils y sont déjà associés ou s’ils connaissent l’adresse. Cela permet aussi d’allumer l’appareil s’il était éteint.

  • QBluetoothLocalDevice::HostDiscoverable : les périphériques Bluetooth distants pourront détecter la présence du périphérique Bluetooth local. L’appareil sera également connectable et allumé. Ce mode reste parfois actif pendant une durée limitée (sur Android pendant 5 minutes maximum).

On peut connaître l’état en appelant hostMode() ou utiliser le signal hostModeStateChanged() pour détecter un changement.

Remarque : après la déconnexion d’un client, le périphérique Bluetooth local reste dans l’état QBluetoothLocalDevice::HostConnectable.

Principe :

Remarque : pour une serveur multi-clients, voir l’exemple Bluetooth Chat.

Exemples

Voir aussi : Programmation socket Bluetooth en C/C++ et Qt

Codes sources Qt5 :

Code source Qt4 : test-mo-qt4-bluetooth.zip

  • Côté client :

Version widgets :

Version QML :

  • Côté serveur :

Connexion et échange :

Déconnexion :

Remarque : Pour changer le nom du périphérique Bluetooth sur Ubuntu 18.04

$ sudo vim /etc/bluetooth/main.conf

[General]

# Default adaper name
# Defaults to 'BlueZ X.YZ'
Name = un-nom

$ ls -l /var/lib/bluetooth/
drwx------ 6 root root 4096 mars  27 12:01 00:1A:7D:DA:71:13

$ sudo vim /var/lib/bluetooth/00\:1A\:7D\:DA\:71\:13/settings

[General]
Discoverable=true
Name=un-nom

$ sudo systemctl restart bluetooth.service

Qt5 et RFCOMM sur une Raspberry Pi 3

Une autre approche serait d’émuler directement un port série virtuel via la commande rfcomm et d’utiliser QSerialPort pour dialoguer en Bluetooth.

Sur une Raspberry Pi :

$ uname -a
Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l GNU/Linux

Il est possible d’utiliser la commande rfcomm pour communiquer en Bluetooth en utilisant un port série virtuel avec le fichier spécial /dev/rfcomm0.

La Raspberry Pi 3 utilise BlueZ 5. Pour accéder aux fonctions de BlueZ 4, il faut utiliser l’option --compat du service bluetoothd :

$ sudo vim /etc/systemd/system/dbus-org.bluez.service

ExecStart=/usr/lib/bluetooth/bluetoothd --compat

$ sudo systemctl daemon-reload
$ sudo systemctl restart bluetooth
$ sudo chmod 777 /var/run/sdp

On va ajouter le service Serial Port (SP) que l’on va associer au channel 22 :

$ sudo sdptool add --channel=22 SP
$ sdptool browse local
...
Service Name: Serial Port
Service Description: COM Port
Service Provider: BlueZ
Service RecHandle: 0x1000b
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 22
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "Serial Port" (0x1101)
    Version: 0x0100

Les commandes disponibles pour l’utilitaire rfcomm sont :

  • connect : connecte le périphérique RFCOMM au périphérique Bluetooth distant sur le canal spécifié.

  • listen : écoute sur un canal RFCOMM spécifié pour les connexions entrantes. Si cmd est donné, il sera exécuté dès qu’un client se connecte. Lorsque le processus fils se termine ou que le client se déconnecte, la commande se termine. Les occurrences de {} dans cmd seront remplacées par le nom du périphérique utilisé par la connexion. Cette commande peut être terminée avec la séquence de touches CTRL-C.

  • watch est identique à listen sauf que lorsque le processus fils se termine ou que le client se déconnecte, la commande redémarre en écoutant avec les mêmes paramètres.

  • bind : lie le périphérique RFCOMM à un périphérique Bluetooth distant. La commande n’établit pas de connexion avec le périphérique distant, elle crée uniquement la liaison. La connexion sera établie juste après qu’une application essaie d’ouvrir le périphérique RFCOMM.

On va utiliser la commande watch sur le cahannel 22 :

$ sudo rfcomm watch 0 22 cat {}

Remarque : {} dans cat sera remplacé par le nom du périphérique utilisé par la connexion.

Pour tester, il suffit d’utiliser un client capable de se connecter sur notre périphérique. Il en existe de nombreuses applications Android (comme Bluetooth Terminal) pour faire cela.

Il faudra tout d’abord appairer le périphérique Bluetooth de la Raspberry Pi 3 :

$ bluetoothctl 
[NEW] Controller B8:27:EB:01:98:7E raspberrypi [default]
[bluetooth]# scan on
Discovery started
[bluetooth]# devices
Device 20:15:11:16:76:04 HC-05
Device 34:14:5F:D2:50:D8 Galaxy Tab S2
[bluetooth]# pair 34:14:5F:D2:50:D8
Attempting to pair with 34:14:5F:D2:50:D8
[CHG] Device 34:14:5F:D2:50:D8 Connected: yes
[CHG] Device 34:14:5F:D2:50:D8 Modalias: bluetooth:v0075p0100d0200
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001105-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001112-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001115-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 0000111f-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001802-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001805-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001811-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 UUIDs: 00001902-0000-1000-8000-00805f9b34fb
[CHG] Device 34:14:5F:D2:50:D8 ServicesResolved: yes
[CHG] Device 34:14:5F:D2:50:D8 Paired: yes
Pairing successful
[CHG] Device 34:14:5F:D2:50:D8 ServicesResolved: no
[CHG] Device 34:14:5F:D2:50:D8 Connected: no
[bluetooth]# trust 34:14:5F:D2:50:D8
[CHG] Device 34:14:5F:D2:50:D8 Trusted: yes
Changing 34:14:5F:D2:50:D8 trust succeeded
[bluetooth]# quit

Pour finir, on peut réaliser ce script :

Ou encore mieux, on va créer un service rfcomm :

$ sudo vim /etc/systemd/system/rfcomm.service

[Unit]
Description=RFCOMM service
After=bluetooth.service
Requires=bluetooth.service

[Service]
ExecStartPre=/bin/chmod 777 /var/run/sdp
ExecStart=/usr/bin/rfcomm watch 0 22
Restart=on-failure
RestartSec=5s
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Puis on modifie le service bluetooth pour y intégrer la commande sdptool :

$ sudo vim /etc/systemd/system/dbus-org.bluez.service

[Unit]
Description=Bluetooth service
Documentation=man:bluetoothd(8)
ConditionPathIsDirectory=/sys/class/bluetooth

[Service]
Type=dbus
BusName=org.bluez
ExecStart=/usr/lib/bluetooth/bluetoothd --compat --noplugin=sap
ExecStartPost=/usr/bin/sdptool add --channel=22 SP
NotifyAccess=main
#WatchdogSec=10
#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
ProtectHome=true
ProtectSystem=full

[Install]
WantedBy=bluetooth.target
Alias=dbus-org.bluez.service

Pour activer le service rfcomm au démarrage, il faut faire :

$ sudo systemctl enabled rfcomm.service

Et un petit programme Qt5 en utilisant le module serialport et la classe QSerialPort :

QT += serialport

TEMPLATE = app
TARGET = qt-serial
DEPENDPATH += .
INCLUDEPATH += .

OBJECTS_DIR = ./tmp
MOC_DIR = ./tmp
DESTDIR = ./bin

# Input
SOURCES += main.cpp

Avec un client Android :

On obtient côté Raspberry Pi :

$ bin/qt-serial 
<debug> etat ouverture port : 1
"données reçues :" "ok\r\n"
<debug> etat ouverture port : 0

Avec un module HC-05, on pourra simplement faire :

$ sudo rfcomm bind 0 20:15:11:16:76:04

Voir aussi : Mise en oeuvre d’un port série sous Qt

Android et Bluetooth

Voir : Activité n°4 : Bluetooth

BlueZ

BlueZ est un logiciel qui met en œuvre la technologie sans fil Bluetooth sur le système d’exploitation Linux. Il est intégré au noyau Linux.

Retour au sommaire