bluetooth arduino

Découvrir Bluetooth® Low Energy (BLE) avec un Arduino Nano ESP32

5
(3)

Ce tutoriel va vous permettre de découvrir la technologie Bluetooth® Low Energy (BLE) au travers d’exemples basés sur un Arduino Nano ESP32. Cette carte à base de microcontrôleur ESP32 est en effet très bien adaptée au développement d’objets connectés utilisant les technologies Bluetooth® ou Wifi.

Le standard BLE permet une communication sans fil dans la bande des 2,4 GHz. Il autorise un débit de l’ordre du Mbit/s sur une distance de quelques dizaines de mètres. Son énorme avantage par rapport au Bluetooth® “classic” est sa très faible consommation électrique. Ce qui le rend très bien adapté pour la communication des objets connectés (IoT) alimentés sur piles ou batteries.

Le protocole BLE est très bien détaillées dans le document The Bluetooth® Low Energy Primer, je ne vais pas le détailler dans ce tutoriel.

Exemple 1 : contrôler l’état de la LED interne de l’Arduino via BLE

Dans ce premier exemple, nous allons contrôler l’état de la LED interne de l’Arduino Nano ESP32. L’Arduino va exposer un service ayant comme caractéristique l’état de la LED autorisé en lecture/écriture. Le nom du périphérique BLE sera “LED”. L’UUID du service et de la caractéristique est choisi arbitrairement avec la valeur “19b10000-e8f2-537e-4f6c-d104768a1214”.

Voici le croquis de l’appareil périphérique BLE (BLE Peripheral) qui va s’exécuter sur l’Arduino Nano ESP32 :

// Turns an Arduino Nano ESP32 into a Bluetooth® Low Energy peripheral.
// This BLE peripheral is providing a service that allows a BLE central 
// to switch on and off the internal LED of the Arduino Nano ESP32.
// https://tutoduino.fr/
// Copyleft 2023
#include <ArduinoBLE.h>
BLEService ledService("19b10000-e8f2-537e-4f6c-d104768a1214"); // Bluetooth® Low Energy LED Service
// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19b10000-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite);
const int ledPin = LED_BUILTIN; // internal LED pin
void setup() {
  Serial.begin(9600);
  // set LED pin to output mode
  pinMode(ledPin, OUTPUT);
  // BLE initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }
  // set advertised local name and service UUID:
  BLE.setLocalName("LED");
  BLE.setAdvertisedService(ledService);
  // add the characteristic to the service
  ledService.addCharacteristic(switchCharacteristic);
  // add service
  BLE.addService(ledService);
  // set the initial value for the characeristic:
  switchCharacteristic.writeValue(0);
  // start advertising
  BLE.advertise();
  Serial.println("BLE LED Peripheral");
}
void loop() {
  // wait for a Bluetooth® Low Energy central
  BLEDevice central = BLE.central();
  // check if a central is connected to this peripheral
  if (central) {
    Serial.print("Connected to central: ");
    // print the central's MAC address:
    Serial.println(central.address());
    // while the central is still connected to peripheral:
    while (central.connected()) {
      // if the remote device wrote to the characteristic,
      // use the value to control the LED:
      if (switchCharacteristic.written()) {
        if (switchCharacteristic.value()) {   // any value other than 0
          Serial.println("LED on");
          digitalWrite(ledPin, HIGH);         // will turn the LED on
        } else {                              // a 0 value
          Serial.println(F("LED off"));
          digitalWrite(ledPin, LOW);          // will turn the LED off
        }
      }
    }
    // the central has disconnected
    Serial.println("Disconnected from central: ");
  }
}

Pour interagir avec l’Arduino en Bluetooth, vous pouvez par exemple utiliser ce petit programme Python sur un PC. Ce programme sera un central BLE (BLE Central) et il va scanner les périphériques BLE (BLE Peripheral) et rechercher celui qui a le nom “LED” afin de s’y connecter. Ensuite le programme va démarrer un client qui va utiliser le service exposé par le périphérique pour faire clignoter la LED interne 5 fois :

#!/usr/bin/python3
# -*- coding: utf8 -*-
# Copyleft https://tutoduino.fr/
import argparse
import asyncio
from bleak import BleakScanner
from bleak import BleakClient
LED_UUID = '19b10000-e8f2-537e-4f6c-d104768a1214'
async def main():
    print("Searching Arduino Nano ESP32 'LED' device, please wait...")
    # Scan BLE devices for timeout seconds and return discovered devices with advertising data
    devices = await BleakScanner.discover(timeout=5,
                                          return_adv=True)
    for ble_device, adv_data in devices.values():
        if ble_device.name == 'LED':
            print("Device found")
            # Connect to Arduino Nano ESP 32 device
            async with BleakClient(ble_device.address) as client:
                print("Connected to device")
                for i in range(5):
                    print("Turning internal LED on...")
                    val = await client.write_gatt_char(LED_UUID, b"\x01")
                    await asyncio.sleep(1.0)
                    print("Turning internal LED off..")
                    val = await client.write_gatt_char(LED_UUID, b"\x00")
                    await asyncio.sleep(1.0)
if __name__ == "__main__":
    asyncio.run(main())

Il est également possible de contrôler l’état de la LED de l’Arduino via une application sur votre Smartphone. En utilisant l’application LightBlue par exemple sur Android il est possible de se connecter au device “LED” et d’agir sur l’état de la LED interne au travers de l’écriture de la valeur 0x00 ou 0x01 la caractéristique du service exposé :

Exemple 2: transmettre la température interne du CPU de l’Arduino via BLE

Pour notre deuxième exemple, nous allons utiliser le mécanisme de publicité (advertising) de BLE. Ce mécanisme est particulièrement adapté à la transmission de données par un capteur car il permet de réduire la consommation électrique.

Dans cet exemple, l’Arduino Nano ESP32 est un périphérique BLE utilisant ce mécanisme de publicité pour envoyer la température interne du microcontrôleur. L’Arduino va publier la température toutes les minutes pendant 2 secondes, et se mettre en veille entre deux émissions afin de limiter la consommation d’énergie. La température sera envoyée dans le premier octet des données constructeur (manufacturer data) du périphérique BLE.

// Turns an Arduino Nano ESP32 into a Bluetooth® Low Energy peripheral.
// This BLE peripheral is advertising manufacturer data that contains
// internal temperature of the microcontroler.
// https://tutoduino.fr/
// Copyleft 2023
#include <ArduinoBLE.h>
#include "driver/temp_sensor.h"
void setup() {
  Serial.begin(9600);
  if (!BLE.begin()) {
    Serial.println("failed to initialize BLE!");
    while (1);
  }
}
void loop() {
  float cpu_temperature;
  uint8_t manufactData[] = { 0x01 };
  // read the internal temperature sensor
  temp_sensor_read_celsius(&cpu_temperature);
  manufactData[0] = uint8_t(cpu_temperature);
  // Build advertising data packet (temperature is first byte of manufacturer data)
  BLEAdvertisingData advData;
  advData.setLocalName("TEMPERATURE");
  // Set parameters for advertising packet
  advData.setManufacturerData(0x09A3, manufactData, sizeof(manufactData));
  // Copy set parameters in the actual advertising packet
  BLE.setAdvertisingData(advData);
  // advertise during 2 seconds
  BLE.advertise();
  delay(2000);
  BLE.stopAdvertise();
  // enter deep sleep for 1 minute
  esp_sleep_enable_timer_wakeup(60000000);
  esp_deep_sleep_start();
}

Le programme Python suivant pourra s’exécuter sur un PC, il affichera la température interne du microcontrôleur contenu dans le premier octet du manufacturer_data publié par l’Arduino.

#!/usr/bin/python3
# -*- coding: utf8 -*-
# Copyleft https://tutoduino.fr/
import argparse
import asyncio
from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
def device_found(
    device: BLEDevice, advertisement_data: AdvertisementData
):
    """Decode advertisement data"""
    try:
        """ Check Arduino manufacturer advertisement data (expect Arduino company ID 0x09A3) """
        manuf_data = advertisement_data.manufacturer_data[0x09A3]
        print('Temperature = {} ; RSSI = {}'.format(
            manuf_data[0], advertisement_data.rssi))
    except KeyError:
        pass
async def main():
    """Scan for devices"""
    scanner = BleakScanner(device_found)
    while True:
        await scanner.start()
        await asyncio.sleep(1.0)
        await scanner.stop()
asyncio.run(main())

Comme dans l’exemple précédent, il sera possible d’afficher la température sur Smartphone via l’application LightBlue par exemple sur Android. Dans la capture d’écran ci-dessous, la température du microcontrôleur est indiquée dans le champ Manufacturer specific Data, et a pour valeur 0x17 soit 23 °C.

Remarque : lorsque l'Arduino est en veille il n'est plus possible de téléverser le code via l'IDE Arduino. Il faut suivre la procédure suivante afin de réinitialiser bootloader et téléverser le sketch via le programmeur Esptool: https://support.arduino.cc/hc/en-us/articles/9810414060188-Reset-the-Arduino-bootloader-on-the-Nano-ESP32

Exemple 3 : Wifi et BLE utilisés conjointement

Le principe de cet exemple est basé sur mon tutoriel Créez votre premier objet connecté (IoT) avec la plateforme Arduino Cloud et un Arduino Nano ESP32. L’Arduino Nano ESP32 est connecté à Arduino Cloud en Wifi. Il est ainsi possible de contrôler l’état de la LED interne de la carte Arduino Nano ESP32 depuis le Dashboard d’Arduino Cloud.

Nous allons ajouter la fonctionnalité de central BLE (BLE Central) à cet Arduino Nano ESP32. L’Arduino Nano ESP32 a donc à la fois une connexion Wifi et une connexion BLE actives simultanément.

La carte uPesy ESP32 Wroom est programmée comme dans l’exemple 1. Elle est utilisée comme périphérique BLE (BLE Peripheral) qui expose un service ayant comme caractéristique l’état de la LED interne autorisé en lecture/écriture. Et c’est notre Arduino Nano ESP32 qui va contrôler l’état de la LED interne de l’uPesy ESP32 Wroom au travers de la liaison BLE.

Nous allons programmer l’Arduino Nano ESP32 pour qu’il allume la LED interne de l’uPesy ESP32 Wroom via BLE lorsque sa propre LED interne est allumée via Arduino Cloud. Ainsi lorsque nous cliquons sur le bouton LED STATE depuis Arduino Cloud les LED internes des 2 cartes Arduino Nano ESP32 et uPesy ESP32 Wroom vont s’allumer.

L’objectif de cet exemple est de montrer que l’Arduino Nano ESP32 peut utiliser le BLE alors qu’il est connecté à Arduino Cloud via le Wifi.

Voici le croquis de l’Arduino Nano ESP32, il n’est pas optimisé mais montre comment imbriquer le maintien des connexions BLE et Wifi. Le fichier thingProperties.h est celui qui est auto-généré par Arduino Cloud dans mon tutoriel Créez votre premier objet connecté (IoT) avec la plateforme Arduino Cloud et un Arduino Nano ESP32 :

// Exemple of basic IoT with Arduino Cloud
// https://tutoduino.fr/
// Copyleft 2023
#include "thingProperties.h"
#include "driver/temp_sensor.h"
#include <ArduinoBLE.h>
BLEService ledService("19b10000-e8f2-537e-4f6c-d104768a1214");  // Bluetooth® Low Energy LED Service
// Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19b10000-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite);
void initTempSensor() {
  temp_sensor_config_t temp_sensor = TSENS_CONFIG_DEFAULT();
  temp_sensor.dac_offset = TSENS_DAC_L2;  // TSENS_DAC_L2 is default; L4(-40°C ~ 20°C), L2(-10°C ~ 80°C), L1(20°C ~ 100°C), L0(50°C ~ 125°C)
  temp_sensor_set_config(temp_sensor);
  temp_sensor_start();
}
void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);
  // Defined in thingProperties.h
  initProperties();
  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
  // set internal LED pin as output
  pinMode(LED_BUILTIN, OUTPUT);
  // initialize the internal temperature sensor
  initTempSensor();
  // initialize the Bluetooth® Low Energy hardware
  BLE.begin();
  // scan the LED peripheral
  BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
void loop() {
  float temp;
  temp_sensor_read_celsius(&temp);
  cpu_temperature = temp;
  ArduinoCloud.update();
  // check if a peripheral has been discovered
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
    // stop scanning
    BLE.stopScan();
    // connect to peripheral and use its services to control its internal LED
    controlLed(peripheral);
    // if we exist controlLed function it means that peripheral is disconnected
    // we start scanning again
    BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
  }
}
void controlLed(BLEDevice peripheral) {
  // connect to the peripheral
  Serial.println("Connecting ...");
  if (peripheral.connect()) {
    Serial.println("Connected");
  } else {
    Serial.println("Failed to connect!");
    return;
  }
  // discover peripheral attributes
  Serial.println("Discovering attributes ...");
  if (peripheral.discoverAttributes()) {
    Serial.println("Attributes discovered");
  } else {
    Serial.println("Attribute discovery failed!");
    peripheral.disconnect();
    return;
  }
  // retrieve the LED characteristic
  BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10000-e8f2-537e-4f6c-d104768a1214");
  if (!ledCharacteristic) {
    Serial.println("Peripheral does not have LED characteristic!");
    peripheral.disconnect();
    return;
  } else if (!ledCharacteristic.canWrite()) {
    Serial.println("Peripheral does not have a writable LED characteristic!");
    peripheral.disconnect();
    return;
  }
  // set LED characteristic while the peripheral is connected
  while (peripheral.connected()) {
    if (led_status == true) {
      ledCharacteristic.writeValue((byte)0x01);
    } else {
      ledCharacteristic.writeValue((byte)0x00);
    }
    // while the peripheral is connected, continue to update Arduino Cloud
    ArduinoCloud.update();
  }
  Serial.println("Peripheral disconnected");
}
/*
  Since LedStatus is READ_WRITE variable, onLedStatusChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLedStatusChange() {
  // Turn LED ON or OFF depending on led_status variable
  digitalWrite(LED_BUILTIN, led_status);
}

J’espère que cet article vous donnera envie d’aller plus loin avec BLE. N’hésitez pas à donner une note à ce tuto (étoiles ci-dessous) et à ajouter un commentaire afin que je puisse connaître votre avis et l’améliorer si besoin.

Votre avis compte !

Note moyenne : 5 / 5. Nombre de votes : 3

Pas encore de vote pour ce tutoriel

Désolé si cet article ne vous a pas intéressé

Merci de commenter afin que je puisse l’améliorer.

Dites-moi comment améliorer cette page.