In this tutorial, we’ll learn how to activate and manage Bluetooth Low Energy (BLE) on an ESP32 using the Arduino programming language.
Bluetooth Low Energy is a low-energy version of Bluetooth that sends small packets of data at regular intervals.
Equipment
- ESP32 module (on-board Bluetooth+Wifi)
- A computer with Python installed or smartphone
- USB cable for ESP32-computer connection
Environment and IDE configuration
To program your ESP32 with the Arduino IDE, you can follow this previous tutorial.
Communication Série via BLE
BLE communication must be configured with a certain number of addresses (UIIDs), which are like memory registers in which we can read and write.
//https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE #include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value = pCharacteristic->getValue(); if (value.length() > 0) { Serial.println("*********"); Serial.print("New value: "); for (int i = 0; i < value.length(); i++) Serial.print(value[i]); Serial.println(); Serial.println("*********"); } } }; void setup() { Serial.begin(115200); Serial.println("1- Download and install an BLE Terminal FREE"); Serial.println("2- Scan for BLE devices in the app"); Serial.println("3- Connect to ESP32BLE"); Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); BLEDevice::init("ESP32BLE"); BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); pCharacteristic->setCallbacks(new MyCallbacks()); pCharacteristic->setValue("Hello World"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); pAdvertising->start(); Serial.print("Server address:"); Serial.println(BLEDevice::getAddress().toString().c_str()); } void loop() { // put your main code here, to run repeatedly: delay(2000); }
N.B.: you can retrieve the ESP32’s MAC address using the BLEDevice::getAddress().toString().c_str() function.
Pairing
Once you’ve configured the module as you wish, you can pair the ESP32 with the system of your choice, just like any other Bluetooth device. Select the name from the list of detected devices (name ESP32BLE)
Test BLE communication using BLE Terminal
We’re going to test BLE communication using the BLE Terminal application.
The message is exchanged between the phone and the ESP32 via Bluetooth LE.
Bidirectional communication between device and ESP32BLE
This is the code used to modify the characteristic value using the serial monitor. In this way, the connected device and the serial monitor modify the value of the characteristic.
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" BLECharacteristic *pCharacteristic = NULL; std::string msg; class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value = pCharacteristic->getValue(); if (value.length() > 0) { Serial.println("*********"); Serial.print("New value: "); for (int i = 0; i < value.length(); i++) Serial.print(value[i]); Serial.println(); Serial.println("*********"); } } }; void setup() { Serial.begin(115200); Serial.println("1- Download and install an BLE Terminal Free"); Serial.println("2- Scan for BLE devices in the app"); Serial.println("3- Connect to ESP32BLE"); Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); BLEDevice::init("ESP32BLE"); BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); pCharacteristic->setCallbacks(new MyCallbacks()); pCharacteristic->setValue("Hello World"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue pAdvertising->setMinPreferred(0x12); pAdvertising->start(); Serial.print("Server address:"); Serial.println(BLEDevice::getAddress().toString().c_str()); } void loop() { readSerialPort(); //Send data to slave if(msg!=""){ pCharacteristic->setValue(msg); msg=""; } delay(2000); } void readSerialPort(){ while (Serial.available()) { delay(10); if (Serial.available() >0) { char c = Serial.read(); //gets one byte from serial buffer msg += c; //add to String } } Serial.flush(); //clean buffer }
In practice, it will certainly be preferable to use one service for writing and one for reading.
Communicating between ESP32 and Python via BLE
You can manage Bluetooth Low Energy communication from your PC.
To do this, install the Bleak package
python -m pip install bleak
Detect bluetooth devices
import asyncio from bleak import BleakScanner async def main(): target_name = "ESP32BLE" target_address = None devices = await BleakScanner.discover() for d in devices: print(d) if target_name == d.name: target_address = d.address print("found target {} bluetooth device with address {} ".format(target_name,target_address)) break asyncio.run(main())
Output
C1:2E:C6:8E:47:E8: None 3C:61:05:31:5F:12: ESP32BLE found target ESP32BLE bluetooth device with address 3C:61:05:31:5F:12
Connecting and communicating with the ESP32
Here’s a Python script to automatically connect to the ESP32 BLE device from a PC. To communicate with the BLE device, it is important to know the uiids of the various services. We define the UUIDs as those defined in the ESP32 code
import asyncio from bleak import BleakScanner from bleak import BleakClient async def main(): target_name = "ESP32BLE" target_address = None SERVICE_UUID= "4fafc201-1fb5-459e-8fcc-c5c9c331914b" CHARACTERISTIC_UUID= "beb5483e-36e1-4688-b7f5-ea07361b26a8" devices = await BleakScanner.discover() for d in devices: print(d) if target_name == d.name: target_address = d.address print("found target {} bluetooth device with address {} ".format(target_name,target_address)) break if target_address is not None: async with BleakClient(target_address) as client: print(f"Connected: {client.is_connected}") while 1: text = input() if text == "quit": break await client.write_gatt_char(CHARACTERISTIC_UUID, bytes(text, 'UTF-8'), response=True) try: data = await client.read_gatt_char(CHARACTERISTIC_UUID) data = data.decode('utf-8') #convert byte to str print("data: {}".format(data)) except Exception: pass else: print("could not find target bluetooth device nearby") asyncio.run(main())
Application
- Create a connected IoT sensor that sends data via BLE
- Creating a BLE Terminal with React Native