Aperçu de Bluetooth Low Energy
L'API Qt Bluetooth Low Energy prend en charge les rôles périphérique/serveur et central/client. Elle est prise en charge sur toutes les principales plateformes Qt. La seule exception est l'absence de prise en charge du rôle périphérique sous Windows.
Qu'est-ce que Bluetooth Low Energy ?
Bluetooth Low Energy, également connu sous le nom de Bluetooth Smart, est une technologie de réseau informatique sans fil qui a été officiellement introduite en 2011. Elle fonctionne sur la même fréquence de 2,4 GHz que le Bluetooth "classique". La principale différence est, comme l'indique le nom de la technologie, la faible consommation d'énergie. Elle permet aux appareils Bluetooth Low Energy de fonctionner pendant des mois, voire des années, avec des piles rechargeables. Cette technologie a été introduite par la version 4.0 de Bluetooth. Les appareils qui prennent en charge cette technologie sont appelés "Bluetooth Smart Ready Devices". Les principales caractéristiques de cette technologie sont les suivantes
- une consommation d'énergie ultra-faible en crête, en moyenne et en mode inactif
- Capacité à fonctionner pendant des années avec des piles standard de type "coin-cell".
- Faible coût
- Interopérabilité multi-fournisseurs
- Portée améliorée
Bluetooth Low Energy utilise une architecture client-serveur. Le serveur (également connu sous le nom de périphérique) offre des services tels que la température ou la fréquence cardiaque et en fait la publicité. Le client (appelé dispositif central) se connecte au serveur et lit les valeurs annoncées par le serveur. Un appartement équipé de capteurs Bluetooth Smart Ready, tels qu'un thermostat, un capteur d'humidité ou de pression, en est un exemple. Ces capteurs sont des périphériques qui annoncent les valeurs de l'environnement de l'appartement. Parallèlement, un téléphone portable ou un ordinateur peut se connecter à ces capteurs, récupérer leurs valeurs et les présenter à l'utilisateur dans le cadre d'une application plus large de contrôle de l'environnement.
Structure de base du service
Bluetooth Low Energy est basé sur deux protocoles : ATT (Attribute Protocol) et GATT (Generic Attribute Profile). Ils spécifient les couches de communication utilisées par chaque appareil Bluetooth Smart Ready.
Protocole ATT
L'élément de base du protocole ATT est un attribut. Chaque attribut se compose de trois éléments :
- une valeur - la charge utile ou l'information souhaitée
- un UUID - le type d'attribut (utilisé par le GATT)
- une poignée de 16 bits - un identifiant unique pour l'attribut
Le serveur stocke les attributs et le client utilise le protocole ATT pour lire et écrire les valeurs sur le serveur.
Profil GATT
Le profil GATT définit le regroupement d'un ensemble d'attributs en appliquant une signification à des UUID prédéfinis. Le tableau ci-dessous montre un exemple de service exposant une fréquence cardiaque pour un jour donné. Les valeurs réelles sont stockées dans les deux caractéristiques :
| Handle | UUID | Valeur | Description |
|---|---|---|---|
| 0x0001 | 0x2800 | UUID 0x180D | Début du service de fréquence cardiaque |
| 0x0002 | 0x2803 | UUID 0x2A37, Poignée de valeur : 0x0003 | Caractéristique du type Mesure de la fréquence cardiaque (HRM) |
| 0x0003 | 0x2A37 | 65 bpm | Valeur de la fréquence cardiaque |
| 0x0004 | 0x2803 | UUID 0x2A08, poignée de valeur : 0x0005 | Caractéristique de type Date Heure |
| 0x0005 | 0x2A08 | 18/08/2014 11:00 | Date et heure de la mesure |
| 0x0006 | 0x2800 | UUID xxxxxx | Début du service suivant |
| ... | ... | ... | ... |
Le GATT précise que l'UUID 0x2800 utilisé ci-dessus marque le début de la définition d'un service. Chaque attribut suivant 0x2800 fait partie du service jusqu'au prochain 0x2800 ou jusqu'à la fin. De la même manière, l'UUID bien connu 0x2803 indique qu'une caractéristique doit être trouvée et que chacune des caractéristiques a un type définissant la nature de la valeur. L'exemple ci-dessus utilise les UUID 0x2A08 (date et heure) et 0x2A37 (mesure de la fréquence cardiaque). Chacun de ces UUID est défini par le Bluetooth Special Interest Group et se trouve dans les spécifications du GATT. Bien qu'il soit conseillé d'utiliser des UUID prédéfinis lorsqu'ils sont disponibles, il est tout à fait possible d'utiliser des UUID nouveaux et non encore utilisés pour les types de caractéristiques et de services.
En général, chaque service peut consister en une ou plusieurs caractéristiques. Une caractéristique contient des données et peut être décrite par des descripteurs, qui fournissent des informations supplémentaires ou des moyens de manipuler la caractéristique. Tous les services, caractéristiques et descripteurs sont reconnus par leur UUID de 128 bits. Enfin, il est possible d'inclure des services à l'intérieur de services (voir l'image ci-dessous).

Utilisation de Qt Bluetooth Low Energy API
Cette section décrit comment utiliser l'API Bluetooth Low Energy fournie par Qt Bluetooth. Côté client, l'API permet de créer des connexions avec des périphériques, de découvrir leurs services, ainsi que de lire et d'écrire des données stockées sur le périphérique. Côté serveur, elle permet de mettre en place des services, de les annoncer et d'être notifié lorsque le client écrit des caractéristiques. L'exemple de code ci-dessous est tiré des exemples Heart Rate Game et Heart Rate Server.
Établissement d'une connexion
Pour pouvoir lire et écrire les caractéristiques d'un périphérique Bluetooth Low Energy, il est nécessaire de le trouver et de le connecter. Pour ce faire, le périphérique doit annoncer sa présence et ses services. Nous commençons la découverte du périphérique à l'aide de la classe QBluetoothDeviceDiscoveryAgent. Nous nous connectons à son signal QBluetoothDeviceDiscoveryAgent::deviceDiscovered() et commençons la recherche avec start() :
m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(15000); connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice); connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, this, &DeviceFinder::scanError); connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished); connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished); m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
Comme nous ne nous intéressons qu'aux appareils à faible consommation d'énergie, nous filtrons le type d'appareil dans la fente de réception. Le type d'appareil peut être déterminé à l'aide de l'indicateur QBluetoothDeviceInfo::coreConfigurations(). Le signal deviceDiscovered() peut être émis plusieurs fois pour le même appareil au fur et à mesure que de nouveaux détails sont découverts. Ici, nous faisons correspondre ces découvertes de dispositifs de sorte que l'utilisateur ne voit que les dispositifs individuels :
void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device) { // If device is LowEnergy-device, add it to the list if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { auto devInfo = new DeviceInfo(device); auto it = std::find_if(m_devices.begin(), m_devices.end(), [devInfo](DeviceInfo *dev) { return devInfo->getAddress() == dev->getAddress(); }); if (it == m_devices.end()) { m_devices.append(devInfo); } else { auto oldDev = *it; *it = devInfo; delete oldDev; } setInfo(tr("Low Energy device found. Scanning more...")); setIcon(IconProgress); } //... }
Une fois que l'adresse du périphérique est connue, nous utilisons la classe QLowEnergyController. Cette classe est le point d'entrée pour tous les développements Bluetooth Low Energy. Le constructeur de la classe accepte l'adresse QBluetoothAddress du périphérique distant. Enfin, nous configurons les emplacements habituels et nous nous connectons directement au périphérique à l'aide de connectToDevice() :
m_control = QLowEnergyController::createCentral(m_currentDevice->getDevice(), this); connect(m_control, &QLowEnergyController::serviceDiscovered, this, &DeviceHandler::serviceDiscovered); connect(m_control, &QLowEnergyController::discoveryFinished, this, &DeviceHandler::serviceScanDone); connect(m_control, &QLowEnergyController::errorOccurred, this, [this](QLowEnergyController::Error error) { Q_UNUSED(error); setError("Cannot connect to remote device."); setIcon(IconError); }); connect(m_control, &QLowEnergyController::connected, this, [this]() { setInfo("Controller connected. Search services..."); setIcon(IconProgress); m_control->discoverServices(); }); connect(m_control, &QLowEnergyController::disconnected, this, [this]() { setError("LowEnergy controller disconnected"); setIcon(IconError); }); // Connect m_control->connectToDevice();
Service Search
L'extrait de code ci-dessus montre comment l'application lance la recherche de services une fois la connexion établie.
Le slot serviceDiscovered() ci-dessous est déclenché à la suite du signal QLowEnergyController::serviceDiscovered() et fournit un rapport de progression intermittent. Étant donné que nous parlons de l'application Heart Listener qui surveille les appareils HeartRate à proximité, nous ignorons tout service qui n'est pas de type QBluetoothUuid::ServiceClassUuid::HeartRate.
void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt) { if (gatt == QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate)) { setInfo("Heart Rate service discovered. Waiting for service scan to be done..."); setIcon(IconProgress); m_foundHeartRateService = true; } }
Finalement, le signal QLowEnergyController::discoveryFinished() est émis pour indiquer la réussite de la découverte du service. Si un service HeartRate a été trouvé, une instance QLowEnergyService est créée pour représenter le service. L'objet de service renvoyé fournit les signaux requis pour les notifications de mise à jour et la découverte des détails du service est déclenchée à l'aide de QLowEnergyService::discoverDetails() :
// If heartRateService found, create new service if (m_foundHeartRateService) m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate), this); if (m_service) { connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged); connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue); connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite); m_service->discoverDetails(); } else { setError("Heart Rate Service not found."); setIcon(IconError); }
Pendant la recherche des détails, l'instance state() du service passe de RemoteService à RemoteServiceDiscovering et se termine finalement par RemoteServiceDiscovered:
void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s) { switch (s) { case QLowEnergyService::RemoteServiceDiscovering: setInfo(tr("Discovering services...")); setIcon(IconProgress); break; case QLowEnergyService::RemoteServiceDiscovered: { setInfo(tr("Service discovered.")); setIcon(IconBluetooth); const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement)); if (!hrChar.isValid()) { setError("HR Data not found."); setIcon(IconError); break; } m_notificationDesc = hrChar.descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); if (m_notificationDesc.isValid()) m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100")); break; } default: //nothing for now break; } emit aliveChanged(); }
Interaction avec le périphérique
Dans l'exemple de code ci-dessus, la caractéristique souhaitée est de type HeartRateMeasurement. Étant donné que l'application mesure les variations de la fréquence cardiaque, elle doit activer les notifications de modification pour la caractéristique. Notez que toutes les caractéristiques ne fournissent pas de notifications de modification. La caractéristique HeartRate ayant été normalisée, on peut supposer que des notifications peuvent être reçues. En fin de compte, l'indicateur QLowEnergyCharacteristic::Notify doit être activé sur QLowEnergyCharacteristic::properties() et un descripteur de type QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration doit exister pour confirmer la disponibilité d'une notification appropriée.
Enfin, nous traitons la valeur de la caractéristique HeartRate, conformément à la norme Bluetooth Low Energy :
void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value) { // ignore any other characteristic change -> shouldn't really happen though if (c.uuid() != QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement)) return; auto data = reinterpret_cast<const quint8 *>(value.constData()); quint8 flags = *data; //Heart Rate int hrvalue = 0; if (flags & 0x1) // HR 16 bit? otherwise 8 bit hrvalue = static_cast<int>(qFromLittleEndian<quint16>(data[1])); else hrvalue = static_cast<int>(data[1]); addMeasurement(hrvalue); }
En général, une valeur de caractéristique est une série d'octets. L'interprétation précise de ces octets dépend du type de caractéristique et de la structure de la valeur. Un grand nombre d'entre eux ont été normalisés par le Bluetooth SIG, tandis que d'autres peuvent suivre un protocole personnalisé. L'extrait de code ci-dessus montre comment lire la valeur normalisée HeartRate.
Services de publicité
Si nous mettons en œuvre une application serveur GATT sur un périphérique, nous devons définir les services que nous voulons offrir aux dispositifs centraux et les annoncer :
QLowEnergyAdvertisingData advertisingData ; advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral) ; advertisingData.setIncludePowerLevel(true) ; advertisingData.setLocalName("HeartRateServer") ; advertisingData.setServices(QList<QBluetoothUuid>()<< QBluetoothUuid::ServiceClassUuid::HeartRate) ;bool errorOccurred = false;const std::unique_ptr<QLowEnergyController> leController(QLowEnergyController::createPeripheral()) ;auto errorHandler = [&leController, &errorOccurred](QLowEnergyController::Error errorCode) { qWarning().noquote().nospace() << errorCode << " occurred: " << leController->errorString() ; if (errorCode ! = QLowEnergyController::RemoteHostClosedError) { qWarning("Heartrate-server quitting due to the error."); errorOccurred = true; QCoreApplication::quit() ; } } ;QObject::connect(leController.get(), &QLowEnergyController::errorOccurred, errorHandler) ; std::unique_ptr<QLowEnergyService> service(leController->addService(serviceData)) ; leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,advertisingData) ;if (errorOccurred) return-1;
Désormais, les clients potentiels peuvent se connecter à notre appareil, découvrir le service fourni et s'enregistrer pour être informés des modifications de la valeur de la caractéristique. Cette partie de l'API a déjà été couverte par les sections précédentes.
Mise en œuvre d'un service sur le périphérique
La première étape consiste à définir le service, ses caractéristiques et ses descripteurs. Pour ce faire, on utilise les classes QLowEnergyServiceData, QLowEnergyCharacteristicData et QLowEnergyDescriptorData. Ces classes servent de conteneurs ou de blocs de construction pour les informations essentielles qui composent le service Bluetooth Low Energy à définir. L'extrait de code ci-dessous définit un simple service HeartRate qui publie les battements mesurés par minute. Une montre-bracelet est un exemple d'utilisation d'un tel service.
QLowEnergyCharacteristicData charData; charData.setUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement); charData.setValue(QByteArray(2, 0)); charData.setProperties(QLowEnergyCharacteristic::Notify); const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration, QByteArray(2, 0)); charData.addDescriptor(clientConfig); QLowEnergyServiceData serviceData; serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); serviceData.setUuid(QBluetoothUuid::ServiceClassUuid::HeartRate); serviceData.addCharacteristic(charData);
L'objet serviceData qui en résulte peut être publié comme décrit dans la section Services publicitaires ci-dessus. Malgré le chevauchement partiel des informations enveloppées par QLowEnergyServiceData et QLowEnergyAdvertisingData, les deux classes remplissent deux tâches très différentes. Les données publicitaires sont publiées à l'intention des appareils proches et leur portée est souvent limitée en raison de leur taille restreinte à 29 octets. Elles ne sont donc pas toujours complètes à 100 %. En comparaison, les données de service contenues dans QLowEnergyServiceData fournissent l'ensemble complet des données de service et ne deviennent visibles pour le client connecté que lorsqu'une connexion avec une découverte de service active a été effectuée.
La section suivante montre comment le service peut mettre à jour la valeur de la fréquence cardiaque. Selon la nature du service, il peut être nécessaire de se conformer à la définition officielle du service telle qu'elle est définie sur le site https://www.bluetooth.org. D'autres services peuvent être entièrement personnalisés. Le service de fréquence cardiaque a été adopté et sa spécification peut être consultée à l'adresse https://www.bluetooth.com/specifications/adopted-specifications.
QTimer heartbeatTimer; quint8 currentHeartRate = 60; enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp; const auto heartbeatProvider = [&service, ¤tHeartRate, &valueChange]() { QByteArray value; value.append(char(0)); // Flags that specify the format of the value. value.append(char(currentHeartRate)); // Actual value. QLowEnergyCharacteristic characteristic = service->characteristic(QBluetoothUuid::CharacteristicType::HeartRateMeasurement); Q_ASSERT(characteristic.isValid()); service->writeCharacteristic(characteristic, value); // Potentially causes notification. if (currentHeartRate == 60) valueChange = ValueUp; else if (currentHeartRate == 100) valueChange = ValueDown; if (valueChange == ValueUp) ++currentHeartRate; else --currentHeartRate; }; QObject::connect(&heartbeatTimer, &QTimer::timeout, heartbeatProvider); heartbeatTimer.start(1000);
En général, les mises à jour des caractéristiques et des valeurs des descripteurs sur le périphérique utilisent les mêmes méthodes que la connexion des appareils Bluetooth Low Energy.
Note : Pour utiliser Qt Bluetooth (dans les rôles central et périphérique) sur iOS, vous devez fournir un fichier Info.plist contenant la description de l'utilisation. Selon la documentation de CoreBluetooth : Votre application se plantera si son Info.plist ne contient pas de clés de description d'utilisation pour les types de données auxquels elle doit accéder. Pour accéder aux API Core Bluetooth dans les applications liées à iOS 13 ou ultérieures, incluez la clé NSBluetoothAlwaysUsageDescription. Dans iOS 12 et les versions antérieures, incluez NSBluetoothPeripheralUsageDescription pour accéder aux données des périphériques Bluetooth.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.