Look, I get it. Youāve finally mastered making an LED blink over Wi-Fi, and now Iām asking you to learn Bluetooth Low Energy (BLE). I know what youāre thinking: āIsnāt Bluetooth just that thing that mysteriously refuses to pair with my car stereo every morning?ā Yes. Yes it is.
But hereās the reality: if you want to build battery-powered wearables, smart home sensors, or literally anything that needs to talk to a smartphone without obliterating its own battery in 45 minutes, you have to learn BLE.
Is the documentation a labyrinth of acronyms like GATT, GAP, UUIDs, and Characteristics? Absolutely. Does it feel like trying to read spaghetti code written by a caffeinated spider? You bet. But fear not. Today, weāre going to strip away the magic blue smoke of confusion and build a BLE GATT Server on an ESP32. Weāll make your board broadcast its feelings (or at least some sensor data) directly to your phone. Letās dive in.
To follow along, you just need a microcontroller with built-in BLE capabilities. The ESP32 is the undisputed king here.
Before we write code, we need to understand the two main roles in BLE.
This is how devices find each other. GAP controls advertising and connections.
Once connected, GATT takes over. This is how data is actually organized and exchanged. Think of GATT like a highly structured spreadsheet.
Weāre going to write an Arduino sketch that turns our ESP32 into a BLE server. It will host one Service and one Characteristic. You can read the characteristic from your phone using an app like nRF Connect.
#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"
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
// 1. Initialize the BLE Device
BLEDevice::init("ESP32_ElectronParade");
// 2. Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
// 3. Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// 4. Create a BLE Characteristic
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setValue("Hello from the Magic Blue Smoke!");
// 5. Start the service
pService->start();
// 6. Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
void loop() {
// We aren't doing anything in the loop for this basic example.
delay(2000);
}
ESP32_ElectronParade.SERVICE_UUID).CHARACTERISTIC_UUID). Youāll see the text āHello from the Magic Blue Smoke!ā pop up.You just created your first BLE server. Youāve bypassed the cloud entirely and passed data straight to your phone like a true hardware wizard.
In our next lesson, weāll look at sending live sensor data and updating the characteristic continuously. But for now, go take a break. You earned it.