ESP32 BLE (Bluetooth Low Energy) : Serveur et Client

Rédigé par sazaki Aucun commentaire
Classé dans : Non classé Mots clés : aucun

Apprenez à établir une connexion BLE (Bluetooth Low Energy) entre deux cartes ESP32. Un ESP32 sera le serveur et l'autre ESP32 sera le client. Le serveur BLE annonce des caractéristiques qui contiennent des lectures de capteur que le client peut lire. Le client ESP32 BLE lit les valeurs de ces caractéristiques (température et humidité) et les affiche sur un écran OLED.

ESP32 BLE Server and Client BLuetooth Low Energy Arduino IDE

Qu'est-ce que le Bluetooth Low Energy ?

Avant de passer directement au projet, il est important de jeter un coup d'œil rapide sur certains concepts BLE essentiels afin de pouvoir mieux comprendre le projet par la suite.

Bluetooth Low Energy, BLE en abrégé, est une variante de Bluetooth économe en énergie. L'application principale de BLE est la transmission à courte distance de petites quantités de données (faible bande passante). Contrairement au Bluetooth qui est toujours activé, le BLE reste constamment en mode veille, sauf lorsqu'une connexion est établie.

Cela lui fait consommer très peu d'énergie. BLE consomme environ 100 fois moins d'énergie que Bluetooth (selon le cas d'utilisation).

Serveur et client BLE

Avec Bluetooth Low Energy, il existe deux types d'appareils : le serveur et le client. L'ESP32 peut agir en tant que client ou en tant que serveur.

Le serveur annonce son existence, afin qu'il puisse être trouvé par d'autres appareils et contient des données que le client peut lire. Le client analyse les appareils à proximité et lorsqu'il trouve le serveur qu'il recherche, il établit une connexion et écoute les données entrantes. C'est ce qu'on appelle la communication point à point.

BLE Client Server Server Advertising

Il existe d'autres modes de communication possibles comme le mode de diffusion et le réseau maillé (non couverts dans ce tutoriel).

GATT

GATT signifie Generic Attributes et définit une structure de données hiérarchique qui est exposée aux appareils BLE connectés. Cela signifie que le GATT définit la manière dont deux appareils BLE envoient et reçoivent des messages standard. Comprendre cette hiérarchie est important car cela facilitera la compréhension de l'utilisation de BLE avec l'ESP32.

GATT Hierarchy ESP32 BLE Server Client Example
  • Profil : ensemble standard de services pour un cas d'utilisation spécifique ;
  • Service : ensemble d'informations connexes, telles que les lectures de capteurs, niveau de batterie, fréquence cardiaque, etc. ;
  • Caractéristique : c'est là que les données réelles sont enregistrées sur la hiérarchie (valeur) ;
  • Descripteur : métadonnées sur les données ;
  • Propriétés : décrire comment la valeur caractéristique peut être interagi avec. Par exemple : lire, écrire, notifier, diffuser, indiquer, etc.

Dans notre exemple, nous allons créer un service avec deux caractéristiques. Un pour la température et un autre pour l'humidité. Les lectures réelles de température et d'humidité sont enregistrées sur la valeursous leurs caractéristiques. Chaque caractéristique a la propriété notifier , de sorte qu'elle informe le client chaque fois que les valeurs changent.

UUID

Chaque service, caractéristique et descripteur possède un UUID (Universally Unique Identifier). Un UUID est un numéro unique de 128 bits (16 octets). Par exemple :

55072829-bc9e-4c53-938a-74a6d4c78776

Il existe des UUID raccourcis pour tous les types, services et profils spécifiés dans le SIG (Groupe d'Intérêt Spécial Bluetooth).

Mais si votre application a besoin de son propre UUID, vous pouvez le générer en utilisant ce site Web générateur d'UUID.

En résumé, l'UUID est utilisé pour identifier de manière unique les informations. Par exemple, il peut identifier un service particulier fourni par un appareil Bluetooth.

Aperçu du projet

Dans ce tutoriel, vous allez apprendre à établir une connexion BLE entre deux cartes ESP32. Un ESP32 sera le serveur BLE et l'autre ESP32 sera le client BLE.

ESP32 BLE Client Server OLED Display Demonstration

Le serveur ESP32 BLE est connecté à un capteur BME280 et il met à jour ses valeurs caractéristiques de température et d'humidité toutes les 30 secondes.

Le client ESP32 se connecte au serveur BLE et est informé de ses valeurs caractéristiques de température et d'humidité. Cet ESP32 est connecté à un écran OLED et imprime les dernières lectures.

Pièces requises

Voici une liste des pièces nécessaires pour suivre ce projet :

Serveur ESP32 BLE :

  • Carte ESP32 DOIT DEVKIT V1
  • Capteur BME280
  • Câbles de raccordement
  • Breadboard
  • Smartphone avec Bluetooth (en option)

Client ESP32 BLE :

  • Carte ESP32 DOIT DEVKIT V1
  • Écran OLED
  • Câbles de raccordement
  • Breadboard

1) Serveur ESP32 BLE

Dans cette partie, nous allons configurer le serveur BLE qui annonce un service contenant deux caractéristiques : une pour la température et une autre pour l'humidité. Ces caractéristiques ont la propriété Notify pour notifier les nouvelles valeurs au client.

ESP32 BLE Server Connected to the nRF Connect App

Schéma schématique

Le serveur ESP32 BLE annoncera les caractéristiques de température et d'humidité d'un capteur BME280. Vous pouvez utiliser n'importe quel autre capteur tant que vous ajoutez les lignes requises dans le code.

Nous allons utiliser la communication I2C avec le module de capteur BME280. Pour cela, câblez le capteur aux broches par défaut ESP32 SCL (GPIO 22) et SDA (GPIO 21), comme illustré dans le diagramme schématique suivant.

ESP32 Wiring Circuit to BME280 Schematic Diagram

Installation des bibliothèques BME280

Comme mentionné précédemment, nous annoncerons les lectures de capteur d'un capteur BME280. Vous devez donc installer les bibliothèques pour s'interfacer avec le capteur BME280.

Vous pouvez installer les bibliothèques à l'aide du gestionnaire de bibliothèques Arduino. Accédez à Esquisse> ; Inclure la bibliothèque > Gérer les bibliothèques et rechercher le nom de la bibliothèque.

Installation des bibliothèques (VS Code + PlatformIO)

Si vous utilisez VS Code avec l'extension PlatformIO, copiez ce qui suit dans le fichier platformio.ini pour inclure les bibliothèques.

lib_deps = adafruit/Adafruit Unified Sensor @ ^1.1.4
           adafruit/Adafruit BME280 Library @ ^2.1.2  

Serveur ESP32 BLE – Code

Avec le circuit prêt et les bibliothèques requises installées, copiez le code suivant dans l'IDE Arduino ou dans le fichier main.cpp si vous utilisez VS Code.



#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// La température par défaut est en degrés Celsius
// Commentez la ligne suivante pour la température en Fahrenheit
#define temperatureCelsius

//Nom du serveur BLE
#define bleServerName "BME280_ESP32"

Adafruit_BME280 bme; // I2C

float temp;
float tempF;
float hum;

// Variables de minuterie
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

bool deviceConnected = false;

// Voir ce qui suit pour générer des UUID :
// https://www.uuidgenerator.net/

#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59"

// Caractéristique de température et descripteur
#ifdef temperatureCelsius
 BLECharacteristic bmeTemperatureCelsiusCharacteristics("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor bmeTemperatureCelsiusDescriptor(BLEUUID((uint16_t)0x2902));
#else
 BLECharacteristic bmeTemperatureFahrenheitCharacteristics("f78ebbff-c8b7-4107-93de-889a6a06d408", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor bmeTemperatureFahrenheitDescriptor(BLEUUID((uint16_t)0x2902));
#endif

// Caractéristique et descripteur d'humidité
BLECharacteristic bmeHumidityCharacteristics("ca73b3ba-39f6-4ab3-91ae-186dc9577d99", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor bmeHumidityDescriptor(BLEUUID((uint16_t)0x2903));

// Configurer les rappels onConnect et onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
 void onConnect(BLEServer* pServer) {
   deviceConnected = true;
 };
 void onDisconnect(BLEServer* pServer) {
   deviceConnected = false;
 }
};

void initBME(){
 if (!bme.begin(0x76)) {
   Serial.println("Could not find a valid BME280 sensor, check wiring!");
   while (1);
 }
}

void setup() {
// Démarrer la communication série
 Serial.begin(115200);

// Init capteur BME
 initBME();

// Créer le périphérique BLE
 BLEDevice::init(bleServerName);

// Créer le serveur BLE
 BLEServer *pServer = BLEDevice::createServer();
 pServer->setCallbacks(new MyServerCallbacks());

// Créer le service BLE
 BLEService *bmeService = pServer->createService(SERVICE_UUID);

 // Créer des caractéristiques BLE et créer un descripteur BLE
  // Température
 #ifdef temperatureCelsius
   bmeService->addCharacteristic(&bmeTemperatureCelsiusCharacteristics);
   bmeTemperatureCelsiusDescriptor.setValue("BME temperature Celsius");
   bmeTemperatureCelsiusCharacteristics.addDescriptor(&bmeTemperatureCelsiusDescriptor);
 #else
   bmeService->addCharacteristic(&bmeTemperatureFahrenheitCharacteristics);
   bmeTemperatureFahrenheitDescriptor.setValue("BME temperature Fahrenheit");
   bmeTemperatureFahrenheitCharacteristics.addDescriptor(&bmeTemperatureFahrenheitDescriptor);
 #endif  

// Humidité
 bmeService->addCharacteristic(&bmeHumidityCharacteristics);
 bmeHumidityDescriptor.setValue("BME humidity");
 bmeHumidityCharacteristics.addDescriptor(new BLE2902());

 // Start the service
 bmeService->start();

// Lancer la publicité
 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID);
 pServer->getAdvertising()->start();
 Serial.println("Waiting a client connection to notify...");
}

void loop() {
 if (deviceConnected) {
   if ((millis() - lastTime) > timerDelay) {
// Lire la température en degrés Celsius (valeur par défaut)
     temp = bme.readTemperature();
     // Fahrenheit
     tempF = 1.8*temp +32;
// Lire l'humidité
     hum = bme.readHumidity();

// Notifier la lecture de la température du capteur BME
     #ifdef temperatureCelsius
       static char temperatureCTemp[6];
       dtostrf(temp, 6, 2, temperatureCTemp);
       //Définir la valeur caractéristique de la température et notifier le client connecté
       bmeTemperatureCelsiusCharacteristics.setValue(temperatureCTemp);
       bmeTemperatureCelsiusCharacteristics.notify();
       Serial.print("Temperature Celsius: ");
       Serial.print(temp);
       Serial.print(" ºC");
    #autre
       static char temperatureFTemp[6];
       dtostrf(tempF, 6, 2, temperatureFTemp);
      //Définir la valeur caractéristique de la température et notifier le client connecté
       bmeTemperatureFahrenheitCharacteristics.setValue(temperatureFTemp);
       bmeTemperatureFahrenheitCharacteristics.notify();
       Serial.print("Temperature Fahrenheit: ");
       Serial.print(tempF);
       Serial.print(" ºF");
     #endif

// Notifier la lecture d'humidité de BME
     static char humidityTemp[6];
     dtostrf(hum, 6, 2, humidityTemp);
//Définir la valeur caractéristique d'humidité et notifier le client connecté
     bmeHumidityCharacteristics.setValue(humidityTemp);
     bmeHumidityCharacteristics.notify();   
     Serial.print(" - Humidity: ");
     Serial.print(hum);
     Serial.println(" %");

     lastTime = millis();
   }
 }
}

Vous pouvez télécharger le code, et il fonctionnera immédiatement en annonçant son service avec les caractéristiques de température et d'humidité.

Il existe plusieurs exemples montrant comment utiliser BLE avec l'ESP32 dans la section Exemples. Dans votre IDE Arduino, accédez à Fichier> Exemples > ; Arduino ESP32 BLE. Cette esquisse de serveur est basée sur l'exemple Notifier .

Importer des bibliothèques

Le code commence par importer les bibliothèques requises.

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Choix de l'unité de température

Par défaut, l'ESP envoie la température en degrés Celsius. Vous pouvez commenter la ligne suivante ou la supprimer pour envoyer la température en degrés Fahrenheit.

//Commentez la ligne suivante pour la température en Fahrenheit
#define temperatureCelsius

Nom du serveur BLE

La ligne suivante définit un nom pour notre serveur BLE. Laissez le nom du serveur BLE par défaut. Sinon, le nom du serveur dans le code client doit également être modifié (car ils doivent correspondre).

//BLE server name
#define bleServerName "BME280_ESP32"

Capteur BME280

Créez un objet Adafruit_BME280 appelé bme sur les broches ESP32 I2C par défaut.

Adafruit_BME280 bme; // I2C

Les variables temp, tempF et hum maintenez la température en degrés Celsius, la température en degrés Fahrenheit et l'humidité lue à partir du capteur BME280.

float temp;
float tempF;
float hum;

Autres variables

Les variables de minuterie suivantes définissent la fréquence à laquelle nous voulons écrire dans les caractéristiques de température et d'humidité. Nous définissons la variable timerDelay sur 30 000 millisecondes (30 secondes), mais vous pouvez la modifier.

// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

La variable booléenne deviceConnected nous permet de savoir si un client est connecté au serveur.

bool deviceConnected = false;

UUID BLE

Dans les lignes suivantes, nous définissons les UUID pour le service, pour la caractéristique de température en Celsius, pour la caractéristique de température en Fahrenheit et pour l'humidité.

// https://www.uuidgenerator.net/
#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59"

// Caractéristique de température et descripteur
#ifdef temperatureCelsius
 BLECharacteristic bmeTemperatureCelsiusCharacteristics("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor bmeTemperatureCelsiusDescriptor(BLEUUID((uint16_t)0x2902));
#else
 BLECharacteristic bmeTemperatureFahrenheitCharacteristics("f78ebbff-c8b7-4107-93de-889a6a06d408", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor bmeTemperatureFahrenheitDescriptor(BLEUUID((uint16_t)0x2901));
#endif

// Caractéristique et descripteur d'humidité
BLECharacteristic bmeHumidityCharacteristics("ca73b3ba-39f6-4ab3-91ae-186dc9577d99", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor bmeHumidityDescriptor(BLEUUID((uint16_t)0x2903));

Je recommande de laisser tous les UUID par défaut. Sinon, vous devez également modifier le code côté client afin que le client puisse trouver le service et récupérer les valeurs des caractéristiques.

setup()

Dans setup(), initialisez le moniteur série et le capteur BME280.

// Start serial communication 
Serial.begin(115200);

// Init capteur BME
initBME();

Créez un nouvel appareil BLE avec le nom de serveur BLE que vous avez défini précédemment :

// Create the BLE Device
BLEDevice::init(bleServerName);

Définissez l'appareil BLE en tant que serveur et attribuez une fonction de rappel.

// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());

La fonction de rappel MyServerCallbacks() change la variable booléenne deviceConnected en true ou false selon l'état actuel de l'appareil BLE. Cela signifie que si un client est connecté au serveur, l'état est true. Si le client se déconnecte, la variable booléenne devient false. Voici la partie du code qui définit la fonction MyServerCallbacks().

//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
 void onConnect(BLEServer* pServer) {
   deviceConnected = true;
 };
 void onDisconnect(BLEServer* pServer) {
   deviceConnected = false;
 }
};

Démarrez un service BLE avec l'UUID de service défini précédemment.

BLEService *bmeService = pServer->createService(SERVICE_UUID);

Ensuite, créez la caractéristique de température BLE. Si vous utilisez des degrés Celsius, il définit la caractéristique et le descripteur suivants :

#ifdef temperatureCelsius
 bmeService->addCharacteristic(&bmeTemperatureCelsiusCharacteristics);
 bmeTemperatureCelsiusDescriptor.setValue("BME temperature Celsius");
 bmeTemperatureCelsiusCharacteristics.addDescriptor(new BLE2902());

Sinon, il définit la caractéristique Fahrenheit :

#else
 bmeService->addCharacteristic(&dhtTemperatureFahrenheitCharacteristics);
 bmeTemperatureFahrenheitDescriptor.setValue("BME temperature Fahrenheit");
 bmeTemperatureFahrenheitCharacteristics.addDescriptor(new BLE2902());
#endif  

Après cela, il définit la caractéristique d'humidité :

// Humidity
bmeService->addCharacteristic(&bmeHumidityCharacteristics);
bmeHumidityDescriptor.setValue("BME humidity");
bmeHumidityCharacteristics.addDescriptor(new BLE2902());

Enfin, vous démarrez le service et le serveur lance la publicité afin que d'autres appareils puissent la trouver.

// Start the service
bmeService->start();

// Lancer la publicité
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pServer->getAdvertising()->start();
Serial.println("Attente d'une connexion client à notifier...");

loop()

La fonction loop() est assez simple. Vous vérifiez constamment si l'appareil est connecté à un client ou non. S'il est connecté et que le timerDelay s'est écoulé, il lit la température et l'humidité actuelles.

if (deviceConnected) {
 if ((millis() - lastTime) > timerDelay) {
// Lire la température en degrés Celsius (valeur par défaut)
   temp = bme.readTemperature();
   // Fahrenheit
   tempF = temp*1.8 +32;
// Lire l'humidité
   hum = bme.readHumidity();

Si vous utilisez la température en degrés Celsius, la section de code suivante s'exécute. Tout d'abord, il convertit la température en une variable char (variable temperatureCTemp). Nous devons convertir la température en une variable de type char pour l'utiliser dans la fonction setValue().

static char temperatureCTemp[6];
dtostrf(temp, 6, 2, temperatureCTemp);

Ensuite, il définit la valeur bmeTemperatureCelsiusCharacteristic sur la nouvelle valeur de température (temperatureCTemp) à l'aide de setValue(). Après avoir défini la nouvelle valeur, nous pouvons notifier le client connecté à l'aide de la fonction notify().

//Set temperature Characteristic value and notify connected client
bmeTemperatureCelsiusCharacteristics.setValue(temperatureCTemp);
bmeTemperatureCelsiusCharacteristics.notify();

Nous suivons une procédure similaire pour la température en Fahrenheit.

#else
   static char temperatureFTemp[6];
   dtostrf(f, 6, 2, temperatureFTemp);
//Définir la valeur caractéristique de la température et notifier le client connecté    bmeTemperatureFahrenheitCharacteristics.setValue(tempF);
   bmeTemperatureFahrenheitCharacteristics.notify();
   Serial.print("Temperature Fahrenheit: ");
   Serial.print(tempF);
   Serial.print(" *F");
#endif

L'envoi de l'humidité utilise également le même processus.

//Notify humidity reading from DHT
static char humidityTemp[6];
dtostrf(hum, 6, 2, humidityTemp);
//Définir la valeur caractéristique d'humidité et notifier le client connecté
bmeHumidityCharacteristics.setValue(humidityTemp);
bmeHumidityCharacteristics.notify();   
Serial.print(" - Humidity: ");
Serial.print(hum);
Serial.println(" %");

Test du serveur ESP32 BLE

Téléchargez le code sur votre carte, puis ouvrez le moniteur série. Il affichera un message comme indiqué ci-dessous.

ESP32 BLE Server Starts Serial Monitor

Ensuite, vous pouvez tester si le serveur BLE fonctionne comme prévu en utilisant une application de numérisation BLE sur votre smartphone comme nRF Connect. Cette application est disponible pour Android et iOS .

Après avoir installé l'application, activez le Bluetooth sur votre smartphone. Ouvrez l'application nRF Connect et cliquez sur le bouton Numériser. Il trouvera tous les appareils Bluetooth à proximité, y compris votre appareil BME280_ESP32 (c'est le nom du serveur BLE que vous avez défini sur le code).

ESP32 BLE Server Scanner App

Connectez-vous à votre appareil BME280_ESP32, puis sélectionnez l'onglet client (l'interface peut être légèrement différente). Vous pouvez vérifier qu'il annonce le service avec l'UUID que nous avons défini dans le code, ainsi que les caractéristiques de température et d'humidité. Notez que ces caractéristiques ont la propriété Notify.

ESP32 BLE Server Characteristics nRF Connect App

Votre serveur ESP32 BLE est prêt !

Passez à la section suivante pour créer un client ESP32 qui se connecte au serveur pour accéder aux caractéristiques de température et d'humidité et obtenir les lectures pour les afficher sur un écran OLED.


2) Client ESP32 BLE

Dans cette section, nous allons créer le client ESP32 BLE qui établira une connexion avec le serveur ESP32 BLE et affichera les lectures sur un écran OLED.

Schéma

Le client ESP32 BLE est connecté à un écran OLED. L'écran affiche les lectures reçues via Bluetooth.

Câblez votre écran OLED à l'ESP32 en suivant le schéma suivant. La broche SCL se connecte au GPIO 22 et la broche SDA au GPIO 21.

ESP32 Wiring Circuit to OLED SSD1306 Schematic Diagram

Installation des bibliothèques SSD1306, GFX et BusIO

Vous devez installer les bibliothèques suivantes pour vous connecter à l'écran OLED :

Pour installer les bibliothèques, allez sur Sketch> Inclure la bibliothèque > Gérer les bibliothèques et rechercher les noms des bibliothèques.

Installation des bibliothèques (VS Code + PlatformIO)

Si vous utilisez VS Code avec l'extension PlatformIO, copiez ce qui suit dans le fichier platformio.ini pour inclure les bibliothèques.

lib_deps = 
  adafruit/Adafruit GFX 1.10.12
  adafruit/Adafruit  2.4.6

Client BLE ESP32 – Code

Copiez le client BLE Sketch dans votre IDE Arduino ou dans le fichier main.cpp si vous utilisez VS Code avec PlatformIO.



#include "BLEDevice.h"
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// La température par défaut est en degrés Celsius
// Commentez la ligne suivante pour la température en Fahrenheit
#define temperatureCelsius

// Nom du serveur BLE (l'autre nom ESP32 exécutant l'esquisse du serveur)
#define bleServerName "BME280_ESP32"

/* UUID du service, caractéristique que nous voulons lire*/
// Service BLE
static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");

// Caractéristiques BLE
#ifdef temperatureCelsius
// Caractéristique de température Celsius
 static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");
#else
// Caractéristique de température Fahrenheit
 static BLEUUID temperatureCharacteristicUUID("f78ebbff-c8b7-4107-93de-889a6a06d408");
#endif

// Caractéristique d'humidité
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");

// Indicateurs indiquant si la connexion doit commencer et si la connexion est établie
static boolean doConnect = false;
static boolean connected = false;

//Adresse du périphérique. L'adresse sera trouvée lors de la numérisation...
static BLEAddress *pServerAddress;

//Caractéristiques que nous voulons lire
static BLERemoteCharacteristic* temperatureCharacteristic;
static BLERemoteCharacteristic* humidityCharacteristic;

//Activer la notification
const uint8_t notificationOn[] = {0x1, 0x0};
const uint8_t notificationOff[] = {0x0, 0x0};

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

//Déclaration pour un afficheur SSD1306 connecté en I2C (broches SDA, SCL)Affichage Adafruit_SSD1306 (SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//Variables pour stocker la température et l'humidité
char* temperatureChar;
char* humidityChar;

// Indicateurs pour vérifier si de nouvelles lectures de température et d'humidité sont disponibles
boolean newTemperature = false;
boolean newHumidity = false;

//Se connecter au serveur BLE qui porte le nom, le service et les caractéristiques
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();

// Se connecte au serveur BLE de suppression.  pClient->connect(pAddress);
 Serial.println(" - Connected to server");

// Se connecte au serveur BLE de suppression.

 BLERemoteService* pRemoteService = pClient->getService(bmeServiceUUID);
 if (pRemoteService == nullptr) {
   Serial.print("Failed to find our service UUID: ");
   Serial.println(bmeServiceUUID.toString().c_str());
   return (false);
 }

// Obtenir une référence aux caractéristiques dans le service du serveur BLE distant.
 temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
 humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);

 if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr) {
   Serial.print("Failed to find our characteristic UUID");
   return false;
 }
 Serial.println(" - Found our characteristics");

//Attribuer des fonctions de rappel pour les caractéristiques
 temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
 humidityCharacteristic->registerForNotify(humidityNotifyCallback);
 return true;
}

//Fonction de rappel qui est appelée lorsqu'une annonce d'un autre appareil a été reçue
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 void onResult(BLEAdvertisedDevice advertisedDevice) {
   if (advertisedDevice.getName() == bleServerName) {
//Vérifier si le nom de l'annonceur correspond      advertisedDevice.getScan()->stop(); 
//Le scan peut être arrêté, nous avons trouvé ce que nous recherchons
     pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //L'adresse de l'annonceur est celle dont nous avons besoin
    
     doConnect = true; 
    //Définir l'indicateur indiquant que nous sommes prêts à nous connecter
     Serial.println("Device found. Connecting!");
   }
 }
};

//Lorsque le serveur BLE envoie une nouvelle lecture de température avec la propriété notify
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                       uint8_t* pData, size_t length, bool isNotify) {
//stocke la valeur de la température
 temperatureChar = (char*)pData;
 newTemperature = true;
}

//Lorsque le serveur BLE envoie une nouvelle lecture d'humidité avec la propriété notify
humidité vide statiqueNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                                   uint8_t* pData, size_t length, bool isNotify) {
//stocke la valeur d'humidité
 humidityChar = (char*)pData;
 newHumidity = true;
 Serial.print(newHumidity);
}

//fonction qui imprime les dernières lectures du capteur sur l'écran OLED
void printLectures(){

 display.clearDisplay();  
// affiche la température
display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Température : ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(temperatureChar);
  display.setTextSize(1);
  display.cp437(vrai);
  display.write(167);
  display.setTextSize(2);
  Serial.print("Température :");
  Serial.print(temperatureChar);
 #ifdef temperatureCelsius
   //Température Celsius
   display.print("C");
   Serial.print("C");
 #else
   // Température Fahrenheit
   display.print("F");
   Serial.print("F");
 #endif

// affiche l'humidité
 display.setTextSize(1);
 display.setCursor(0, 35);
 display.print("Humidity: ");
 display.setTextSize(2);
 display.setCursor(0, 45);
 display.print(humidityChar);
 display.print("%");
 display.display();
 Serial.print(" Humidity:");
 Serial.print(humidityChar); 
 Serial.println("%");
}

void setup() {
 //Configuration de l'affichage OLED
  // SSD1306_SWITCHCAPVCC = génère une tension d'affichage à partir de 3,3 V en interne
 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Adresse 0x3C pour 128x32
    Serial.println(F("Echec de l'allocation SSD1306"));
    pour(;;); // Ne continue pas, boucle indéfiniment }
 display.clearDisplay();
 display.setTextSize(2);
 display.setTextColor(WHITE,0);
 display.setCursor(0,25);
 display.print("BLE Client");
 display.display();

//Démarrer la communication série
 Serial.begin(115200);
 Serial.println("Starting Arduino BLE Client application...");

 //Initier l'appareil BLE
 BLEDevice::init("");

 // Récupérer un scanner et définir le rappel que nous voulons utiliser pour être informé lorsque nous
  // ont détecté un nouveau périphérique. Spécifiez que nous voulons une analyse active et démarrez le
  // analyse pour s'exécuter pendant 30 secondes.
 BLEScan* pBLEScan = BLEDevice::getScan();
 pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
 pBLEScan->setActiveScan(true);
 pBLEScan->start(30);
}

void loop() {
// Si le drapeau "doConnect" est vrai, nous avons recherché et trouvé le fichier souhaité
  // Serveur BLE avec lequel on souhaite se connecter. Maintenant, on s'y connecte. Une fois que nous sommes
  // connecté, nous définissons le drapeau connecté sur vrai.
  si (doConnect == vrai) {
    if (connectToServer(*pServerAddress)) {
      Serial.println("Nous sommes maintenant connectés au serveur BLE.");
      //Activer la propriété Notifier de chaque Caractéristique
     temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
     humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
     connected = true;
   } else {
     Serial.println("We have failed to connect to the server; Restart your device to scan for nearby BLE server again.");
   }
   doConnect = false;
 }
 //si de nouvelles lectures de température sont disponibles, imprimer dans l'OLED
  si(newTemperature && newHumidity){
   newTemperature = false;
   newHumidity = false;
   printReadings();
 }
 delay(1000); // Delay a second between loops.
}

Importer des bibliothèques

Vous commencez par importer les bibliothèques requises :

#include "BLEDevice.h"
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

Choix de l'unité de température

Par défaut, le client recevra la température en degrés Celsius, si vous commentez la ligne suivante ou la supprimez, il commencera à recevoir la température en degrés Fahrenheit.

// La température par défaut est en degrés Celsius
// Commentez la ligne suivante pour la température en Fahrenheit
#define temperatureCelsius

Nom du serveur BLE et UUID

Ensuite, définissez le nom du serveur BLE auquel nous voulons nous connecter et les UUID de service et de caractéristique que nous voulons lire. Laissez le nom du serveur BLE et les UUID par défaut pour qu'ils correspondent à ceux définis dans l'esquisse du serveur.

//BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "BME280_ESP32"

/* UUID du service, caractéristique que nous voulons lire*/
// Service BLE
BLEUUID statique bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");

// Caractéristiques BLE
#ifdef temperatureCelsius
 //Temperature Celsius Characteristic
 static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");
#else
 //Température Fahrenheit Caractéristique
 static BLEUUID temperatureCharacteristicUUID("f78ebbff-c8b7-4107-93de-889a6a06d408");
#endif

// Caractéristique d'humidité
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");

Déclarer des variables

Ensuite, vous devez déclarer certaines variables qui seront utilisées plus tard avec Bluetooth pour vérifier si nous sommes connectés au serveur ou non.

//Flags stating if should begin connecting and if the connection is up
static boolean doConnect = false;
static boolean connected = false;

Créez une variable de type BLEAddress qui fait référence à l'adresse du serveur auquel nous voulons nous connecter. Cette adresse sera trouvée lors de la numérisation.

//Address of the peripheral device. Address will be found during scanning...
static BLEAddress *pServerAddress;

Définissez les caractéristiques que nous voulons lire (température et humidité).

//Characteristicd that we want to read
static BLERemoteCharacteristic* temperatureCharacteristic;
static BLERemoteCharacteristic* humidityCharacteristic;

Écran OLED

Vous devez également déclarer certaines variables pour travailler avec l'OLED. Définissez la largeur et la hauteur OLED :

#define SCREEN_WIDTH 128 // Largeur d'affichage OLED, en pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Instancier l'affichage OLED avec la largeur et la hauteur définies précédemment.

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

Variables de température et d'humidité

Définissez des variables char pour contenir les valeurs de température et d'humidité reçues par le serveur.

//Variables to store temperature and humidity
char* temperatureChar;
char* humidityChar;

Les variables suivantes sont utilisées pour vérifier si de nouvelles lectures de température et d'humidité sont disponibles et s'il est temps de mettre à jour l'affichage OLED.

//Flags to check whether new temperature and humidity readings are available
boolean newTemperature = false;
boolean newHumidity = false;

printLectures()

Nous avons créé une fonction appelée printReadings() qui affiche les relevés de température et d'humidité sur l'écran OLED.

void printReadings(){

 display.clearDisplay();  
 // display temperature
 display.setTextSize(1);
 display.setCursor(0,0);
 display.print("Temperature: ");
 display.setTextSize(2);
 display.setCursor(0,10);
 display.print(temperatureChar);
 display.print(" ");
 display.setTextSize(1);
 display.cp437(true);
 display.write(167);
 display.setTextSize(2);
 Serial.print("Temperature:");
 Serial.print(temperatureChar);
 #ifdef temperatureCelsius
   //Température Celsius
   display.print("C");
   Serial.print("C");
 #else
   //Temperature Fahrenheit
   display.print("F");
   Serial.print("F");
 #endif

// affiche l'humidité
 display.setTextSize(1);
 display.setCursor(0, 35);
 display.print("Humidity: ");
 display.setTextSize(2);
 display.setCursor(0, 45);
 display.print(humidityChar);
 display.print("%");
 display.display();
 Serial.print(" Humidity:");
 Serial.print(humidityChar); 
 Serial.println("%");
}

setup()

Dans setup(), démarrez l'affichage OLED.

//OLED display setup
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
 Serial.println(F("SSD1306 allocation failed"));
 for(;;); // Ne continue pas, boucle indéfiniment
}

Ensuite, imprimez un message sur la première ligne disant "BME SENSOR".

display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,0);
display.setCursor(0,25);
display.print("BLE Client");
display.display();

Démarrez la communication série à un débit en bauds de 115200.

Serial.begin(115200);

Et initialiser l'appareil BLE.

//Init BLE device
BLEDevice::init("");

Analyser les appareils à proximité

Les méthodes suivantes recherchent les appareils à proximité.

// Retrieve a Scanner and set the callback we want to use to be informed when we
// ont détecté un nouveau périphérique. Spécifiez que nous voulons une analyse active et démarrez le
// analyse pour s'exécuter pendant 30 secondes.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(30);

Fonction MyAdvertisedDeviceCallbacks()

Notez que la fonction MyAdvertisedDeviceCallbacks(), lorsqu'elle trouve un appareil BLE, vérifie si l'appareil trouvé a le bon nom de serveur BLE. Si tel est le cas, il arrête l'analyse et remplace la variable booléenne doConnect par true. De cette façon, nous savons que nous avons trouvé le serveur que nous recherchons et nous pouvons commencer à établir une connexion.

//Callback function that gets called, when another device's advertisement has been received
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 void onResult(BLEAdvertisedDevice advertisedDevice) {
   if (advertisedDevice.getName() == bleServerName) { //Check if the name of the advertiser matches
     advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
     pServerAddress = new BLEAddress(advertisedDevice.getAddress()); //Address of advertiser is the one we need
     doConnect = true; //Set indicator, stating that we are ready to connect
     Serial.println("Device found. Connecting!");
   }
 }
};

Se connecter au serveur

Si la variable doConnect est true, elle essaie de se connecter au serveur BLE. La fonction connectToServer() gère la connexion entre le client et le serveur.

//Connect to the BLE Server that has the name, Service, and Characteristics
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();

// Se connecte au serveur BLE de suppression.
 pClient->connect(pAddress);
 Serial.println(" - Connected to server");

// Obtenir une référence au service que nous recherchons dans le serveur BLE distant.
 BLERemoteService* pRemoteService = pClient->getService(bmeServiceUUID);
 if (pRemoteService == nullptr) {
   Serial.print("Failed to find our service UUID: ");
   Serial.println(bmeServiceUUID.toString().c_str());
   return (false);
 }

 //Obtenir une référence aux caractéristiques au service du serveur BLE distant.
 temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
 humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);

 if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr) {
   Serial.print("Failed to find our characteristic UUID");
   return false;
 }
 Serial.println(" - Found our characteristics");

//Attribuer des fonctions de rappel pour les caractéristiques
 temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
 humidityCharacteristic->registerForNotify(humidityNotifyCallback);
 return true;
}

Il attribue également une fonction de rappel chargée de gérer ce qui se passe lorsqu'une nouvelle valeur est reçue.

It also assigns a callback function responsible to handle what happens when a new value is received.

//Assign callback functions for the Characteristics
temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
humidityCharacteristic->registerForNotify(humidityNotifyCallback);

Une fois le client BLE connecté au serveur, vous devez activer la propriété notifier pour chaque caractéristique. Pour cela, utilisez la méthode writeValue() sur le descripteur.

if (connectToServer(*pServerAddress)) {
 Serial.println("We are now connected to the BLE Server.");
 //Activate the Notify property of each Characteristic
 temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
 humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);

Notifier les nouvelles valeurs

Lorsque le client reçoit une nouvelle valeur de notification, il appelle ces deux fonctions : temperatureNotifyCallback() et humidityNotifyCallback() qui sont responsables de la récupération de la nouvelle valeur, mettez à jour l'OLED avec les nouvelles lectures et imprimez-les sur le moniteur série.

//When the BLE Server sends a new temperature reading with the notify property
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                       uint8_t* pData, size_t length, bool isNotify) {
//stocke la valeur de la température
 temperatureChar = (char*)pData;
 newTemperature = true;
}
///Lorsque le serveur BLE envoie une nouvelle lecture d'humidité avec la propriété notify
humidité vide statiqueNotifyCallbac(BLERemoteCharacteristic* pBLERemoteCharacteristic, 
                                   uint8_t* pData, size_t length, bool isNotify) {
//stocke la valeur d'humidité
 humidityChar = (char*)pData;
 newHumidity = true;
 Serial.print(newHumidity);
}

Ces deux fonctions précédentes sont exécutées chaque fois que le serveur BLE notifie le client avec une nouvelle valeur, ce qui se produit toutes les 30 secondes. Ces fonctions enregistrent les valeurs reçues sur les variables temperatureChar et humidityChar. Ceux-ci changent également les variables newTemperature et newHumidity en true, afin que nous sachions que nous avons reçu de nouvelles lectures.

Afficher les nouvelles lectures de température et d'humidité

Dans la loop(), il y a une instruction if qui vérifie si de nouvelles lectures sont disponibles. S'il y a de nouvelles lectures, nous définissons les variables newTemperature et newHumidity sur false , afin que nous puissions recevoir de nouvelles lectures plus tard. Ensuite, nous appelons la fonction printReadings() pour afficher les lectures sur l'OLED.

//if new temperature readings are available, print in the OLED
if (newTemperature && newHumidity){
 newTemperature = false;
 newHumidity = false;
 printReadings();
}

Tester le projet

C'est tout pour le code. Vous pouvez le télécharger sur votre carte ESP32.

Une fois le code téléchargé. Alimentez le serveur ESP32 BLE, puis alimentez l'ESP32 avec le sketch client. Le client commence à scanner les appareils à proximité et lorsqu'il trouve l'autre ESP32, il établit une connexion Bluetooth. Toutes les 30 secondes, il met à jour l'affichage avec les dernières lectures.

ESP32 BLE Client Server OLED Display Demonstration

Important : n'oubliez pas de déconnecter votre smartphone du serveur BLE. Sinon, le client ESP32 BLE ne pourra pas se connecter au serveur.

ESP32 BLE Client Connected to ESP32 BLE Server Serial Monitor

Conclusion

Dans ce didacticiel, vous avez appris à créer un serveur BLE et un client BLE avec l'ESP32. Vous avez appris à définir de nouvelles valeurs de température et d'humidité sur les caractéristiques du serveur BLE. Ensuite, d'autres appareils BLE (clients) peuvent se connecter à ce serveur et lire ces valeurs caractéristiques pour obtenir les dernières valeurs de température et d'humidité. Ces caractéristiques ont la propriété notify , de sorte que le client est averti chaque fois qu'il y a une nouvelle valeur.

L'utilisation de BLE est un autre protocole de communication que vous pouvez utiliser avec les cartes ESP32 en plus du Wi-Fi. Nous espérons que vous avez trouvé ce tutoriel utile. Nous créerons d'autres tutoriels sur d'autres protocoles de communication.

Écrire un commentaire

Quelle est le quatrième caractère du mot nl9q4 ?