Pendahuluan — Janji Bluetooth Terpenuhi

Di artikel pengenalan Mengenal ESP32 (#1), kita menyebut ESP32 punya WiFi dan Bluetooth dalam satu chip. Seri 1 dan Tier 1 Seri 2 fokus ke WiFi, MQTT, dan cloud — sampai sekarang belum ada tutorial Bluetooth. Artikel Tier 2 ini menutup celah itu.

Kamu akan membuat BLE GATT server di ESP32 yang membaca DHT22 (#5) dan mengirim JSON suhu & kelembaban ke smartphone lewat notifikasi BLE — tanpa router WiFi, tanpa broker 192.168.1.50, tanpa kabel USB setelah upload firmware.

Prasyarat: Sudah pernah upload sketch Arduino (Blink LED #3), paham GPIO dasar, dan pernah baca DHT22 (#5). Familiar dengan pola JSON di MQTT (#7) membantu membandingkan transport — tapi MQTT tidak wajib untuk lab ini.

BLE vs WiFi vs ESP-NOW — Kapan Pakai Apa

TransportJangkauan tipikalInfrastrukturCocok untuk
BLE~5–15 mHanya ESP32 + smartphoneSetup perangkat, monitoring dekat, app companion
WiFi + MQTTSeluruh jaringan rumahRouter + broker (#16)Dashboard 24/7, histori Grafana (#19)
ESP-NOW (#25)~50–200 m (LOS)Dua+ ESP32, tanpa routerSensor node → gateway ESP32

BLE bukan pengganti WiFi (#4) atau MQTT — melainkan saluran langsung ke HP saat teknisi berdiri di samping perangkat. Kombinasi umum di produksi: BLE untuk provisioning SSID/password, WiFi untuk telemetry harian.

Arsitektur GATT — Server, Service, Characteristic

BLE (Bluetooth Low Energy) memakai model GATT (Generic Attribute Profile):

  • Peripheral (ESP32) — mengiklankan diri, menyimpan service & characteristic
  • Central (smartphone) — scan, connect, baca/tulis characteristic
  • Service — grup logical (mis. "sensor lingkungan")
  • Characteristic — nilai data aktual (JSON suhu/kelembaban)
  • Notify — ESP32 push data otomatis saat terhubung (butuh descriptor BLE2902)
Smartphone (Central)          ESP32 (Peripheral / GATT Server)
      │ scan "KindoESP32-DHT22"
      │ connect ─────────────────► onConnect → deviceConnected=true
      │ subscribe notify ◄──────── pCharacteristic->notify(JSON)
      │ read value ◄────────────── setValue + notify tiap 2 detik

UUID Kustom Koding Indonesia

Gunakan UUID 128-bit kustom agar tidak bentrok dengan service standar (Battery Service, dll.):

  • Service: b10d4001-0001-4001-8001-000032000001
  • Characteristic: b10d4002-0002-4002-8002-000032000002
  • Nama perangkat: KindoESP32-DHT22 — muncul saat scan di app

Simpan UUID ini di dokumentasi proyek internal — app Android/iOS custom nanti memakai nilai yang sama.

Hardware & Wiring DHT22

Sama seperti artikel DHT22 (#5):

  • VCC → 3.3 V
  • GND → GND
  • DATAGPIO 4
  • Resistor pull-up 4,7–10 kΩ antara DATA dan VCC (banyak modul sudah onboard)

ESP32 DevKit cukup — tidak perlu modul Bluetooth eksternal. Chip sudah punya radio BLE seperti dijelaskan di #1.

Library & Dependensi

Di Arduino IDE, library BLE sudah termasuk di core ESP32 — tidak perlu install tambahan:

  • BLEDevice.h, BLEServer.h, BLEUtils.h, BLE2902.h
  • DHT sensor library oleh Adafruit (sama seperti #5)

Di PlatformIO (#29), tambahkan di platformio.ini:

lib_deps =
  adafruit/DHT sensor library
  adafruit/Adafruit Unified Sensor

Sketch BLE Server — Struktur Utama

Alur setup():

  1. BLEDevice::init("KindoESP32-DHT22")
  2. Buat server + callback connect/disconnect
  3. Buat service & characteristic (READ + NOTIFY)
  4. Tambah descriptor BLE2902 untuk notifikasi
  5. startAdvertising() — ESP32 siap discan

Sketch Lengkap — BLE + DHT22

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <DHT.h>

#define DHT_PIN  4
#define DHT_TYPE DHT22

#define SERVICE_UUID        "b10d4001-0001-4001-8001-000032000001"
#define CHARACTERISTIC_UUID "b10d4002-0002-4002-8002-000032000002"

DHT dht(DHT_PIN, DHT_TYPE);
BLECharacteristic* pCharacteristic = nullptr;
bool deviceConnected = false;

class ServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* server) {
    deviceConnected = true;
    Serial.println("BLE client terhubung");
  }
  void onDisconnect(BLEServer* server) {
    deviceConnected = false;
    Serial.println("BLE client putus — advertising ulang");
    BLEDevice::startAdvertising();
  }
};

void setup() {
  Serial.begin(115200);
  dht.begin();
  delay(2000);  // stabilisasi DHT22 — sama seperti #5

  BLEDevice::init("KindoESP32-DHT22");
  BLEServer* server = BLEDevice::createServer();
  server->setCallbacks(new ServerCallbacks());

  BLEService* service = server->createService(SERVICE_UUID);
  pCharacteristic = service->createCharacteristic(
    CHARACTERISTIC_UUID,
    BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
  );
  pCharacteristic->addDescriptor(new BLE2902());
  service->start();

  BLEAdvertising* advertising = BLEDevice::getAdvertising();
  advertising->addServiceUUID(SERVICE_UUID);
  advertising->setScanResponse(true);
  BLEDevice::startAdvertising();

  Serial.println("BLE advertising — buka nRF Connect di HP");
}

void loop() {
  if (!deviceConnected) {
    delay(500);
    return;
  }

  float suhu = dht.readTemperature();
  float kelembaban = dht.readHumidity();

  if (isnan(suhu) || isnan(kelembaban)) {
    Serial.println("DHT22 baca gagal — cek wiring GPIO4");
    delay(2000);
    return;
  }

  // Unix contoh konsisten Seri 2 — production: NTP (#34)
  char json[96];
  snprintf(json, sizeof(json),
    "{\"suhu\":%.1f,\"kelembaban\":%.1f,\"unix\":%lu}",
    suhu, kelembaban, (unsigned long)1782977400UL);

  pCharacteristic->setValue(json);
  pCharacteristic->notify();
  Serial.println(json);

  delay(2000);
}

Payload JSON mengikuti konvensi topic MQTT kodingindonesia/esp32/dht22/data — memudahkan port ke MQTT (#7) nanti. Timestamp contoh 1782977400 = 2026-07-02T14:30:00 UTC (lihat NTP #34).

Memahami GATT — Istilah di nRF Connect

Saat membuka nRF Connect, kamu akan melihat istilah berikut:

  • Advertising — ESP32 mengirim beacon "saya di sini" dengan nama KindoESP32-DHT22
  • Connection — smartphone dan ESP32 menyetujui sesi; callback onConnect di sketch
  • Primary Service — wadah UUID b10d4001-...
  • Characteristic — nilai JSON; properti READ + NOTIFY
  • CCC (Client Characteristic Configuration) — descriptor BLE2902 yang mengaktifkan notifikasi

Tidak perlu hafal seluruh spesifikasi Bluetooth SIG untuk proyek sensor — cukup paham alur advertise → connect → subscribe notify → terima JSON.

Callback Connect & Disconnect

Kelas ServerCallbacks menangani siklus hidup koneksi:

  • onConnect — set deviceConnected = true; mulai kirim notifikasi di loop()
  • onDisconnect — set flag false; panggil BLEDevice::startAdvertising() agar perangkat bisa discan lagi

Tanpa restart advertising setelah disconnect, smartphone tidak akan menemukan ESP32 di scan berikutnya — bug umum pemula BLE.

Testing dengan nRF Connect

nRF Connect (Nordic Semiconductor) gratis di Android & iOS — standar de facto untuk debug BLE:

  1. Upload sketch, buka Serial Monitor 115200
  2. Di HP: buka nRF Connect → tab Scanner
  3. Cari KindoESP32-DHT22 → tap Connect
  4. Buka service UUID b10d4001-...
  5. Tap characteristic → aktifkan Notify (ikon tiga panah)
  6. Lihat JSON masuk setiap ~2 detik: {"suhu":28.5,"kelembaban":62.0,"unix":1782977400}

Pro tip: Jika tidak muncul di scan, pastikan Bluetooth HP aktif, ESP32 tidak sedang connect USB ke laptop yang juga punya BLE stack aktif, dan coba reboot board setelah upload.

Testing dengan Serial Monitor

Serial mencetak status connect/disconnect dan setiap JSON yang di-notify. Urutan debug yang disarankan:

  1. Pastikan BLE advertising muncul setelah boot
  2. Connect dari HP → harus muncul BLE client terhubung
  3. Jika DHT22 gagal → perbaiki wiring sebelum blame BLE
  4. Putuskan HP → harus advertising ulang otomatis

BLE + WiFi Bersamaan di ESP32

ESP32 bisa menjalankan BLE dan WiFi bersamaan (coexistence), tapi:

  • Radio berbagi antena — throughput turun, timing lebih sensitif
  • Sketch gabungan BLE provisioning + MQTT lebih kompleks — pertimbangkan FreeRTOS (#31) untuk pisah task
  • Artikel ini sengaja BLE-only agar fokus; tambah WiFi setelah pola GATT paham

Jangkauan, Latency & Daya

  • Jangkauan: 5–15 m di dalam ruangan; dinding beton mengurangi drastis
  • Latency notify: <100 ms — cocok untuk display real-time di app
  • Daya: Advertising terus-menerus boros baterai; untuk node baterai kombinasikan deep sleep (#11) + wake singkat saat ada koneksi
  • Throughput: JSON kecil (<100 byte) jauh di bawah limit BLE — aman

Perbandingan dengan Pola Seri 2 Lain

Jika tujuanmu dashboard 24 jam di server, tetap pakai MQTT ke broker dan capstone #10. BLE cocok untuk:

  • Teknisi lapangan cek suhu tanpa buka laptop
  • Onboarding: kirim SSID GANTI_NAMA_WIFI dari app (lanjutan di WiFiManager #12)
  • Demo cepat di pameran sekolah/kampus tanpa infrastruktur WiFi

Keamanan BLE Dasar

  • BLE tanpa pairing di artikel ini = data terbuka untuk siapa saja dalam jangkauan — OK untuk lab, tidak untuk data sensitif
  • Produksi: aktifkan LE Secure Connections + pairing PIN atau Just Works sesuai threat model
  • Jangan kirim kredensial MQTT (GANTI_PASSWORD_MQTT) mentah lewat characteristic tanpa enkripsi
  • Nama perangkat generik (KindoESP32-DHT22) lebih aman daripada nama lokasi spesifik di publik

Estimasi Biaya

KomponenHarga perkiraan (IDR)
ESP32 DevKit35.000 – 55.000
Modul DHT2215.000 – 25.000
Smartphone (sudah punya)0
Total~50.000 – 80.000

Checklist Sebelum Demo

  • ☐ Sketch ter-upload tanpa error compile
  • ☐ Serial menampilkan BLE advertising
  • ☐ DHT22 baca valid (bukan nan) di Serial
  • ☐ nRF Connect bisa connect ke KindoESP32-DHT22
  • ☐ Notify aktif — JSON update tiap 2 detik
  • ☐ UUID service & characteristic cocok dengan dokumentasi proyek

FAQ Singkat

Perlu internet?
Tidak. BLE langsung ESP32 ↔ smartphone.
Bisa pakai app buatan sendiri?
Ya — Flutter/React Native dengan plugin BLE; UUID harus sama dengan sketch.
Bluetooth Classic vs BLE?
ESP32 mendukung keduanya; IoT sensor hampir selalu pakai BLE karena hemat daya.
Bentrok dengan WiFi sketch lama?
Sketch ini tidak init WiFi — upload terpisah atau gabung manual dengan pola coexistence.

Tips & Troubleshooting

  • Tidak muncul di scan: Cek BLEDevice::init terpanggil; reboot ESP32; matikan BLE laptop jika bentrok
  • Connect langsung putus: Naikkan interval notify; kurangi beban Serial print
  • Notify kosong: Pastikan client subscribe (ikon notify aktif di nRF Connect)
  • DHT22 nan: Cek GPIO4, pull-up, dan delay(2000) setelah dht.begin()
  • iOS tidak connect: iOS butuh UUID valid 128-bit — jangan pakai UUID 16-bit saja
  • Heap menurun: Hindari new berulang di loop(); callback cukup sekali di setup

Langkah Selanjutnya — Tier 2 Seri 2

Artikel pertama Tier 2 pelengkap selesai — janji Bluetooth dari #1 terpenuhi. Lanjutkan ke topik pelengkap:

BLE membuka ESP32 ke dunia smartphone — lanjutkan perjalanan di halaman artikel Koding Indonesia.