Ce didacticiel est un guide de démarrage pour Bluetooth Low Energy (BLE) avec l’ESP32 programmé avec le micrologiciel MicroPython. Nous vous présenterons les concepts de base du BLE et présenterons quelques exemples simples : publier et exposer les données pour qu’elles soient lues par d’autres appareils BLE ; et détecter quand un autre appareil BLE écrit des données sur les caractéristiques de l’ESP32.

Utilisez-vous l’IDE Arduino ? Suivez plutôt ce tutoriel : ESP32 avec Bluetooth et Bluetooth Low Energy : Le guide ultime
Conditions préalables
Avant de poursuivre ce didacticiel, assurez-vous de vérifier les prérequis suivants.
Micrologiciel MicroPython

Pour suivre ce tutoriel, vous devez installer le firmware MicroPython sur vos cartes ESP32 ou ESP8266. Vous avez également besoin d’un IDE pour écrire et télécharger le code sur votre tableau. Nous vous suggérons d’utiliser Thonny IDE :
En savoir plus sur MicroPython : Programmation MicroPython avec ESP32 et ESP8266
Présentation du Bluetooth Low Energy (BLE)
Avant de plonger dans les exemples, nous expliquerons quelques concepts de base sur Bluetooth et Bluetooth Low Energy.
Qu’est-ce que le Bluetooth ?

Bluetooth est une technologie sans fil qui permet aux appareils de communiquer sur de courtes distances. Cette variante du Bluetooth est également appelée « Bluetooth Classic » ou simplement « Bluetooth ». Il a été conçu pour la transmission de données à grande vitesse et est couramment utilisé pour connecter des appareils tels que des écouteurs à des téléphones, relier un clavier ou une souris à un ordinateur ou transférer des fichiers entre appareils.
Qu’est-ce que le Bluetooth Low Energy ?

Bluetooth Low Energy, BLE en abrégé (également appelé Bluetooth Smart), est une variante de Bluetooth économe en énergie. La principale application du BLE est la transmission à courte distance de petites quantités de données (faible bande passante). Contrairement à Bluetooth qui est toujours activé, BLE reste constamment en mode veille, sauf lorsqu’une connexion est établie. Cela lui permet de consommer très peu d’énergie. BLE consomme environ 100 fois moins d’énergie que Bluetooth (selon le cas d’utilisation).
Alors, quelles sont les principales différences entre Bluetooth Classic et Bluetooth Low Energy ?
Bluetooth Classic est connu pour ses taux de transfert de données plus élevés, ce qui le rend adapté aux applications telles que le streaming audio et le transfert de fichiers. Il consomme plus d’énergie, ce qui le rend moins idéal pour les appareils fonctionnant sur batterie. Habituellement, Bluetooth Classic est plus facile à comprendre et à mettre en œuvre pour les débutants, tandis que Bluetooth Low Energy peut prendre un peu plus de temps pour comprendre les concepts de base. C’est pourquoi de nombreuses personnes préfèrent encore utiliser Bluetooth Classic plutôt que BLE dans leurs projets IoT.
D’autre part, BLE (Bluetooth Low Energy) est conçu pour une faible consommation d’énergie, ce qui le rend parfait pour les appareils tels que les gadgets et les appareils portables IoT, et constitue également une excellente solution pour l’ESP32 dans les applications IoT et domotiques. BLE fonctionne avec des taux de transfert de données inférieurs, mais est économe en énergie et fonctionne bien dans les scénarios à courte portée.
Une autre grande différence entre les deux versions de Bluetooth réside dans la manière de transférer les données. Bluetooth Classic utilise quelque chose de similaire à la communication série (Serial Port Profile), tandis que Bluetooth Low Energy utilise un modèle client-serveur, dans lequel il utilise le GATT (Generic Attribute Profile) pour structurer les données.
Vous pouvez consulter ce site Web pour en savoir plus sur les principales différences entre Bluetooth et Bluetooth Low Energy.
| Bluetooth Classique | Bluetooth basse consommation (BLE) | |
| Consommation d’énergie | Consommation d’énergie plus élevée | Faible consommation d’énergie |
| Taux de transfert de données | Taux de transfert de données plus élevés | Taux de transfert de données inférieurs |
| Gamme | Portée plus longue | Portée plus courte |
| Applications | Streaming audio, transfert de fichiers | Appareils IoT, wearables, maison intelligente |
| Transfert de données | Profil de port série (SPP) | Profil d’attribut générique (GATT) |
Avant de continuer, il est important de se familiariser avec certains concepts de base du BLE.
Périphérique et contrôleur BLE (périphérique central)
Lorsque vous utilisez Bluetooth Low Energy (BLE), il est important de comprendre les rôles du périphérique BLE et du contrôleur BLE (également appelé périphérique central).

L’ESP32 peut agir soit comme périphérique, soit comme appareil central. Lorsqu’il agit comme périphérique, il établit un profil GATT et annonce son service avec des caractéristiques que les appareils centraux peuvent lire ou avec lesquelles interagir. En revanche, lorsqu’il est défini comme appareil central, il peut se connecter à d’autres appareils BLE pour lire ou interagir avec leurs profils et lire leurs caractéristiques.
Dans le diagramme ci-dessus, l’ESP32 joue le rôle de périphérique BLE, servant de dispositif qui fournit des données ou des services. Votre smartphone ou ordinateur fait office de contrôleur BLE, gérant la connexion et la communication avec l’ESP32.
Serveur et client BLE
Avec Bluetooth Low Energy, il existe deux types d’appareils : le serveur et le client. L’ESP32 peut agir soit en tant que client, soit en tant que serveur. Dans l’image ci-dessous, il agit comme un serveur, exposant sa structure GATT contenant des données. Le serveur BLE agit en tant que fournisseur de données ou de services, tandis que le client BLE consomme ou utilise ces services.

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 ou avec lesquelles interagir. Le client scanne les appareils à proximité, et lorsqu’il trouve le serveur qu’il recherche, il établit une connexion et peut interagir avec cet appareil en lisant ou en écrivant sur ses caractéristiques.
Le serveur BLE est essentiellement le périphérique BLE avant d’établir une connexion. Le client BLE est le contrôleur BLE avant d’établir une connexion. Souvent, ces termes sont utilisés de manière interchangeable.
GATT
GATT, qui signifie Generic Attribute Profile, est un concept fondamental de la technologie Bluetooth Low Energy (BLE). Essentiellement, il sert de modèle pour la façon dont les appareils BLE communiquent entre eux. Considérez-le comme un langage structuré que deux appareils BLE utilisent pour échanger des informations de manière transparente.

- Profil : ensemble standard de services pour un cas d’utilisation spécifique ;
- Service : collecte d’informations associées, telles que les lectures des capteurs, le niveau de la batterie, la fréquence cardiaque, etc. ;
- Caractéristique : c’est là que les données réelles sont enregistrées dans la hiérarchie (valeur) ;
- Descripteur : métadonnées sur les données ;
- Propriétés : décrivez comment la valeur caractéristique peut être interagie. Par exemple : lire, écrire, notifier, diffuser, indiquer, etc.
Examinons plus en profondeur le service et les caractéristiques BLE.
Service BLE
Le niveau supérieur de la hiérarchie est un profil et est composé d’un ou plusieurs services. Habituellement, un appareil BLE contient plusieurs services, comme le service de batterie et le service de fréquence cardiaque.
Chaque service contient au moins une caractéristique. Il existe des services prédéfinis pour plusieurs types de données définies par le SIG (Bluetooth Special Interest Group) comme : Niveau de batterie, Tension artérielle, Fréquence cardiaque, Balance de poids, Détection environnementale, etc. Vous pouvez vérifier sur le lien suivant pour les services prédéfinis :
UUID
Un UUID est un identifiant numérique unique utilisé dans le BLE et le GATT pour distinguer et localiser les services, les caractéristiques et les descripteurs. C’est comme une étiquette distincte qui garantit que chaque composant d’un appareil Bluetooth porte un nom unique.
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 et par défaut pour les services, ainsi que des caractéristiques spécifiées dans le SIG (Bluetooth Special Interest Group). Cela signifie que si vous disposez d’un appareil BLE qui utilise les UUID par défaut pour ses services et caractéristiques, vous saurez exactement comment interagir avec cet appareil pour obtenir ou interagir avec les informations que vous recherchez.
Vous pouvez également générer vos propres UUID personnalisés si vous ne souhaitez pas vous en tenir aux valeurs prédéfinies ou si les données que vous échangez ne rentrent dans aucune des catégories. Vous pouvez générer des UUID personnalisés à l’aide de ce site Web générateur d’UUID.
Communication entre les appareils BLE
Voici les étapes habituelles qui décrivent la communication entre les appareils BLE.

- Le périphérique BLE (serveur) annonce son existence (ESP32).
- Le périphérique central BLE (client) recherche les périphériques BLE.
- Lorsque l’appareil central trouve le périphérique qu’il recherche, il s’y connecte.
- Après connexion, il lit le profil GATT du périphérique et recherche le service qu’il recherche (par exemple : détection environnementale).
- S’il trouve le service, il peut désormais interagir avec les caractéristiques. Par exemple, lire la valeur de la température.
Pour écrire du code permettant d’utiliser Bluetooth avec l’ESP32, nous allons installer le package aioble, qui est actuellement la bibliothèque recommandée pour la communication BLE avec MicroPython.
Avant de passer aux exemples concrets, vous devez l’installer sur votre carte.
- Connectez la carte (avec MicroPython installé – vérifiez les prérequis) à votre ordinateur et connectez-la à Thonny IDE.
- Sur Thonny IDE, allez dans Outils > Gérer les packages…
- Recherchez aioble et cliquez sur l’option aioble.

Enfin, cliquez sur le bouton Installer.

Attendez quelques secondes pendant l’installation. Après l’installation, vous pouvez passer aux exemples.
L’ESP32 comme périphérique BLE
Dans cet exemple, nous définirons l’ESP32 comme périphérique BLE. Nous allons vous montrer comment annoncer des services, modifier la valeur des caractéristiques et comment détecter si un autre appareil BLE a écrit sur les caractéristiques ESP32. Voici un aperçu rapide du fonctionnement du projet :
- Dans cet exemple, l’ESP32 agira comme un périphérique BLE/serveur BLE qui annonce son existence.
- La structure ESP32 du GATT comportera un service présentant deux caractéristiques. Une caractéristique (appelons-la caractéristique du capteur) sera l’endroit où enregistrer une valeur qui change au fil du temps (comme les lectures du capteur).
- L’ESP32 écrira périodiquement une nouvelle valeur dans la caractéristique du capteur.
- L’autre caractéristique (appelons-la caractéristique LED) sera l’endroit où sauvegarder l’état d’un GPIO. En changeant la valeur de cette caractéristique, nous pourrons contrôler une LED connectée à ce GPIO.
- Nous utiliserons une application appelée nRF Connect pour nous connecter au périphérique ESP32 BLE afin de lire les caractéristiques du capteur et d’écrire sur les caractéristiques de la LED pour contrôler la LED intégrée.

Code MicroPython périphérique ESP32 BLE
Copiez le code suivant dans Thonny IDE.
# Rui Santos & Sara Santos - Raspberryme.com
# Complete project details at https://Raspberryme.com/micropython-esp32-bluetooth-low-energy-ble/
from micropython import const
import asyncio
import aioble
import bluetooth
import struct
from machine import Pin
from random import randint
# Init LED
led = Pin(2, Pin.OUT)
led.value(0)
# Init random value
value = 0
# See the following for generating UUIDs:
# https://www.uuidgenerator.net/
_BLE_SERVICE_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_BLE_SENSOR_CHAR_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')
_BLE_LED_UUID = bluetooth.UUID('19b10002-e8f2-537e-4f6c-d104768a1214')
# How frequently to send advertising beacons.
_ADV_INTERVAL_MS = 250_000
# Register GATT server, the service and characteristics
ble_service = aioble.Service(_BLE_SERVICE_UUID)
sensor_characteristic = aioble.Characteristic(ble_service, _BLE_SENSOR_CHAR_UUID, read=True, notify=True)
led_characteristic = aioble.Characteristic(ble_service, _BLE_LED_UUID, read=True, write=True, notify=True, capture=True)
# Register service(s)
aioble.register_services(ble_service)
# Helper to encode the data characteristic UTF-8
def _encode_data(data):
return str(data).encode('utf-8')
# Helper to decode the LED characteristic encoding (bytes).
def _decode_data(data):
try:
if data is not None:
# Decode the UTF-8 data
number = int.from_bytes(data, 'big')
return number
except Exception as e:
print("Error decoding temperature:", e)
return None
# Get sensor readings
def get_random_value():
return randint(0,100)
# Get new value and update characteristic
async def sensor_task():
while True:
value = get_random_value()
sensor_characteristic.write(_encode_data(value), send_update=True)
print('New random value written: ', value)
await asyncio.sleep_ms(1000)
# Serially wait for connections. Don't advertise while a central is connected.
async def peripheral_task():
while True:
try:
async with await aioble.advertise(
_ADV_INTERVAL_MS,
name="ESP32",
services=[_BLE_SERVICE_UUID],
) as connection:
print("Connection from", connection.device)
await connection.disconnected()
except asyncio.CancelledError:
# Catch the CancelledError
print("Peripheral task cancelled")
except Exception as e:
print("Error in peripheral_task:", e)
finally:
# Ensure the loop continues to the next iteration
await asyncio.sleep_ms(100)
async def wait_for_write():
while True:
try:
connection, data = await led_characteristic.written()
print(data)
print(type)
data = _decode_data(data)
print('Connection: ', connection)
print('Data: ', data)
if data == 1:
print('Turning LED ON')
led.value(1)
elif data == 0:
print('Turning LED OFF')
led.value(0)
else:
print('Unknown command')
except asyncio.CancelledError:
# Catch the CancelledError
print("Peripheral task cancelled")
except Exception as e:
print("Error in peripheral_task:", e)
finally:
# Ensure the loop continues to the next iteration
await asyncio.sleep_ms(100)
# Run tasks
async def main():
t1 = asyncio.create_task(sensor_task())
t2 = asyncio.create_task(peripheral_task())
t3 = asyncio.create_task(wait_for_write())
await asyncio.gather(t1, t2)
asyncio.run(main())
Afficher le code brut
Comment fonctionne le code
Jetons un coup d’œil rapide aux parties pertinentes du code pour cet exemple.
Inclure les bibliothèques
Vous devez inclure les bibliothèques aioble et Bluetooth pour utiliser Bluetooth avec l’ESP32. Nous importons également la méthode randint du module random pour générer des nombres aléatoires. Notre code sera asynchrone. Pour cela, nous utiliserons la bibliothèque asyncio.
from micropython import const
import asyncio
import aioble
import bluetooth
import struct
from machine import Pin
from random import randint
Définir les UUID et enregistrer le service et les caractéristiques du GATT
Nous définissons les UUID du service et ses caractéristiques. Une caractéristique contiendra la valeur de la LED et une autre pour la lecture hypothétique du capteur (dans ce cas, une valeur aléatoire).
_BLE_SERVICE_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_BLE_SENSOR_CHAR_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')
_BLE_LED_UUID = bluetooth.UUID('19b10002-e8f2-537e-4f6c-d104768a1214')
Ces UUID ont été créés à l’aide du site Web uuidgenerator. Vous pouvez générer vos propres UUID pour votre application, mais pour cet exemple, nous vous recommandons d’utiliser les mêmes UUID que nous utilisons.
Ensuite, enregistrez le service et les caractéristiques du GATT.
# Register GATT server, the service and characteristics
ble_service = aioble.Service(_BLE_SERVICE_UUID)
sensor_characteristic = aioble.Characteristic(ble_service, _BLE_SENSOR_CHAR_UUID, read=True, notify=True)
led_characteristic = aioble.Characteristic(ble_service, _BLE_LED_UUID, read=True, write=True, notify=True, capture=True)
# Register service(s)
aioble.register_services(ble_service)
Lors de la création de sensor_characteristic, nous définissons les arguments de lecture et de notification sur True. Cela définit la manière dont le périphérique central peut interagir avec la caractéristique. Il peut lire la caractéristique et être averti lorsqu’elle change.
Pour la led_characteristic, nous avons une propriété supplémentaire. La propriété de capture définie sur True. Cela indique que d’autres appareils BLE peuvent écrire sur cette caractéristique : c’est ainsi que d’autres appareils peuvent contrôler la LED ESP32.
led_characteristic = aioble.Characteristic(ble_service, _BLE_LED_UUID, read=True, write=True, notify=True, capture=True)
Encoder et décoder les données
Les données à inscrire sur la caractéristique doivent être dans un format spécifique. La fonction _encode_data() convertit les données au format UTF-8.
# Helper to encode the data characteristic UTF-8
def _encode_data(data):
return str(data).encode('utf-8')
Lorsque d’autres appareils écrivent dans led_characteristic, les données seront au format octet. La fonction suivante convertit les octets en entier.
# Helper to decode the LED characteristic encoding (bytes).
def _decode_data(data):
try:
if data is not None:
# Decode the UTF-8 data
number = int.from_bytes(data, 'big')
return number
except Exception as e:
print("Error decoding temperature:", e)
return None
Obtenez une nouvelle valeur et écrivez sur la caractéristique
sensor_task() est une fonction asynchrone qui obtient une nouvelle valeur aléatoire et écrit sur la caractéristique à l’aide de la méthode write() sur sensor_characteristic. Cette tâche est répétée en continu toutes les secondes. Vous pouvez ajuster le temps de retard selon vos besoins.
# Get new value and update characteristic
async def sensor_task():
while True:
value = get_random_value()
sensor_characteristic.write(_encode_data(value), send_update=True)
#print('New random value written: ', value)
await asyncio.sleep_ms(1000)
La fonction get_random_value(), dans un scénario réel, doit être remplacée par une fonction qui obtiendra les données du capteur, comme la température d’un capteur de température DS18B20, par exemple.
Publicité
En plus d’écrire les caractéristiques du capteur, nous devons également annoncer l’ESP32 en tant que service BLE. Pour cela, nous utilisons la fonction périphérique_task().
# Serially wait for connections. Don't advertise while a central is connected.
async def peripheral_task():
while True:
try:
async with await aioble.advertise(
_ADV_INTERVAL_MS,
name="ESP32",
services=[_BLE_SERVICE_UUID],
) as connection:
print("Connection from", connection.device)
await connection.disconnected()
except asyncio.CancelledError:
# Catch the CancelledError
print("Peripheral task cancelled")
except Exception as e:
print("Error in peripheral_task:", e)
finally:
# Ensure the loop continues to the next iteration
await asyncio.sleep_ms(100)
Dans cette fonction, nous définissons le nom du périphérique BLE (« ESP32 »). Vous pouvez changer son nom si vous le souhaitez. Mais, pour suivre nos exemples, nous vous recommandons de laisser ce nom.
En attente d’une écriture
Dans la fonction wait_for_write(), nous vérifions en permanence si la led_characteristic a été écrite. Lorsque c’est le cas, nous décodons les données et allumons ou éteignons la LED intégrée de l’ESP32 en conséquence.
async def wait_for_write():
while True:
try:
connection, data = await led_characteristic.written()
print(data)
print(type)
data = _decode_data(data)
print('Connection: ', connection)
print('Data: ', data)
if data == 1:
print('Turning LED ON')
led.value(1)
elif data == 0:
print('Turning LED OFF')
led.value(0)
else:
print('Unknown command')
except asyncio.CancelledError:
# Catch the CancelledError
print("Peripheral task cancelled")
except Exception as e:
print("Error in peripheral_task:", e)
finally:
# Ensure the loop continues to the next iteration
await asyncio.sleep_ms(100)
Fonction principale
Enfin, nous créons une fonction asynchrone main(), dans laquelle nous écrirons la base de notre code. Nous créons trois tâches asynchrones : une pour la publicité, une autre pour écrire sur la caractéristique du capteur et enfin, une autre pour contrôler la LED lorsque d’autres appareils écrivent sur la caractéristique_led.
async def main():
t1 = asyncio.create_task(sensor_task())
t2 = asyncio.create_task(peripheral_task())
t3 = asyncio.create_task(wait_for_write())
await asyncio.gather(t1, t2)
Enfin, nous exécutons le code comme suit.
asyncio.run(main())
Tester le code
Exécutez le code précédent sur l’ESP32. Il commencera à écrire la température sur les caractéristiques du capteur et annoncera son service.

Pour nous connecter à ce périphérique, lire les caractéristiques de son capteur et écrire dans les caractéristiques de la LED, nous utiliserons l’application nRF connect. Vous pouvez également utiliser notre application Web BLE (fonctionne uniquement avec les ordinateurs Android et Windows).
Application nRF Connect
L’application nRF Connect de Nordic fonctionne sur Android (Google Play Store) et iOS (App Store). Accédez à Google Play Store ou App Store, recherchez « nRF Connect for Mobile » et installez l’application sur votre smartphone.

Accédez à votre smartphone, ouvrez l’application nRF Connect de Nordic et commencez à rechercher de nouveaux appareils. Vous devriez trouver un périphérique appelé ESP32 : il s’agit du nom du serveur BLE que vous avez défini précédemment.

Connectez-vous à l’appareil ESP32. Sur Thonny IDE, vous verrez qu’il a détecté une nouvelle connexion.

Vous verrez qu’il affiche le service avec l’UUID que nous avons défini dans le code et qu’il contient deux caractéristiques avec les UUID que nous avons définis précédemment.
Lire la caractéristique
Sur la caractéristique du capteur, cliquez sur les flèches pour lire la caractéristique et activer les notifications. Ensuite, cliquez sur l’icône pour modifier le format des données (défini sur UTF-8) : sur les appareils Android, vous n’avez pas besoin de modifier le format.

Il commencera à afficher les valeurs aléatoires sur le champ Valeur. Il est mis à jour toutes les secondes.

Écrire dans la caractéristique
Pour écrire dans la caractéristique de la LED et contrôler la LED, cliquez sur la flèche supérieure de la caractéristique de la LED.

Sélectionnez le type UnsignedInt ou Bool. Ensuite, écrivez 1 ou 0 pour allumer ou éteindre la LED. 1 allume la LED et 0 l’éteint.


L’ESP32 remarquera que l’autre appareil a écrit sur la caractéristique de la LED et lira sa nouvelle valeur.

Selon la valeur écrite, la LED s’allumera ou s’éteindra.


Vous pouvez également utiliser notre application Web BLE pour vous connecter à l’appareil ESP32 BLE. Il fonctionne sur Google Chrome sur les appareils Windows et Android.

Web Bluetooth (également parfois appelé Web BLE) est une technologie relativement récente qui vous permet de connecter et de contrôler des appareils compatibles BLE, comme l’ESP32 ou le Raspberry Pi Pico, directement depuis votre navigateur Web à l’aide de JavaScript. Vous pouvez suivre ce guide pour en savoir plus : Premiers pas avec ESP32 Web Bluetooth (BLE).
Conclusion
Dans ce didacticiel, vous avez appris les bases de l’utilisation de Bluetooth Low Energy avec l’ESP32 programmé avec MicroPython. Nous vous avons montré un exemple de configuration de l’ESP32 en tant que périphérique exposant une structure du GATT. Dans un prochain didacticiel, nous vous montrerons comment configurer l’ESP32 comme périphérique central pour interagir avec les périphériques BLE.
Si vous souhaitez en savoir plus sur le Bluetooth avec l’ESP32 programmé avec l’IDE Arduino, vous pouvez plutôt suivre les tutoriels suivants :
Nous espérons que vous avez trouvé ce tutoriel utile. Si vous souhaitez en savoir plus sur MicroPython, consultez nos ressources :
Merci d’avoir lu.
Plongez dans l’histoire de Raspberry Pi avec cette vidéo :

-
ELEGOO 2PCS ESP-32 Carte de Développement Type-C, 2,4 GHz WiFi + Bluetooth
-
Lot de 4 Carte Développement ESP32-C3, Module ESP32-C3 Mini, WiFi 2,4GHz et Bluetooth 5.0 Le, Processeur RISC-V 32 Bits 160MHz, pour Arduino MicroPython, IoT Maison Intelligente Réseau de Capteurs
