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

