ESP32 BLE: Menghubungkan ESP32 sebagai BLE Master (Server) dan BLE Client (Client) dengan LCD I2C

Tujuan Pembelajaran
- Memahami komunikasi BLE (Bluetooth Low Energy) antara dua ESP32.
- Menggunakan sensor DHT11 untuk mengukur suhu dan kelembapan.
- Menampilkan data dari server pada LCD I2C melalui BLE client.
- Menguasai konsep penggunaan BLE service dan characteristic.
Dasar Teori
- ESP32 dan BLE (Bluetooth Low Energy)
- ESP32 adalah mikrokontroler yang dilengkapi dengan fitur Wi-Fi dan BLE, menjadikannya sangat serbaguna untuk aplikasi IoT (Internet of Things). BLE (Bluetooth Low Energy) adalah salah satu mode komunikasi yang didukung ESP32, yang dirancang untuk konsumsi daya rendah dan jangkauan lebih jauh dibandingkan Bluetooth klasik. BLE sangat cocok untuk aplikasi yang memerlukan koneksi terus-menerus tetapi dengan penggunaan daya yang minimal, seperti perangkat yang memonitor kesehatan, sensor nirkabel, dan smart home.
- BLE Server dan Client: Dalam arsitektur BLE, ada dua peran utama yang dapat dimainkan perangkat: Server dan Client. BLE Server menyediakan data atau layanan yang dapat diakses oleh BLE Client. Server menyimpan data dan dapat memberikan pemberitahuan (notifikasi) ketika data diperbarui. Sebaliknya, Client berperan dalam meminta data atau menerima notifikasi dari Server.
- Konsep BLE Service dan Characteristic
- Service dalam BLE adalah kumpulan dari beberapa karakteristik yang didefinisikan bersama untuk menyediakan layanan tertentu. Setiap Service memiliki UUID unik yang mengidentifikasinya.
- Characteristic adalah unit data dalam BLE yang merepresentasikan informasi tertentu, misalnya suhu, kelembapan, detak jantung, dsb. Characteristic memiliki UUID sendiri dan dapat memiliki beberapa properti seperti READ, WRITE, dan NOTIFY.
- Dalam praktik ini, ESP32 server akan berperan sebagai penyedia layanan sensor suhu dan kelembapan (melalui DHT11), di mana nilai-nilai ini dikirimkan sebagai Characteristic yang dapat diakses oleh ESP32 client.
- Sensor DHT11
- DHT11 adalah sensor suhu dan kelembapan yang murah dan mudah digunakan. Sensor ini dapat mengukur suhu dalam rentang 0-50°C dengan toleransi ±2°C dan kelembapan dalam rentang 20-90% dengan toleransi ±5%. Sensor ini mengirimkan data digital yang mudah dibaca menggunakan mikroprosesor seperti ESP32.
- Dalam proyek ini, DHT11 digunakan oleh ESP32 server untuk mendapatkan pembacaan suhu dan kelembapan, yang kemudian dikirim ke ESP32 client melalui BLE.
- LCD I2C
- LCD I2C (Inter-Integrated Circuit) adalah antarmuka yang memungkinkan tampilan data pada LCD dengan hanya menggunakan dua pin untuk komunikasi data (SDA dan SCL). Hal ini membuat komunikasi lebih sederhana dibandingkan dengan antarmuka paralel tradisional.
- ESP32 client menggunakan LCD I2C untuk menampilkan data yang diterima dari BLE server, yaitu suhu dan kelembapan. Data ini ditampilkan dalam format yang mudah dibaca untuk monitoring secara real-time.
- Komunikasi BLE Server-Client
- Proses Komunikasi: Dalam komunikasi BLE, client akan mencari (scan) server BLE yang sesuai dan mencoba terhubung dengannya. Setelah terhubung, client dapat membaca data dari characteristic yang diiklankan oleh server atau menerima notifikasi jika ada data baru yang tersedia.
- Penggunaan NOTIFY: Properti NOTIFY digunakan pada BLE characteristic untuk memungkinkan server mengirimkan data secara otomatis ke client saat nilai berubah, tanpa perlu client meminta data berulang kali. Ini memastikan bahwa client mendapatkan data terbaru dengan lebih efisien.
- Implementasi Program
- Server: Program ESP32 server akan membaca data dari sensor DHT11 dan mengirimkannya melalui BLE characteristic dengan UUID spesifik. ESP32 server bertindak sebagai sumber data, menyimpan nilai suhu dan kelembapan, serta memperbarui client menggunakan notifikasi.
- Client: Program ESP32 client akan mencari server BLE dengan nama tertentu, terhubung ke layanan dan karakteristik yang relevan, lalu menerima data suhu dan kelembapan. Data ini kemudian ditampilkan pada LCD I2C untuk monitoring.
- Alat dan Bahan
- 2 buah modul ESP32
- Sensor DHT11
- LCD I2C 16×2
- Kabel jumper
- Breadboard
- Desain Rangkaian
- Langkah-langkah Pengerjaan
Bagian 1: Program ESP32 sebagai BLE Master (Server)
- Buat Rangkaian
- Sambungkan pin VCC dan GND DHT11 ke 3.3V dan GND pada ESP32.
- Sambungkan pin data DHT11 ke pin GPIO 15 pada ESP32 Server.
- Program BLE Server (Master)
- Berikut adalah kode yang akan digunakan pada ESP32 server untuk membaca data dari sensor DHT11 dan mengirimkan data tersebut melalui BLE.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <DHT.h>
#define DHTPIN 2 Â Â Â // Pin DHT11 terhubung
#define DHTTYPE DHT11 Â // Tipe sensor DHT11
DHT dht(DHTPIN, DHTTYPE);
// Komentar baris berikut untuk suhu dalam Fahrenheit
#define temperatureCelsius
// Nama server BLE
#define bleServerName "DHT11_ESP32"
// Variabel untuk menyimpan nilai suhu dan kelembapan
float temp;
float tempF;
float hum;
// Timer
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
bool deviceConnected = false;
// UUID BLE
#define SERVICE_UUID "91bad492-b950-4226-aa2b-4ede9fa42f59"
// Karakteristik dan Deskriptor Suhu
#ifdef temperatureCelsius
 BLECharacteristic temperatureCelsiusCharacteristics("cba1d466-344c-4be3-ab3f-189f80dd7518", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor temperatureCelsiusDescriptor(BLEUUID((uint16_t)0x2902));
#else
 BLECharacteristic temperatureFahrenheitCharacteristics("f78ebbff-c8b7-4107-93de-889a6a06d408", BLECharacteristic::PROPERTY_NOTIFY);
 BLEDescriptor temperatureFahrenheitDescriptor(BLEUUID((uint16_t)0x2902));
#endif
// Karakteristik dan Deskriptor Kelembapan
BLECharacteristic humidityCharacteristics("ca73b3ba-39f6-4ab3-91ae-186dc9577d99", BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor humidityDescriptor(BLEUUID((uint16_t)0x2903));
// Callbacks untuk koneksi dan disconnection
class MyServerCallbacks: public BLEServerCallbacks {
 void onConnect(BLEServer* pServer) {
  deviceConnected = true;
 };
 void onDisconnect(BLEServer* pServer) {
  deviceConnected = false;
 }
};
void setup() {
 // Memulai komunikasi serial
 Serial.begin(115200);
 // Memulai sensor DHT
 dht.begin();
 // Membuat perangkat BLE
 BLEDevice::init(bleServerName);
 // Membuat server BLE
 BLEServer *pServer = BLEDevice::createServer();
 pServer->setCallbacks(new MyServerCallbacks());
 // Membuat layanan BLE
 BLEService *dhtService = pServer->createService(SERVICE_UUID);
 // Menambahkan karakteristik dan deskriptor BLE
 #ifdef temperatureCelsius
  dhtService->addCharacteristic(&temperatureCelsiusCharacteristics);
  temperatureCelsiusDescriptor.setValue("DHT11 temperature Celsius");
  temperatureCelsiusCharacteristics.addDescriptor(&temperatureCelsiusDescriptor);
 #else
  dhtService->addCharacteristic(&temperatureFahrenheitCharacteristics);
  temperatureFahrenheitDescriptor.setValue("DHT11 temperature Fahrenheit");
  temperatureFahrenheitCharacteristics.addDescriptor(&temperatureFahrenheitDescriptor);
 #endif Â
 // Kelembapan
 dhtService->addCharacteristic(&humidityCharacteristics);
 humidityDescriptor.setValue("DHT11 humidity");
 humidityCharacteristics.addDescriptor(new BLE2902());
 // Memulai layanan
 dhtService->start();
 // Memulai periklanan
 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID);
 pServer->getAdvertising()->start();
 Serial.println("Waiting for client connection to notify...");
}
void loop() {
 if (deviceConnected) {
  if ((millis() - lastTime) > timerDelay) {
   // Membaca suhu dalam Celsius
   temp = dht.readTemperature();
   // Suhu dalam Fahrenheit
   tempF = dht.readTemperature(true);
   // Membaca kelembapan
   hum = dht.readHumidity();
   // Memastikan pembacaan berhasil
   if (isnan(temp) || isnan(hum)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
   }
   // Memberi notifikasi suhu
   #ifdef temperatureCelsius
    static char temperatureCTemp[6];
    dtostrf(temp, 6, 2, temperatureCTemp);
    temperatureCelsiusCharacteristics.setValue(temperatureCTemp);
    temperatureCelsiusCharacteristics.notify();
    Serial.print("Temperature Celsius: ");
    Serial.print(temp);
    Serial.print(" ºC");
   #else
    static char temperatureFTemp[6];
    dtostrf(tempF, 6, 2, temperatureFTemp);
    temperatureFahrenheitCharacteristics.setValue(temperatureFTemp);
    temperatureFahrenheitCharacteristics.notify();
    Serial.print("Temperature Fahrenheit: ");
    Serial.print(tempF);
    Serial.print(" ºF");
   #endif
  Â
   // Memberi notifikasi kelembapan
   static char humidityTemp[6];
   dtostrf(hum, 6, 2, humidityTemp);
   humidityCharacteristics.setValue(humidityTemp);
   humidityCharacteristics.notify(); Â
   Serial.print(" - Humidity: ");
   Serial.print(hum);
   Serial.println(" %");
  Â
   lastTime = millis();
  }
 }
}
Bagian 2: Program ESP32 sebagai BLE Client (Client)
- Buat Rangkaian
- Sambungkan pin VCC dan GND LCD I2C ke 3.3V dan GND pada ESP32 Client.
- Sambungkan pin SDA ke GPIO 21 dan SCL ke GPIO 22 pada ESP32 Client.
- Program BLE Client
- Berikut adalah kode untuk ESP32 client yang akan menampilkan data suhu dan kelembapan pada LCD I2C.
#include <BLEDevice.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
// Definisikan ukuran LCD dan alamat I2C
LiquidCrystal_PCF8574 lcd(0x27); // Ganti alamat jika berbeda
// Default Temperature is in Celsius
#define temperatureCelsius
// BLE Server name (the other ESP32 name running the server sketch)
#define bleServerName "DHT11_ESP32"
/* UUIDs for the service and characteristic we want to read */
static BLEUUID bmeServiceUUID("91bad492-b950-4226-aa2b-4ede9fa42f59");
#ifdef temperatureCelsius
 static BLEUUID temperatureCharacteristicUUID("cba1d466-344c-4be3-ab3f-189f80dd7518");
#else
 static BLEUUID temperatureCharacteristicUUID("f78ebbff-c8b7-4107-93de-889a6a06d408");
#endif
static BLEUUID humidityCharacteristicUUID("ca73b3ba-39f6-4ab3-91ae-186dc9577d99");
static boolean doConnect = false;
static boolean connected = false;
static BLEAddress *pServerAddress;
static BLERemoteCharacteristic* temperatureCharacteristic;
static BLERemoteCharacteristic* humidityCharacteristic;
const uint8_t notificationOn[] = {0x1, 0x0};
char temperatureChar[6];
char humidityChar[6];
boolean newTemperature = false;
boolean newHumidity = false;
// Connect to BLE Server
bool connectToServer(BLEAddress pAddress) {
  BLEClient* pClient = BLEDevice::createClient();
  pClient->connect(pAddress);
  Serial.println(" - Connected to server");
 Â
  BLERemoteService* pRemoteService = pClient->getService(bmeServiceUUID);
  if (pRemoteService == nullptr) {
   Serial.print("Failed to find service UUID: ");
   Serial.println(bmeServiceUUID.toString().c_str());
   return false;
  }
  temperatureCharacteristic = pRemoteService->getCharacteristic(temperatureCharacteristicUUID);
  humidityCharacteristic = pRemoteService->getCharacteristic(humidityCharacteristicUUID);
 Â
  if (temperatureCharacteristic == nullptr || humidityCharacteristic == nullptr) {
   Serial.println("Failed to find characteristic UUID");
   return false;
  }
 Â
  temperatureCharacteristic->registerForNotify(temperatureNotifyCallback);
  humidityCharacteristic->registerForNotify(humidityNotifyCallback);
  return true;
}
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 void onResult(BLEAdvertisedDevice advertisedDevice) {
  if (advertisedDevice.getName() == bleServerName) {
   advertisedDevice.getScan()->stop();
   pServerAddress = new BLEAddress(advertisedDevice.getAddress());
   doConnect = true;
   Serial.println("Device found. Connecting!");
  }
 }
};
// Temperature Notify Callback
static void temperatureNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
 strncpy(temperatureChar, (char*)pData, sizeof(temperatureChar) - 1);
 temperatureChar[sizeof(temperatureChar) - 1] = '\0'; // Pastikan null-terminated
 newTemperature = true;
}
// Humidity Notify Callback
static void humidityNotifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
 strncpy(humidityChar, (char*)pData, sizeof(humidityChar) - 1);
 humidityChar[sizeof(humidityChar) - 1] = '\0'; // Pastikan null-terminated
 newHumidity = true;
}
// Print readings to the LCD
void printReadings() {
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print("Temp: ");
 lcd.print(temperatureChar);
 #ifdef temperatureCelsius
  lcd.write(223);  // Karakter derajat
  lcd.print("C");
 #else
  lcd.write(223);  // Karakter derajat
  lcd.print("F");
 #endif
 lcd.setCursor(0, 1);
 lcd.print("Hum: ");
 lcd.print(humidityChar);
 lcd.print("%");
 Serial.print("Temperature: ");
 Serial.print(temperatureChar);
 Serial.print(" Humidity: ");
 Serial.print(humidityChar);
 Serial.println("%");
}
void setup() {
 // Start serial communication
 Serial.begin(115200);
 Serial.println("Starting BLE Client...");
 // Initialize LCD
 lcd.begin(16, 2);
 lcd.setBacklight(255);
 lcd.home();
 lcd.print("BLE Client");
 BLEDevice::init("");
 BLEScan* pBLEScan = BLEDevice::getScan();
 pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
 pBLEScan->setActiveScan(true);
 pBLEScan->start(30);
}
void loop() {
 if (doConnect == true) {
  if (connectToServer(*pServerAddress)) {
   Serial.println("Connected to BLE Server.");
   temperatureCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
   humidityCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);
   connected = true;
  } else {
   Serial.println("Failed to connect to server; restart device to rescan.");
  }
  doConnect = false;
 }
 if (newTemperature && newHumidity) {
  newTemperature = false;
  newHumidity = false;
  printReadings();
 }
 delay(1000);
}
- Pertanyaan
- Jelaskan fungsi dari BLE UUID dalam program di atas.
- Apa perbedaan antara BLE Server dan BLE Client?
- Bagaimana cara menghubungkan dan menampilkan data dari sensor di BLE Client?
- Mengapa kita perlu mengatur BLE Characteristic dengan PROPERTY_NOTIFY?
Dengan jobsheet ini, peserta diharapkan memahami cara mengirim data sensor dari ESP32 BLE server ke ESP32 BLE client dan menampilkannya pada LCD I2C.







