1. BLE vs Classic Bluetooth
Bluetooth Low Energy (BLE) adalah protokol nirkabel yang dirancang oleh Bluetooth Special Interest Group (SIG) untuk komunikasi jarak pendek dengan konsumsi daya yang sangat rendah. Berbeda dengan Classic Bluetooth yang digunakan untuk streaming audio atau transfer file, BLE dioptimalkan untuk mengirimkan data kecil secara periodik โ sangat cocok untuk aplikasi IoT.
ESP32 mendukung kedua jenis Bluetooth ini: Classic Bluetooth (BR/EDR) dan Bluetooth Low Energy (BLE 4.2). Ini menjadikan ESP32 salah satu mikrokontroler paling fleksibel untuk proyek nirkabel.
Perbandingan BLE vs Classic Bluetooth
| Fitur | Classic Bluetooth | Bluetooth Low Energy (BLE) |
|---|---|---|
| Konsumsi Daya | Tinggi (~30 mA) | Sangat rendah (~15 ยตA sleep) |
| Throughput | Hingga 3 Mbps | Hingga 2 Mbps |
| Latency | ~100 ms | ~6 ms |
| Jarak Efektif | ~100 m | ~100 m (tergantung TX power) |
| Ukuran Data | Streaming kontinyu | Paket kecil (hingga 512 bytes) |
| Koneksi | Point-to-point | Point-to-point, Broadcast, Mesh |
| Penggunaan | Audio, file transfer, serial | Sensor, beacon, wearable, IoT |
| Waktu Koneksi | ~100 ms | ~6 ms |
Untuk proyek IoT yang menggunakan baterai, selalu pilih BLE dibanding Classic Bluetooth. BLE dapat berjalan bertahun-tahun dengan satu baterai koin CR2032 karena desain hemat dayanya.
- โ Streaming audio (A2DP)
- โ Transfer file besar (OBEX)
- โ Serial Port Profile (SPP)
- โ Konsumsi daya tinggi
- โ Tidak cocok untuk baterai
- โ Koneksi lambat
- โ Konsumsi daya sangat rendah
- โ Koneksi cepat (6 ms)
- โ Mendukung Beacon/Broadcast
- โ Ideal untuk sensor IoT
- โ Tidak cocok untuk streaming
- โ Ukuran data terbatas
2. Arsitektur BLE: GATT Protocol
Untuk memahami BLE, kita harus memahami arsitektur protokolnya. BLE menggunakan model Client-Server dengan protokol yang disebut GATT (Generic Attribute Profile). GATT mendefinisikan bagaimana dua perangkat BLE bertukar data menggunakan konsep Services, Characteristics, dan Descriptors.
Komponen Utama GATT
- Service: Kumpulan data terkait yang merepresentasikan fungsi tertentu. Setiap service memiliki UUID unik. Contoh: "Heart Rate Service" (UUID: 0x180D).
- Characteristic: Unit data terkecil dalam service. Berisi value, properties (read, write, notify), dan descriptor. Contoh: "Heart Rate Measurement" (UUID: 0x2A37).
- Descriptor: Metadata tambahan yang mendeskripsikan characteristic, seperti deskripsi teks atau format data.
- UUID: Identifier unik 128-bit untuk service dan characteristic. BLE menyediakan UUID 16-bit untuk standar umum.
UUID standar BLE menggunakan format 16-bit (contoh: 0x180D) yang disematkan dalam base UUID Bluetooth SIG: 0000XXXX-0000-1000-8000-00805F9B34FB. Untuk custom service, gunakan UUID 128-bit unik yang Anda generate sendiri.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ Weather Station (Profile) โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ ๐ฆ Service: Environmental Sensing โ โ UUID: 0x181A โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ โ ๐ Characteristic: Temperature โ โ โ โ UUID: 0x2A6E โ โ โ โ Properties: Read, Notify โ โ โ โ Value: 25.5 ยฐC โ โ โ โ ๐ Descriptor: "Suhu ruangan" โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ โ โ ๐ Characteristic: Humidity โ โ โ โ UUID: 0x2A6F โ โ โ โ Properties: Read, Notify โ โ โ โ Value: 65 % โ โ โ โ ๐ Descriptor: "Kelembaban" โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ โ โ ๐ Characteristic: Pressure โ โ โ โ UUID: 0x2A6D โ โ โ โ Properties: Read โ โ โ โ Value: 1013 hPa โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
3. Setup ESP32 BLE
ESP32 memiliki Bluetooth controller terintegrasi yang mendukung BR/EDR dan BLE. Untuk menggunakan BLE di Arduino IDE, kita perlu menginstal library yang tepat.
Library yang Dibutuhkan
ESP32 Arduino Core sudah termasuk library BLE bawaan, jadi Anda tidak perlu menginstal library tambahan. Cukup gunakan header berikut:
#include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h>
Inisialisasi BLE Device
void setup() {
Serial.begin(115200);
// Inisialisasi BLE dengan nama device
BLEDevice::init("ESP32-BLE-Server");
// Buat BLE Server
BLEServer *pServer = BLEDevice::createServer();
Serial.println("BLE Device initialized!");
Serial.print("MAC Address: ");
Serial.println(BLEDevice::getAddress().toString().c_str());
}
Nama BLE device terbatas 29 karakter. Jika nama terlalu panjang, akan dipotong secara otomatis. Gunakan nama yang singkat dan deskriptif seperti "ESP32-Sensor" atau "BeebaneLabs-Node".
4. Membuat BLE Server
BLE Server adalah perangkat yang menyediakan data (biasanya sensor node). Server membuat services dan characteristics, lalu mengiklankan (advertise) keberadaannya agar client dapat menemukan dan terhubung.
Kode Lengkap BLE Server dengan DHT Sensor
// BLE Server - ESP32 dengan Sensor DHT22
// BeebaneLabs - https://beebanelabs.pages.dev
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <DHT.h>
// UUID untuk service dan characteristic
#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc"
#define TEMP_CHAR_UUID "12345678-1234-1234-1234-123456789ab1"
#define HUMID_CHAR_UUID "12345678-1234-1234-1234-123456789ab2"
// Pin dan tipe sensor
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
BLEServer* pServer = NULL;
BLECharacteristic* pTempChar = NULL;
BLECharacteristic* pHumidChar = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
// Callback saat client terhubung/putus
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("Client terhubung!");
}
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Client terputus!");
}
};
void setup() {
Serial.begin(115200);
dht.begin();
// Inisialisasi BLE
BLEDevice::init("ESP32-WeatherStation");
// Buat BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Buat BLE Service
BLEService* pService = pServer->createService(SERVICE_UUID);
// Buat Characteristic untuk Suhu
pTempChar = pService->createCharacteristic(
TEMP_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pTempChar->addDescriptor(new BLE2902());
// Buat Characteristic untuk Kelembaban
pHumidChar = pService->createCharacteristic(
HUMID_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pHumidChar->addDescriptor(new BLE2902());
// Mulai service
pService->start();
// Mulai advertising
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
BLEDevice::startAdvertising();
Serial.println("BLE Server aktif! Menunggu koneksi...");
}
void loop() {
if (deviceConnected) {
// Baca sensor DHT22
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if (!isnan(temperature) && !isnan(humidity)) {
// Kirim data suhu
char tempStr[8];
dtostrf(temperature, 4, 1, tempStr);
pTempChar->setValue(tempStr);
pTempChar->notify();
Serial.print("Suhu: ");
Serial.print(tempStr);
Serial.println(" ยฐC");
// Kirim data kelembaban
char humidStr[8];
dtostrf(humidity, 4, 1, humidStr);
pHumidChar->setValue(humidStr);
pHumidChar->notify();
Serial.print("Kelembaban: ");
Serial.print(humidStr);
Serial.println(" %");
}
}
// Handle reconnection
if (!deviceConnected && oldDeviceConnected) {
delay(500);
pServer->startAdvertising();
Serial.println("Mulai advertising lagi...");
oldDeviceConnected = deviceConnected;
}
if (deviceConnected && !oldDeviceConnected) {
oldDeviceConnected = deviceConnected;
}
delay(2000); // Kirim data setiap 2 detik
}
Gunakan nRF Connect app (tersedia di Android dan iOS) untuk memindai dan menguji BLE Server Anda. Aplikasi ini sangat berguna untuk debugging dan memverifikasi bahwa service serta characteristic terbaca dengan benar.
5. Membuat BLE Client
BLE Client adalah perangkat yang mencari dan terhubung ke BLE Server untuk membaca data. Dalam contoh ini, kita akan membuat ESP32 kedua yang bertindak sebagai client dan membaca data suhu dari server.
// BLE Client - Membaca data dari BLE Server
// BeebaneLabs - https://beebanelabs.pages.dev
#include "BLEDevice.h"
// UUID yang sama dengan server
#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc"
#define TEMP_CHAR_UUID "12345678-1234-1234-1234-123456789ab1"
static BLEAddress* pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pTempCharacteristic;
// Callback saat menemukan device
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {
BLEDevice::getScan().stop();
pServerAddress = new BLEAddress(advertisedDevice.getAddress());
doConnect = true;
Serial.print("Server ditemukan: ");
Serial.println(advertisedDevice.toString().c_str());
}
}
};
bool connectToServer() {
Serial.print("Menghubungkan ke ");
Serial.println(pServerAddress->toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
pClient->connect(*pServerAddress);
BLERemoteService* pRemoteService =
pClient->getService(BLEUUID(SERVICE_UUID));
if (pRemoteService == nullptr) {
Serial.println("Service tidak ditemukan!");
pClient->disconnect();
return false;
}
pTempCharacteristic =
pRemoteService->getCharacteristic(BLEUUID(TEMP_CHAR_UUID));
if (pTempCharacteristic == nullptr) {
Serial.println("Characteristic tidak ditemukan!");
pClient->disconnect();
return false;
}
// Register callback untuk notifikasi
pTempCharacteristic->registerForNotify([](
BLERemoteCharacteristic* pChar,
uint8_t* pData, size_t length, bool isNotify
) {
String value = String((char*)pData);
Serial.print("Suhu dari server: ");
Serial.print(value);
Serial.println(" ยฐC");
});
connected = true;
return true;
}
void setup() {
Serial.begin(115200);
Serial.println("BLE Client - Scanning...");
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(
new MyAdvertisedDeviceCallbacks()
);
pBLEScan->setActiveScan(true);
pBLEScan->start(30); // Scan selama 30 detik
}
void loop() {
if (doConnect) {
if (connectToServer()) {
Serial.println("Terhubung ke BLE Server!");
} else {
Serial.println("Gagal terhubung.");
}
doConnect = false;
}
delay(1000);
}
6. Transfer Data BLE
BLE memiliki batasan ukuran data per transaksi. Secara default, MTU (Maximum Transmission Unit) BLE adalah 23 bytes, di mana 3 bytes digunakan untuk header ATT, sehingga hanya 20 bytes payload yang tersedia. Namun ESP32 mendukung MTU negosiasi hingga 512 bytes.
Mode Komunikasi BLE
| Mode | Arah | Deskripsi |
|---|---|---|
| Read | Client โ Server | Client membaca value dari characteristic |
| Write | Client โ Server | Client menulis value ke characteristic |
| Notify | Server โ Client | Server mengirim data otomatis saat berubah |
| Indicate | Server โ Client | Seperti Notify tapi dengan ACK |
Mengirim Data String Besar
// Fungsi untuk mengirim data panjang via BLE
// Memecah data menjadi paket-paket kecil
void sendDataChunked(BLECharacteristic* pChar, String data) {
int chunkSize = 20; // MTU default
int dataLen = data.length();
int offset = 0;
while (offset < dataLen) {
int len = min(chunkSize, dataLen - offset);
String chunk = data.substring(offset, offset + len);
pChar->setValue((uint8_t*)chunk.c_str(), len);
pChar->notify();
offset += len;
delay(50); // Delay antar paket
}
// Kirim terminator
pChar->setValue((uint8_t*)"\0", 1);
pChar->notify();
}
// Mengirim struct sensor data
struct SensorData {
float temperature;
float humidity;
float pressure;
uint16_t battery;
};
void sendSensorStruct(BLECharacteristic* pChar, SensorData data) {
pChar->setValue((uint8_t*)&data, sizeof(SensorData));
pChar->notify();
}
Untuk meningkatkan MTU, gunakan pClient->setMTU(512) pada sisi client sebelum koneksi. Namun, perlu diingat bahwa tidak semua perangkat mendukung MTU besar. Aman menggunakan 20 bytes untuk kompatibilitas maksimal dengan smartphone.
7. BLE Beacons (iBeacon & Eddystone)
BLE Beacon adalah mode di mana perangkat BLE menyiarkan (broadcast) informasi secara terus-menerus tanpa perlu koneksi. Ini sangat berguna untuk indoor positioning, asset tracking, dan proximity marketing.
Dua Format Beacon Utama
- iBeacon โ Format milik Apple. Mengirim UUID, Major, dan Minor number.
- Eddystone โ Format open-source milik Google. Mendukung UID, URL (Physical Web), dan TLM (telemetry).
Membuat iBeacon dengan ESP32
// iBeacon dengan ESP32
// BeebaneLabs - https://beebanelabs.pages.dev
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLEBeacon.h>
#define BEACON_UUID "12345678-1234-1234-1234-123456789abc"
void setup() {
Serial.begin(115200);
Serial.println("ESP32 iBeacon Starting...");
BLEDevice::init("BeebaneLabs-Beacon");
// Buat iBeacon
BLEBeacon oBeacon = BLEBeacon();
oBeacon.setManufacturerId(0x4C00); // Apple manufacturer ID
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
oBeacon.setMajor(1); // Major number
oBeacon.setMinor(10); // Minor number
oBeacon.setSignalPower(-59); // RSSI pada 1 meter
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
oAdvertisementData.setFlags(0x04); // BR/EDR Not Supported
std::string strServiceData = "";
strServiceData += (char)26; // Panjang
strServiceData += (char)0xFF; // Type: Manufacturer Specific
strServiceData += oBeacon.getData();
oAdvertisementData.addData(strServiceData);
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
pAdvertising->setAdvertisementData(oAdvertisementData);
pAdvertising->setScanResponseData(oScanResponseData);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("iBeacon aktif dan broadcasting!");
}
void loop() {
delay(1000);
}
Gunakan app Beacon Simulator atau nRF Connect untuk memverifikasi beacon ESP32 Anda terdeteksi. Untuk indoor positioning, pasang minimal 3 beacon dan gunakan trilateration berdasarkan RSSI.
8. Keamanan BLE
Keamanan adalah aspek penting dalam komunikasi BLE, terutama untuk perangkat IoT yang mengirim data sensitif. BLE mendukung beberapa tingkat keamanan:
Tingkat Keamanan BLE
| Level | Nama | Deskripsi |
|---|---|---|
| Level 1 | No Security | Tanpa enkripsi, tanpa autentikasi |
| Level 2 | Unauthenticated Encryption | Enkripsi tanpa pairing (Just Works) |
| Level 3 | Authenticated Encryption | Enkripsi dengan pairing (PIN/Passkey) |
| Level 4 | Authenticated LE SC | Secure Connections dengan ECDH |
Mengaktifkan Keamanan pada BLE Server
// Konfigurasi keamanan BLE
// BeebaneLabs - https://beebanelabs.pages.dev
void setupSecurity() {
// Set security
BLESecurity* pSecurity = new BLESecurity();
// Set authentication mode
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
// Set encryption key size
pSecurity->setCapability(ESP_IO_CAP_NONE);
// Set static PIN (untuk pairing dengan passkey)
// pSecurity->setCapability(ESP_IO_CAP_OUT);
// pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK |
// ESP_BLE_ID_KEY_MASK);
// Set key size
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
// Set passkey (6 digit)
// BLEDevice::setSecurityAuth(true, true, true);
// pSecurity->setStaticPIN(123456);
Serial.println("BLE Security diaktifkan!");
}
// Protect characteristic dengan keamanan
void setupSecureCharacteristic(BLEService* pService) {
BLECharacteristic* pSecureChar =
pService->createCharacteristic(
SECURE_CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
// Set characteristic hanya bisa diakses setelah pairing
pSecureChar->setAccessPermissions(
ESP_GATT_PERM_READ_ENCRYPTED |
ESP_GATT_PERM_WRITE_ENCRYPTED
);
pSecureChar->setValue("Secret Data");
}
Jangan pernah mengirim data sensitif (password, token, API key) melalui BLE tanpa enkripsi. Gunakan minimal Level 2 (unauthenticated encryption) bahkan untuk proyek sederhana. Untuk aplikasi produksi, gunakan Level 4 (LE Secure Connections).
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang ESP32 Bluetooth BLE: