The ESP32 features built-in Bluetooth, compatible with both Classic Bluetooth and Bluetooth Low Energy (BLE).
BLE is a variant of the Bluetooth standard (introduced in Bluetooth version 4.0) designed for applications that require low energy consumption.
- Classic Bluetooth, which is optimized for continuous data transmission (like audio)
- BLE is designed to send small packets of data intermittently.
This drastically reduces power consumption, making it ideal for IoT (Internet of Things) devices and wearables. Let’s see how we can use it.
Differences Between Classic Bluetooth and BLE
Classic Bluetooth and Bluetooth Low Energy (BLE) are two variants of Bluetooth technology, but they have key differences:
- Power Consumption: BLE consumes significantly less power than Classic Bluetooth, allowing devices to operate for months or even years on a small battery.
- Data Transfer Rate: BLE has a lower data transfer rate (up to 1 Mbps) compared to Classic Bluetooth (up to 3 Mbps).
- Latency: Classic Bluetooth has lower latency than BLE (important for real-time applications, like wireless headphones).
- Connection: BLE is designed for quick, short-lived connections, while Classic Bluetooth is more suitable for continuous, long-lasting connections.
Basically, BLE is “worse” in everything except for power consumption (which makes sense).
BLE Architecture
BLE is based on a client-server architecture, where:
- Peripheral Device: Acts as a server and provides data. For example, a temperature sensor that sends readings.
- Central Device: Acts as a client and requests data. For example, a smartphone that receives readings from the sensor.
Key Components of BLE
- GATT (Generic Attribute Profile): Defines how data is organized and transmitted between BLE devices. Data is organized into services and characteristics.
- Service: A set of related characteristics. For example, a health service might include characteristics like heart rate and body temperature.
- Characteristic: A specific value that can be read or written. For example, the current heart rate.
- Advertising: Peripheral devices send advertising packets for central devices to detect. These packets contain information like the device name and the services it offers.
Setting Up ESP32 for BLE
To set up the ESP32 as a BLE peripheral device, we will use the ESP32 BLE Arduino
library, which simplifies BLE implementation.
Configuring BTLE
To use BTLE, we need to include the BLEDevice.h
library and create an instance of BLEServer
. Then, we add services and characteristics that we want to expose to other devices.
#include <BLEDevice.h>
#include <BLEServer.h>
// Variables to identify the BTLE service and characteristic
#define SERVICE_UUID "00000000-0000-0000-0000-000000000000"
#define CHARACTERISTIC_UUID "00000000-0000-0000-0000-0000000b0000"
BLEServer *pServer;
BLEService *pService;
BLECharacteristic *pCharacteristic;
void configureBTLE() {
// Create the BTLE server
BLEDevice::init("ESP32_BTLE");
pServer = BLEDevice::createServer();
// Create the service
pService = pServer->createService(SERVICE_UUID);
// Add a characteristic to the service
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
// Start the service
pService->start();
// Start advertising the BTLE server
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
}
Reading and Writing BTLE Data
Once the characteristics are added, we can read or write data. For example,
void setup() {
Serial.begin(115200);
configureBTLE();
}
void loop() {
// Read data from the BTLE characteristic
std::string receivedValue = pCharacteristic->getValue();
if (receivedValue.length() > 0) {
Serial.print("Received value: ");
Serial.println(receivedValue.c_str());
}
// Write data to the BTLE characteristic
std::string valueToSend = "Hello, this is a BTLE message";
pCharacteristic->setValue(valueToSend);
pCharacteristic->notify();
delay(1000);
}
Interaction from Android / iOS
Once the ESP32 is configured as a BLE peripheral, you can connect to it from a phone using an app like nRF Connect (available for iOS and Android).
- Open the app and scan for nearby BLE devices.
- Connect to the device named “ESP32_BLE_Server”.
- Explore the service and characteristic we created.
- Read or write to the characteristic to interact with the ESP32.
Practical Examples
Setting Up a Simple BLE Server
In this example, we set up the ESP32 as a BLE server that sends simulated temperature data.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
// BLE Device Name
#define DEVICE_NAME "ESP32_BLE_Server"
// Custom UUIDs
#define SERVICE_UUID "00000000-0000-0000-0000-000000000000"
#define CHARACTERISTIC_UUID "00000000-0000-0000-0000-0000000b0000"
BLEServer *bleServer;
BLECharacteristic *characteristic;
void setup() {
Serial.begin(9600);
// Initialize the BLE device
BLEDevice::init(DEVICE_NAME);
// Create the BLE server
bleServer = BLEDevice::createServer();
// Create the service and characteristic
BLEService *service = bleServer->createService(SERVICE_UUID);
characteristic = service->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
// Set an initial value
characteristic->setValue("Initializing...");
// Start the service
service->start();
// Make the device discoverable
BLEAdvertising *advertising = BLEDevice::getAdvertising();
advertising->start();
Serial.println("BLE server started");
}
void loop() {
// Simulate a temperature
float temperature = random(20, 30) + 0.1 * random(0, 10);
String value = "Temp: " + String(temperature) + "°C";
// Update the characteristic value
characteristic->setValue(value.c_str());
characteristic->notify(); // Notify connected clients
Serial.println(value);
delay(1000);
}
BLE Client that Reads Data
In this example, we configure an ESP32 as a BLE client that connects to the previous server to read data.
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEClient.h>
#define SERVER_ADDRESS "XX:XX:XX:XX:XX:XX" // MAC address of the BLE server
#define SERVICE_UUID "00000000-0000-0000-0000-000000000000"
#define CHARACTERISTIC_UUID "00000000-0000-0000-0000-0000000b0000"
void setup() {
Serial.begin(9600);
// Initialize the BLE device
BLEDevice::init("ESP32_BLE_Client");
// Connect to the BLE server
BLEClient *client = BLEDevice::createClient();
client->connect(BLEAddress(SERVER_ADDRESS));
// Access the service and characteristic
BLERemoteService *service = client->getService(SERVICE_UUID);
BLERemoteCharacteristic *characteristic = service->getCharacteristic(CHARACTERISTIC_UUID);
if (characteristic->canRead()) {
String value = characteristic->readValue().c_str();
Serial.println("Received value: " + value);
}
}
void loop() {
// The client can read the value at regular intervals
delay(2000);
}