Site : tvaira.free.fr
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.
BLE (Bluetooth Low Energy, Bluetooth à basse consommation ou Bluetooth à basse énergie) est une technique de transmission sans fil créée par Nokia en 2006 sous la forme d’un standard ouvert basé sur Bluetooth, qu’il complète mais sans le remplacer. Cette technologie est apparue en 2010 avec la sortie de la version 4.0 du Bluetooth Core Specification.
Comparé au Bluetooth, le BLE permet un débit du même ordre de grandeur (1 Mb/s) pour une consommation d’énergie 10 fois moindre. Cela permet d’intégrer cette technologie dans de nouveaux types d’équipements : montres, appareils de surveillance médicale ou capteurs pour sportifs.
Lire : Bluetooth BLE
L’ESP32 est un microcontrôleur intégrant notamment le WiFi et le Bluetooth :
Lire : Mise en oeuvre du module ESP32
Dans l’IDE Arduino, on sélectionne la carte dans Outils -> Type de carte :
On va utiliser la bibliothèque intégrée au SDK ESP32 pour l’Arduino : ESP32_BLE_Arduino. Cette bibliothèque est installée par défaut lorsque vous avez installé l’ESP32 pour l’EDI Arduino.
L’ESP32 peut agir en tant que serveur BLE (ou en tant que client BLE). Il existe plusieurs exemples BLE pour l’ESP32 dans cette bibliothèque :
Pour créer un serveur BLE, on doit suivre les étapes suivantes :
BLEDevice::init()
BLEDevice::createServer()
BLEServer::createService()
BLEService::createCharacteristic()
BLECharacteristic::addDescriptor()
BLEService::start()
BLEServer::getAdvertising()->start()
Pour cela, on utilisera les classes suivantes :
Pour lire et/ou écrire la valeur d’une caractéristique, on utilisera les méthodes de la classe BLECharacteristic :
getValue()
: qui retourne la valeur sous la forme d’un std::string
setValue()
: qui fixe la valeur sous la forme soit d’une chaîne de caractère (char *
ou std::string
), soit d’un nombre entier ou soit d’un nombre en virgule flottante (float
ou double
)notify()
: qui permet de notifier un changement de la valeurLe serveur BLE peut recevoir des demandes de lecture pour obtenir la valeur actuelle de la caractéristique ou des demandes d’écriture pour définir une nouvelle valeur. Mais le serveur BLE peut aussi vouloir “transmettre” des données lorsque quelque chose d’intéressant se produit. Cette opération s’appelle “notify” (notification). Elle est utilisée pour signaler (ou notifier) au client que la valeur de la caractéristique a changé. Le client recevra un événement d’indication pour l’informer de la modification.
Remarque : Il existe uen fonction similaire à notify()
appelée indicate()
. La différence est qu’indicate()
reçoit une confirmation, tandis que notify()
n’en reçoit pas.
Pour disposer des indications/notifications, il faut ajouter un descripteur BLE appelé “Configuration des caractéristiques du client” qui a pour UUID 0x2902. Celui-ci contient (entre autres choses) deux champs de bits distincts pouvant être activés ou désactivés :
Attention : La spécification BLE limite la quantité maximale de données pouvant être envoyée via une notification (ou une indication) à 20 octets maximum. Si la valeur d’une caractéristique est supérieure à cette quantité, seuls les 20 premiers octets des données seront transmis.
La bibliothèque utilise aussi le principe des fonctions de rappel (callback) sur les évènements suivants :
onRead
) et écriture (onWrite
) d’une caractéristiqueonConnect
) et déconnexion (onDisconnect
) du serveur BLECes fonctions sont des méthodes à redéfinir des classes :
Il faudra donc dériver ces classes pour écrire ses propres fonctions de rappel :
class MyCallback : public BLECharacteristicCallbacks
{
void onRead(BLECharacteristic* pCharacteristic)
{
// Do something before the read completes
}
void onWrite(BLECharacteristic* pCharacteristic)
{
// Do something because a new value was written.
}
};
class MyServerCallback : public BLEServerCallbacks
{
void onConnect(BLEServer* pServer)
{
// Do something when a connection occurs.
}
void onDisconnect(BLEServer* pServer)
{
// Do something when a disconnection occurs.
}
};
Puis, il faudra les installer en appelant :
BLECharacteristic::setCallbacks(new MyCallback())
BLEServer::setCallbacks(new MyServerCallbacks())
Remarque : Les méthodes onConnect
et onDisconnect
peuvent être utilisées pour activer ou désactiver les lectures du capteur. Par exemple, si aucun client n’est connecté, il n’est pas nécessaire de dépenser de l’énergie pour échantillonner une valeur s’il n’y a personne pour la lire. Cependant, lorsqu’un client se connecte, nous pouvons le détecter et commencer à lire le capteur jusqu’à ce qu’une indication de déconnexion soit détectée.
On va utiliser notre ESP32 comme un simple compteur (8 bits). Cette donnée pourra donc être lue ou notifiée à chaque changement de valeur du compteur.
Pour faire en sorte que notre notre caractéristique soit opérationnelle, il faudra :
Il faudra donc compléter la table d’attributs suivante :
Chaque service, caractéristique et descripteur doit posséder un UUID (identificateur unique universel). Un UUID est codé sur 128 bits (16 octets). Par exemple : 4fafc201-1fb5-459e-8fcc-c5c9c331914b
Remarque : Il existe des UUID sur 16 bits (ou de 32 bits) abrégés pour les standards du SIG (Bluetooth Special Interest Group).
On peut utiliser un générateur d’UUID en ligne : www.uuidgenerator.net
Tester sur une carte Wemos LOLIN ESP32 OLED, on obtient :
Notre serveur BLE fournit deux services : celui que l’on a créé (042bd80f-14f6-42be-a45c-a62836a4fa3f) et le “Generic Attribute” (0x1801) :
On va lire maintenant notre caractéristique (“065de41b-79fb-479d-b592-47caf39bfccb”) :
#define DISPLAY_SSD1306Wire
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define SERVICE_UUID "042bd80f-14f6-42be-a45c-a62836a4fa3f"
#define CHARACTERISTIC_UUID "065de41b-79fb-479d-b592-47caf39bfccb"
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
#define NOTIFY_CHARACTERISTIC
bool estConnecte = false;
bool etaitConnecte = false;
uint8_t valeur = 0; // le compteur
class EtatServeur : public BLEServerCallbacks
{
void onConnect(BLEServer* pServer)
{
estConnecte = true;
}
void onDisconnect(BLEServer* pServer)
{
estConnecte = false;
}
};
#ifdef DISPLAY_SSD1306Wire
#include "SSD1306Wire.h"
SSD1306Wire display(0x3c, 5, 4);
void initDisplay();
void afficherMessage(String msg, int duree);
void afficherDatas(String msg, String datas, int duree);
#endif
void setup()
{
Serial.begin(115200);
Serial.println("Test BLE init");
#ifdef DISPLAY_SSD1306Wire
initDisplay();
afficherMessage("Test BLE init server", 0);
#endif
Serial.println("Test BLE init server");
BLEDevice::init("MonESP32");
//BLEDevice::getAddress(); // Retrieve our own local BD BLEAddress
pServer = BLEDevice::createServer();
pServer->setCallbacks(new EtatServeur());
BLEService *pService = pServer->createService(SERVICE_UUID);
#ifdef NOTIFY_CHARACTERISTIC
pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Crée un descripteur : Client Characteristic Configuration (pour les indications/notifications)
pCharacteristic->addDescriptor(new BLE2902());
#else
pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
#endif
pService->start();
pServer->getAdvertising()->start();
//BLEAdvertising *pAdvertising = pServer->getAdvertising();
//pAdvertising->start();
Serial.println("Test BLE start advertising");
Serial.println("Test BLE wait connection");
#ifdef DISPLAY_SSD1306Wire
afficherMessage("Test BLE wait", 1000);
#endif
}
void loop()
{
bool fini = false;
while(!fini)
{
// notification
if (estConnecte)
{
pCharacteristic->setValue(&valeur, 1); // la nouvelle valeur du compteur
#ifdef NOTIFY_CHARACTERISTIC
pCharacteristic->notify();
#endif
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
String datas(valeur);
//Serial.println("BLE notify");
Serial.printf("BLE notify : %d\n", valeur);
#ifdef DISPLAY_SSD1306Wire
afficherDatas("BLE notify", datas, 500);
#endif
valeur++; // on compte ...
}
// déconnecté ?
if (!estConnecte && etaitConnecte)
{
Serial.println("BLE deconnection");
#ifdef DISPLAY_SSD1306Wire
afficherMessage("BLE deconnecte", 500);
#else
delay(500); // give the bluetooth stack the chance to get things ready
#endif
pServer->startAdvertising(); // restart advertising
Serial.println("BLE restart advertising");
Serial.println("Test BLE wait connection");
#ifdef DISPLAY_SSD1306Wire
afficherMessage("Test BLE wait", 0);
#endif
etaitConnecte = estConnecte;
}
// connecté ?
if (estConnecte && !etaitConnecte)
{
Serial.println("BLE connection");
#ifdef DISPLAY_SSD1306Wire
afficherMessage("BLE connecte", 0);
#endif
etaitConnecte = estConnecte;
}
}
}
// ...
Code source : exemple_ble_1.ino
On peut aussi tester avec l’application mobile nRF Connect for Mobile.
Notre serveur BLE fournit trois services : celui que l’on a créé (042bd80f-14f6-42be-a45c-a62836a4fa3f), le “Generic Access” (0x1800) et le “Generic Attribute” (0x1801) :
On va lire maintenant notre caractéristique (“065de41b-79fb-479d-b592-47caf39bfccb”) :
On peut aussi demander les indications/notifications :
Activer les options de développement de la tablette, puis la journalisation des paquets Blutooth :
Maintenant, la tablette enregistrera tous les paquets Blutooth échangés dans un fichier de journalisation.
Il suffit ensuite de récupérer le fichier btsnoop_hci.log
dans le dossier Android/data/
. On le renomme en btsnoop_hci.pcap
et on l’ouvre dans Wireshark. On peut directement filtrer la réponse à la demande de lecture de la caractéristique avec btatt.opcode.method==0x0b
:
La transmission UART sur BLE ne fait pas partie des profils définis par Bluetooth SIG. C’est un service personnalisé par Nordic Semiconductor. Nordic Semiconductor (fabricant des puces nRFxxx) a créé le Nordic UART Service (NUS) qui est un exemple d’émulation d’un port série sur BLE. Cela inclut le service “Nordic UART” dont l’UUID spécifique est 6E400001-B5A3-F393-E0A9-E50E24DCCA9E.
Ce service présente deux caractéristiques (l’une pour l’émission et l’autre pour la réception) :
BLE NUS est donc un service BLE propriétaire, en quelque sorte l’équivalent du profil SPP (Serial Port Profile), basé sur le protocole RFCOMM, du Bluetooth Classic.
Liens :
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define SERVICE_UART_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
BLEServer* pServer = NULL;
BLECharacteristic* pTxCharacteristic = NULL;
BLECharacteristic* pRxCharacteristic = NULL;
bool estConnecte = false;
bool etaitConnecte = false;
uint8_t valeur = 0;
class EtatServeur : public BLEServerCallbacks
{
void onConnect(BLEServer* pServer)
{
estConnecte = true;
}
void onDisconnect(BLEServer* pServer)
{
estConnecte = false;
}
};
class CharacteristicUART : public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristique)
{
std::string rxValue = pCharacteristique->getValue();
if (rxValue.length() > 0)
{
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
Serial.print(rxValue[i]);
Serial.println();
Serial.println("*********");
}
}
};
void setup()
{
Serial.begin(115200);
Serial.println("UART Over BLE init");
BLEDevice::init("MonESP32");
//BLEDevice::getAddress(); // Retrieve our own local BD BLEAddress
pServer = BLEDevice::createServer();
pServer->setCallbacks(new EtatServeur());
BLEService *pServiceUART = pServer->createService(SERVICE_UART_UUID);
pTxCharacteristic = pServiceUART->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
// Create a BLE Descriptor : Client Characteristic Configuration (for indications/notifications)
pTxCharacteristic->addDescriptor(new BLE2902());
pRxCharacteristic = pServiceUART->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
pRxCharacteristic->setCallbacks(new CharacteristicUART());
pServiceUART->start();
pServer->getAdvertising()->start();
//BLEAdvertising *pAdvertising = pServer->getAdvertising();
//pAdvertising->start();
Serial.println("UART Over BLE start advertising");
Serial.println("UART Over BLE wait connection");
}
void loop()
{
bool fini = false;
while(!fini)
{
// notification
if (estConnecte)
{
String datas(valeur);
pTxCharacteristic->setValue(&valeur, 1);
pTxCharacteristic->notify();
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
valeur++;
}
// déconnecté ?
if (!estConnecte && etaitConnecte)
{
Serial.println("UART Over BLE deconnection");
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("UART Over BLE restart advertising");
etaitConnecte = estConnecte;
}
// connecté ?
if (estConnecte && !etaitConnecte)
{
Serial.println("UART Over BLE connection");
etaitConnecte = estConnecte;
}
}
}
Code source : exemple_ble_2.ino
Exemple (avec une classe sécifique) : esp32-ble-uart.zip
Tester sur une carte AZDelivery ESP32 :
Voici les résultats obtenus avec l’application mobile nRF Connect for Mobile :
Maintenant, on active les notifications :
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. BLE (Bluetooth Low Energy, Bluetooth à basse consommation ou Bluetooth à basse énergie) est apparu en 2010 avec la sortie de la version 4.0 du Bluetooth Core Specification.
Lire : Bluetooth BLE et Bluetooth Classic
L’API Qt Bluetooth Low Energy a été introduite dans Qt 5.4. Depuis Qt 5.5, cette partie de l’API est définitive et une garantie de compatibilité est donnée pour les versions futures. Depuis Qt 5.7, une API supplémentaire prenant en charge le rôle de périphérique a été ajoutée, avec le backend implémenté pour Linux/BlueZ, iOS et macOS.
Activités :
Android 4.3 (API de niveau 18) introduit le support pour le Bluetooth Low Energy (BLE) dans le rôle central et fournit des API que les applications peuvent utiliser pour découvrir des périphériques, rechercher des services et transmettre des informations.
Activité : Mise en oeuvre du Bluetooth BLE sous Android