Bluetooth Low Energy (BLE) is a powerful communication protocol, especially for IoT applications where low power consumption is crucial. In this tutorial, we’ll learn how to set up an ESP32 as both a BLE server and a BLE client using the Arduino IDE. By the end, you’ll be able to establish a BLE connection, exchange data between two ESP32s, and apply BLE communication to various projects.
Introduction to ESP32 BLE Communication
The ESP32 microcontroller, known for its robust feature set and Wi-Fi capability, also supports Bluetooth Low Energy (BLE). BLE is a wireless communication protocol optimized for minimal energy consumption, suitable for applications like smart wearables, IoT devices, and home automation.
In this project, we’ll explore BLE communication between two ESP32 modules. One ESP32 will act as a BLE server, broadcasting data, while the other will be a BLE client, receiving and processing that data. This setup enables seamless data transfer between the two microcontrollers without excessive power use, making it ideal for battery-powered projects.
Working Principle of ESP32 BLE Server and Client
In BLE, the server advertises data, and the client connects to the server to receive data. Here’s how the BLE connection works in this project:
- BLE Server: The server periodically advertises its presence and provides data that other devices can request. In our example, the BLE server ESP32 will continuously broadcast a sample message or a sensor value.
- BLE Client: The client scans for available BLE servers, connects to the server, and receives the data. Our BLE client ESP32 will be programmed to scan, detect the BLE server, and fetch the advertised data.
The ESP32’s BLE library for Arduino IDE makes this interaction straightforward, allowing us to define BLE characteristics, establish connections, and send data.
Things we Need
To complete this project, you will need the following components:
- 2x ESP32 Development Boards
- Breadboard and Jumper Wires
- Power Source (USB or battery pack for ESP32)
The ESP32 boards will handle the entire BLE setup, so no additional components are required for basic BLE communication.
Circuit Diagram
This BLE communication setup doesn’t require a complex circuit. You simply need to power the ESP32 boards and connect them to your computer for programming.
Circuit Setup:
- Power ESP32: Connect the ESP32 boards to your computer via USB cables for both programming and power.
- Optional Connections: If using sensors or LEDs for testing, connect them according to the pinout requirements of your ESP32 board.
Note: BLE communication is wireless, so no additional wired connections are required between the two ESP32 boards.
Setting Up the ESP32 BLE Server and Client
We’ll write separate code for the BLE server and the BLE client. First, we set up the BLE server to advertise data, and then configure the BLE client to connect and read data from the server.
Code for ESP32 BLE Server
This code sets up the ESP32 to broadcast a BLE characteristic, which the client can read.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
int value = 0;
class MyCallbacks: public BLECharacteristicCallbacks {
void onRead(BLECharacteristic* pCharacteristic) {
Serial.println("Client is reading the value");
}
};
void setup() {
Serial.begin(115200);
BLEDevice::init("ESP32 BLE Server");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->setValue("Hello BLE Client");
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
Serial.println("BLE server started, advertising...");
}
void loop() {
// Update the value periodically
value++;
pCharacteristic->setValue(String(value).c_str());
pCharacteristic->notify(); // Notify clients of updated value
delay(1000);
}
Code for ESP32 BLE Client
The BLE client code scans for the BLE server, connects to it, and reads the advertised characteristic.
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
bool deviceFound = false;
BLEAdvertisedDevice *myDevice;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {
deviceFound = true;
myDevice = new BLEAdvertisedDevice(advertisedDevice);
advertisedDevice.getScan()->stop();
}
}
};
void setup() {
Serial.begin(115200);
BLEDevice::init("ESP32 BLE Client");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(5);
}
void loop() {
if (deviceFound) {
BLEClient* pClient = BLEDevice::createClient();
pClient->connect(myDevice);
BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
if (pRemoteService == nullptr) {
Serial.println("Failed to find the service.");
pClient->disconnect();
return;
}
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
if (pRemoteCharacteristic == nullptr) {
Serial.println("Failed to find the characteristic.");
pClient->disconnect();
return;
}
std::string value = pRemoteCharacteristic->readValue();
Serial.print("Received Value: ");
Serial.println(value.c_str());
pClient->disconnect();
delay(1000);
}
}
Explanation of the Code and Working of BLE Setup
BLE Server Code Explanation
- Initialize BLE: Start the BLE server with
BLEDevice::init
. - Create Service and Characteristic: Define a UUID for the service and characteristic. These unique identifiers allow the BLE client to recognize the server and its data.
- Broadcast Data:
pCharacteristic->setValue("Hello BLE Client");
sets an initial value.notify()
inloop()
updates and notifies the client.
BLE Client Code Explanation
- Scan for BLE Devices: The client continuously scans for devices that match the service UUID.
- Connect to BLE Server: Once the target server is found, the client connects, retrieves the characteristic, and reads the data.
- Display Data: The received value is printed on the Serial Monitor.
Testing
- Upload the server code to one ESP32 and the client code to the other.
- Open the Serial Monitor on both ESP32 boards to observe the communication. The server will display a log of data being sent, and the client will display data as it’s received.
Conclusion
In this tutorial, we set up two ESP32 modules to communicate via BLE. The server continuously broadcasts data, while the client receives and displays it. BLE communication can be extended to other ESP32 projects, such as remote sensors, smart home devices, and portable health monitors.
With the ESP32’s BLE capabilities, you can develop energy-efficient projects that easily integrate into larger IoT ecosystems. Experiment with other data types and characteristics to fully harness the power of BLE communication!