Bluetooth BLE on ESP32
Bluetooth BLE on ESP32
Advertise a BLE service, send notifications, and connect from a phone.
Bluetooth on the ESP32
The ESP32 supports both Classic Bluetooth (for audio and serial SPP profiles) and Bluetooth Low Energy (BLE). BLE is more commonly used in modern IoT projects because it is energy-efficient and compatible with phones.
BLE vs Classic Bluetooth
| Feature | Classic Bluetooth | BLE |
|---|---|---|
| Data rate | Up to 3 Mbps | Up to 2 Mbps |
| Power use | Higher | Very low |
| Phone support | Older apps / SPP | All modern phones |
| Use cases | Audio, file transfer | Sensors, beacons, IoT |
BLE Terminology
- GATT ߀” Generic Attribute Profile: how data is organised in BLE
- Service ߀” a collection of related data (e.g., "Heart Rate Service")
- Characteristic ߀” a single data value within a service (e.g., "Heart Rate Measurement")
- UUID ߀” a unique identifier for services and characteristics
- Notify ߀” the server pushes updates to a connected client automatically
- Read/Write ߀” the client reads or writes a value on demand
BLE Server (Arduino Library)
The ESP32 Arduino core includes BLEDevice.h for easy BLE setup:
#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;
class ServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { deviceConnected = true; }
void onDisconnect(BLEServer* pServer) { deviceConnected = false; }
};
void setup() {
Serial.begin(115200);
BLEDevice::init("ESP32-BLE-Sensor");
BLEServer* pServer = BLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
BLEService* pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
Serial.println("BLE advertising started");
}
void loop() {
if (deviceConnected) {
int value = analogRead(34);
String msg = String(value);
pCharacteristic->setValue(msg.c_str());
pCharacteristic->notify();
delay(1000);
}
}
Scanning for BLE Devices
#include <BLEDevice.h>
#include <BLEScan.h>
BLEScan* pBLEScan;
void setup() {
Serial.begin(115200);
BLEDevice::init("");
pBLEScan = BLEDevice::getScan();
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);
}
void loop() {
BLEScanResults results = pBLEScan->start(5, false);
Serial.print("Devices found: ");
Serial.println(results.getCount());
for (int i = 0; i < results.getCount(); i++) {
BLEAdvertisedDevice d = results.getDevice(i);
Serial.println(d.toString().c_str());
}
pBLEScan->clearResults();
delay(2000);
}
Classic Bluetooth Serial (SPP)
For simple serial communication over Bluetooth Classic (works with Bluetooth serial apps on Android):
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
SerialBT.begin("ESP32-BT-Device"); // Name visible when pairing
Serial.println("Bluetooth device ready to pair.");
}
void loop() {
if (SerialBT.available()) {
char c = SerialBT.read();
Serial.write(c); // Echo to USB serial
}
if (Serial.available()) {
SerialBT.write(Serial.read()); // Forward USB serial to BT
}
}
BLE vs WiFi Coexistence
The ESP32 can run WiFi and BLE simultaneously, but there are limitations:
- They share the same 2.4 GHz antenna via time-division multiplexing
- Running both reduces throughput on each
- ADC2 cannot be used when WiFi or BT radio is active
- Use
esp_coex_preference_set()to prioritise one radio if needed