Panduan · 14 mnt baca

Tutorial Bluetooth ESP32: Classic vs BLE, Kendali LED, dan Sensor

Panduan Bluetooth ESP32: kendalikan LED dari ponselmu dengan BluetoothSerial, buat server BLE dengan layanan dan UUID, uji dengan nRF Connect, lalu kirim data sensor.

Bluetooth ESP32 memakai radio nirkabel yang sudah tertanam di dalam chip untuk menghubungkan ESP32-mu ke ponsel, tablet, atau komputer tanpa modul tambahan apa pun. ESP32 orisinal mendukung dua jenis Bluetooth yang berbeda sekaligus: Bluetooth Classic, yang bekerja seperti port serial, dan Bluetooth Low Energy (BLE), yang hemat sekali dayanya. Di Arduino Uno kamu butuh modul HC-05 terpisah untuk berbicara dengan ponselmu; di ESP32 beberapa baris kode sudah cukup. Di panduan ini kamu akan menyiapkan kedua cara itu langkah demi langkah, menyalakan LED dari ponselmu, menulis layanan BLE sendiri, dan mengirim data sensor ke ponselmu.

Cara mana yang kamu pilih bergantung pada ponsel dan board-mu. Kalau kamu punya ponsel Android dan ESP32 DevKit klasik, awal tercepat adalah library BluetoothSerial. Kalau kamu memakai iPhone, atau board-mu ESP32-S3 atau ESP32-C3, Bluetooth Classic tidak akan membantumu; kamu butuh BLE. Menjelang akhir tulisan kamu juga akan menemukan proyek BLE yang sama untuk Raspberry Pi Pico W dengan MicroPython. Kalau ini pertama kalinya kamu memakai ESP32, selesaikan dulu pengaturan Arduino IDE di Apa Itu ESP32?.

Board pengembangan ESP32 dengan Bluetooth dan BLE
Board pengembangan ESP32: Wi-Fi, Bluetooth Classic, dan BLE semuanya ada di dalam chip yang sama, jadi kamu tidak butuh modul Bluetooth eksternal untuk terhubung ke ponsel.

Apa itu Bluetooth ESP32? Classic vs BLE

Bluetooth Classic adalah jenis lama yang sudah akrab, dipakai di headphone nirkabel dan modul seperti HC-05. ESP32 memakainya lewat SPP (Serial Port Profile): byte mengalir satu per satu seolah ada kabel serial tak terlihat antara ponsel dan board. Kamu melakukan pairing perangkat dulu di pengaturan Bluetooth ponselmu, lalu menyambung dengan aplikasi terminal. BLE, di sisi lain, adalah sistem terpisah yang diperkenalkan bersama Bluetooth 4.0 pada 2010 dan dirancang untuk perangkat yang berjalan berbulan-bulan dengan baterai. BLE tidak punya aliran data terus-menerus; ESP32 menyimpan tabel nilai kecil (GATT), dan ponsel membaca nilai-nilai itu, menulisnya, atau berlangganan (subscribe) agar diberi tahu ketika nilainya berubah. Walaupun namanya mirip, keduanya tidak bisa saling berbicara: aplikasi yang ditulis untuk BLE tidak bisa terhubung ke perangkat Bluetooth Classic.

FiturBluetooth Classic (SPP)Bluetooth Low Energy (BLE)
Library ArduinoBluetoothSerial (sudah ada di paket ESP32)BLEDevice (sudah ada di paket ESP32) atau NimBLE-Arduino
Model dataAliran byte terus-menerus, seperti port serialTabel GATT yang terdiri dari layanan dan karakteristik
Jumlah dataTinggi, cocok untuk aliran terus-menerusPaket kecil; bawaannya 20 byte per notifikasi
Konsumsi dayaTerus tinggi selama terhubungSangat rendah; bisa berjalan berbulan-bulan dengan baterai
PairingDilakukan di pengaturan Bluetooth ponselTidak perlu; koneksi dibuat di dalam aplikasi
AndroidDidukungDidukung
iPhone (iOS)Tidak didukungDidukung
ESP32 yang mendukungHanya ESP32 orisinal (WROOM-32, WROVER)ESP32, S3, C3, C6, dan H2 (bukan S2)
Penggunaan umumTerminal serial, mobil robot, pengganti HC-05Sensor, perangkat wearable, rumah pintar, beacon

Aturan praktisnya begini: kalau kamu ingin mengirim dan menerima teks dari ponsel Android dan cepat melihat hasilnya, pakai Classic; kalau kamu butuh dukungan iPhone, daya rendah, atau salah satu board ESP32 yang lebih baru, pakai BLE. Kalau kamu memulai proyek baru, BLE layak dipelajari, karena bekerja di kedua jenis ponsel dan ada di setiap chip Espressif baru yang punya Bluetooth. Kalau sebelumnya kamu pernah memakai HC-05, kamu akan melihat bahwa logika perintah dari Modul Bluetooth HC-05 sama persis di BluetoothSerial.

Board ESP32 mana punya Bluetooth jenis apa?

Tidak semua chip di keluarga ESP32 punya perangkat keras Bluetooth yang sama. Kesalahan yang paling sering terjadi adalah mencoba meng-upload contoh BluetoothSerial yang ditemukan di internet ke board ESP32-S3 atau ESP32-C3. Espressif hanya menaruh Bluetooth Classic di chip ESP32 orisinal; chip-chip berikutnya yang punya Bluetooth hanya mendukung BLE. Kamu bisa tahu chip apa yang dibawa board-mu dari tulisan di pelindung logam modulnya (misalnya ESP32-WROOM-32 atau ESP32-S3-WROOM-1).

Board / chipBluetooth ClassicBLECatatan
ESP32 (WROOM-32, WROVER, DevKit V1)YaYa (4.2)Satu-satunya keluarga ESP32 yang bisa menjalankan BluetoothSerial
ESP32-S2TidakTidakHanya Wi-Fi
ESP32-S3TidakYa (Bluetooth 5)BluetoothSerial tidak bisa dikompilasi, pakai BLE
ESP32-C3TidakYa (Bluetooth 5)Board BLE kecil dan murah
ESP32-C6TidakYa (Bluetooth 5)Juga punya Wi-Fi 6, Zigbee, dan Thread
ESP32-H2TidakYa (Bluetooth 5)Tanpa Wi-Fi; fokus pada Zigbee dan Thread
ESP8266 (NodeMCU, Wemos D1)TidakTidakButuh HC-05 atau HM-10 untuk Bluetooth
Raspberry Pi Pico W / Pico 2 WAda di perangkat kerasYaMicroPython hanya mendukung BLE
Raspberry Pi Pico / Pico 2TidakTidakButuh modul Bluetooth eksternal
Kalau kamu mengompilasi kode BluetoothSerial dengan ESP32-S3, C3, C6, atau H2 terpilih di menu board, baris pengecekan di contoh-contoh Espressif (dan di kode kita di bawah) menghentikan kompilasi dengan pesan “Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.” Ini bukan masalah instalasi atau library; chip-nya memang tidak punya perangkat keras Bluetooth Classic. Kalau kamu punya salah satu board ini, langsung saja ke contoh BLE (Nordic UART) di bawah.

Bluetooth Classic: mengendalikan LED dari ponselmu dengan BluetoothSerial

Library BluetoothSerial memperkenalkan ESP32 ke ponselmu seperti modul HC-05 dan memakai perintah yang sama dengan objek Serial: available(), read(), print(), dan println(). Library ini sudah ikut dalam paket board ESP32, jadi tidak ada yang perlu diinstal lagi. Untuk rangkaiannya, LED bawaan berwarna biru yang terhubung ke GPIO 2 di kebanyakan board DevKit sudah cukup; kalau kamu memakai LED eksternal, hubungkan ke GPIO 2 lewat resistor 220 Ω. Kamu bisa mengecek nilai resistornya dengan kalkulator resistor LED dan melihat pin aman ESP32 lainnya di alat diagram pin. Pastikan ESP32 Dev Module terpilih di Tools › Board, lalu upload kode ini:

C++ (Arduino IDE)
1#include "BluetoothSerial.h"
2
3// Klasik Bluetooth yalnızca ilk ESP32'de var / Classic Bluetooth exists only on the original ESP32
4#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
5#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
6#endif
7#if !defined(CONFIG_BT_SPP_ENABLED)
8#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
9#endif
10
11BluetoothSerial SerialBT;
12const int LED_PIN = 2;                 // DevKit dahili LED / DevKit onboard LED
13bool ledAcik = false;
14
15void ledAyarla(bool acik) {
16  ledAcik = acik;
17  digitalWrite(LED_PIN, acik ? HIGH : LOW);
18  SerialBT.println(acik ? "LED acik / LED on" : "LED kapali / LED off");
19}
20
21void setup() {
22  Serial.begin(115200);
23  pinMode(LED_PIN, OUTPUT);
24  SerialBT.begin("BlueGrays-ESP32");   // telefonda görünecek ad / name shown on the phone
25  Serial.println("Eslesmeye hazir / Ready to pair");
26}
27
28void loop() {
29  if (SerialBT.available()) {
30    String komut = SerialBT.readStringUntil('\n');  // satır sonuna kadar oku / read until newline
31    komut.trim();                                   // CR ve boşlukları sil / strip CR and spaces
32    komut.toLowerCase();
33
34    if (komut == "1" || komut == "ac") {
35      ledAyarla(true);
36    } else if (komut == "0" || komut == "kapat") {
37      ledAyarla(false);
38    } else if (komut == "durum") {
39      SerialBT.println(ledAcik ? "LED acik / LED on" : "LED kapali / LED off");
40    } else if (komut.length() > 0) {
41      SerialBT.print("Bilinmeyen komut / Unknown command: ");
42      SerialBT.println(komut);
43    }
44  }
45
46  if (Serial.available()) {            // Seri Monitör'den telefona / Serial Monitor to phone
47    SerialBT.write(Serial.read());
48  }
49}

Kode ini membaca setiap baris yang datang dari ponsel sebagai perintah. readStringUntil() menunggu sampai karakter baris baru tiba, dan trim() membuang karakter carriage return (CR) yang ditambahkan ponsel beserta spasi; kalau kamu lupa baris ini, mengirim “1” sebenarnya mengantarkan “1” ditambah karakter CR yang tak terlihat, dan tidak ada perintah yang cocok. Karena perintah diubah ke huruf kecil, “AC” dan “ac” punya efek yang sama. Dua baris paling bawah meneruskan semua yang kamu ketik di Serial Monitor ke ponsel, jadi kamu bisa mengobrol tanpa kabel antara komputer dan ponselmu. Pengecekan #error di bagian atas membuat kompilasi berhenti dengan pesan yang jelas kalau board yang terpilih salah.

Menyambung dari ponselmu dengan Serial Bluetooth Terminal

  1. Setelah meng-upload, buka Serial Monitor di 115200 baud dan cari pesan “Eslesmeye hazir / Ready to pair”.
  2. Di ponsel Android-mu, pindai perangkat baru di Setelan › Bluetooth dan lakukan pairing dengan BlueGrays-ESP32. Secara bawaan ESP32 melakukan pairing tanpa meminta PIN; kalau ponsel menampilkan kode konfirmasi, terima saja.
  3. Instal aplikasi Serial Bluetooth Terminal buatan Kai Morich dari Google Play.
  4. Di aplikasi, buka Devices dari menu samping dan pilih BlueGrays-ESP32 di tab Bluetooth Classic.
  5. Ketuk ikon colokan di bilah atas; begitu “Connected” muncul di layar, kamu sudah terhubung.
  6. Ketik 1 di kotak bawah lalu kirim: LED menyala dan muncul balasan “LED acik / LED on”. 0 mematikannya, dan durum (bahasa Turki untuk “status”) memberi tahu keadaannya sekarang.
Tekan lama tombol makro M1 dan M2 di bagian bawah Serial Bluetooth Terminal lalu simpan perintah “1” dan “0” agar LED bisa dinyalakan dan dimatikan dengan sekali ketuk. Opsi Send › Newline di pengaturan aplikasi bawaannya CR+LF; trim() di kode sudah membuang CR yang berlebih. Hubungkan relay sebagai ganti LED, dan kode yang sama bisa menyalakan lampu atau stopkontak; lihat Apa Itu Modul Relay? untuk detailnya.

Bagaimana BLE bekerja? Layanan, karakteristik, dan UUID

BLE punya dua peran. ESP32 berperan sebagai server (peripheral): ia menyimpan nilai-nilai dan mengumumkan “aku di sini” dengan mengirim paket advertising kecil berukuran paling banyak 31 byte dalam selang waktu pendek (bawaannya setiap 20–40 ms di ESP32). Ponsel berperan sebagai klien (central): ia memindai sekitarnya, menemukan perangkat, lalu terhubung. Setelah terhubung, ponsel membaca tabel GATT (Generic Attribute Profile) milik ESP32. Bayangkan tabel ini sebagai lemari arsip: setiap laci adalah sebuah layanan (service), dan setiap map di dalam laci adalah sebuah karakteristik (characteristic). Karakteristik membawa satu nilai (misalnya suhu atau perintah LED) plus properti yang menyatakan apa saja yang bisa dilakukan dengan nilai itu.

IstilahArtiContoh di panduan ini
Advertising (siaran)Paket pendek tempat perangkat mengumumkan nama dan layanannyaAdvertising dengan nama “BlueGrays-BLE”
Layanan (service)Grup yang menyatukan karakteristik-karakteristik yang saling berkaitanLayanan Nordic UART, Environmental Sensing (0x181A)
KarakteristikSatu nilai yang dibaca atau ditulisPerintah LED, suhu (0x2A6E)
READPonsel membaca nilainya kapan pun ia mauSuhu yang terakhir diukur
WRITE / WRITE_NRPonsel menulis nilai (dengan atau tanpa respons)Menulis “1” menyalakan LED
NOTIFYESP32 memberi tahu ponsel dengan sendirinya ketika nilainya berubahSuhu baru setiap 2 detik
Deskriptor 0x2902 (CCCD)Sakelar yang menyalakan dan mematikan notifikasiTombol panah tiga di nRF Connect
UUIDNomor identitas layanan dan karakteristik6E400001-B5A3-F393-E0A9-E50E24DCCA9E

Setiap layanan dan karakteristik dikenali lewat sebuah UUID. Fitur standar yang didefinisikan Bluetooth SIG memakai nomor 16-bit yang pendek, misalnya 0x180F untuk layanan baterai dan 0x2A6E untuk karakteristik suhu. Untuk datamu sendiri yang tidak punya padanan standar, buat UUID acak 128-bit; di Python cukup import uuid; print(uuid.uuid4()). Ketika memilih UUID sendiri, jangan pakai nilai yang cocok dengan pola xxxxxxxx-0000-1000-8000-00805F9B34FB, karena pola itu disediakan untuk nomor 16-bit milik Bluetooth SIG. Kalau kamu ingin lancar bekerja dengan aplikasi yang sudah ada, lebih praktis lagi memakai UUID yang dikenal semua orang:

Layanan / karakteristikUUIDFormat data
Battery Service / Battery Level0x180F / 0x2A191 byte, persen (0–100)
Environmental Sensing / Temperature0x181A / 0x2A6EBilangan bulat bertanda 2 byte, 0,01 °C
Environmental Sensing / Humidity0x181A / 0x2A6FBilangan bulat tak bertanda 2 byte, 0,01%
Heart Rate / Heart Rate Measurement0x180D / 0x2A37Byte flag + nilai detak jantung
Nordic UART Service (NUS)6E400001-B5A3-F393-E0A9-E50E24DCCA9ERX 6E400002 (tulis), TX 6E400003 (notifikasi)

Server BLE ESP32: mengendalikan LED dari ponselmu (Nordic UART)

Di proyek BLE pertama kita, ESP32 diubah menjadi server Nordic UART Service (NUS). NUS adalah layanan “port serial lewat BLE” yang didefinisikan Nordic Semiconductor dan sudah menjadi standar de facto: ponsel menulis ke karakteristik RX dan ESP32 mengirim notifikasi dari karakteristik TX. Kelebihannya, banyak aplikasi siap pakai mengenalinya; nRF Connect, tab Bluetooth LE di Serial Bluetooth Terminal, dan Bluefruit Connect di iPhone semuanya bekerja dengan kode yang sama. Jadi kode di bagian ini bekerja dengan Android maupun iPhone, dan juga di board yang hanya punya BLE seperti ESP32-S3 dan C3. Library BLE adalah bagian dari paket board ESP32, jadi tidak ada yang perlu diinstal lagi.

C++ (Arduino IDE)
1#include <BLEDevice.h>
2#include <BLEServer.h>
3#include <BLEUtils.h>
4#include <BLE2902.h>
5
6// Nordic UART Service UUID'leri / Nordic UART Service UUIDs
7#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
8#define RX_UUID      "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"  // telefon -> ESP32 / phone -> ESP32
9#define TX_UUID      "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"  // ESP32 -> telefon / ESP32 -> phone
10
11const int LED_PIN = 2;
12
13BLEServer *sunucu = nullptr;
14BLECharacteristic *txKarakteristik = nullptr;
15volatile bool bagli = false;
16bool oncekiBagli = false;
17volatile int bekleyenKomut = -1;          // -1: yeni komut yok / no new command
18
19class SunucuOlaylari : public BLEServerCallbacks {
20  void onConnect(BLEServer *s) { bagli = true; }
21  void onDisconnect(BLEServer *s) { bagli = false; }
22};
23
24class RxOlaylari : public BLECharacteristicCallbacks {
25  void onWrite(BLECharacteristic *c) {
26    String deger = c->getValue().c_str(); // çekirdek 2.x ve 3.x ile uyumlu / works on core 2.x and 3.x
27    deger.trim();
28    if (deger == "1") bekleyenKomut = 1;
29    else if (deger == "0") bekleyenKomut = 0;
30  }
31};
32
33void cevapGonder(const char *mesaj) {
34  txKarakteristik->setValue((uint8_t *)mesaj, strlen(mesaj));
35  txKarakteristik->notify();              // telefona bildirim / notify the phone
36}
37
38void setup() {
39  Serial.begin(115200);
40  pinMode(LED_PIN, OUTPUT);
41
42  BLEDevice::init("BlueGrays-BLE");       // yayında görünecek ad / advertised name
43  sunucu = BLEDevice::createServer();
44  sunucu->setCallbacks(new SunucuOlaylari());
45
46  BLEService *servis = sunucu->createService(SERVICE_UUID);
47
48  txKarakteristik = servis->createCharacteristic(TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
49  txKarakteristik->addDescriptor(new BLE2902());   // bildirim anahtarı (CCCD) / notification switch (CCCD)
50
51  BLECharacteristic *rx = servis->createCharacteristic(
52    RX_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
53  rx->setCallbacks(new RxOlaylari());
54
55  servis->start();
56
57  BLEAdvertising *yayin = BLEDevice::getAdvertising();
58  yayin->addServiceUUID(SERVICE_UUID);
59  yayin->setScanResponse(true);
60  yayin->setMinPreferred(0x06);           // iPhone bağlantısına yardımcı / helps iPhone connections
61  yayin->setMaxPreferred(0x12);
62  BLEDevice::startAdvertising();
63  Serial.println("BLE yayini basladi / BLE advertising started");
64}
65
66void loop() {
67  if (bekleyenKomut != -1) {              // telefondan komut geldi / command received
68    int komut = bekleyenKomut;
69    bekleyenKomut = -1;
70    digitalWrite(LED_PIN, komut == 1 ? HIGH : LOW);
71    if (bagli) cevapGonder(komut == 1 ? "LED acik" : "LED kapali");
72  }
73
74  if (!bagli && oncekiBagli) {            // bağlantı koptu / disconnected
75    delay(500);                           // yığına zaman tanı / give the stack time
76    sunucu->startAdvertising();           // yeniden görünür ol / advertise again
77    Serial.println("Yeniden yayin / Advertising again");
78    oncekiBagli = false;
79  }
80  if (bagli && !oncekiBagli) {            // yeni bağlantı / new connection
81    Serial.println("Telefon baglandi / Phone connected");
82    oncekiBagli = true;
83  }
84}

Bagian terpenting dari kode ini adalah kelas-kelas callback. onConnect dan onDisconnect memantau status koneksi, dan onWrite menangkap nilai yang ditulis ponsel ke karakteristik RX. Fungsi-fungsi ini berjalan di task milik stack Bluetooth sendiri, jadi alih-alih mengerjakan tugas panjang di dalamnya, kita hanya mengubah sebuah variabel; pekerjaan sebenarnya dilakukan loop(). Deskriptor BLE2902 yang ditambahkan ke karakteristik TX diperlukan supaya ponsel bisa menyalakan notifikasi. Ketika koneksi terputus, ESP32 tidak memulai advertising lagi dengan sendirinya; tanpa baris startAdvertising() di loop(), perangkat tidak bisa ditemukan lagi setelah koneksi pertama. Baris setMinPreferred dan setMaxPreferred menambahkan interval koneksi yang diinginkan ke dalam paket advertising, sehingga koneksi dengan iPhone jadi lebih mudah.

Kode di artikel ini dikompilasi dengan versi 2.0.11 dari ESP32 Arduino core (paket board). Di versi ini, fungsi BLE seperti getValue() mengembalikan std::string, dan kebanyakan contoh di internet juga memakai tipe itu. Sejak core 3.0, fungsi BLE beralih ke tipe String milik Arduino, sehingga kode yang ditulis dengan std::string gagal di 3.x dengan error “conversion from 'String' to non-scalar type 'std::string' requested”. Kalau kamu menyimpan hasilnya ke variabel String dan menambahkan .c_str() di ujungnya, seperti di kode kita, baris itu bisa dikompilasi di core 2.x maupun 3.x.

Menguji BLE dengan nRF Connect langkah demi langkah

  1. Instal aplikasi gratis nRF Connect for Mobile dari Nordic Semiconductor di ponselmu; aplikasinya tersedia untuk Android dan iPhone.
  2. Di Android, aplikasi meminta izin Bluetooth dan Perangkat di sekitar (di Android 11 dan yang lebih lama, izin lokasi dengan layanan lokasi dinyalakan); tanpa izin itu daftar pindaian tetap kosong.
  3. Di tab Scanner, tekan SCAN dan cari BlueGrays-BLE di daftar. Nilai RSSI di sebelahnya adalah kekuatan sinyal: -50 dBm berarti sangat dekat, -90 dBm adalah batas jangkauan.
  4. Ketuk CONNECT. Layar yang terbuka menampilkan Nordic UART Service; kalau kamu memakai UUID buatanmu sendiri, di sini akan tertulis “Unknown Service”.
  5. Ketuk ikon panah tiga ke bawah di sebelah karakteristik TX (6E400003) untuk menyalakan notifikasi.
  6. Ketuk ikon panah ke atas di sebelah karakteristik RX (6E400002), pilih Text (UTF-8) sebagai tipe data, ketik 1, lalu kirim. LED menyala dan “LED acik” muncul di baris TX.

Tes ini adalah cara tercepat untuk membuktikan sisi ESP32 bekerja sebelum kamu menulis aplikasi ponsel. Kalau ada yang salah, coba dulu dengan nRF Connect: kalau perangkat tidak ada di daftar, masalahnya di advertising; kalau bisa terhubung tetapi layanannya tidak muncul, panggilan start() untuk layanan itu terlewat; kalau kamu menulis nilai tetapi LED tetap mati, periksa perbandingan di dalam onWrite. Kamu juga bisa mencoba kode yang sama dari tab Bluetooth LE di Serial Bluetooth Terminal; aplikasinya mengenali layanan NUS dan kamu bisa mengobrol persis seperti dengan Bluetooth Classic. Kalau kamu ingin membuat aplikasi Android sendiri, lihat App Inventor dan Bluetooth Arduino.

Menyiarkan data sensor lewat BLE (DHT22)

Sensor suhu dan kelembapan DHT22
DHT22 mengukur suhu dengan resolusi 0,1 °C; karena bisa bekerja di 3,3 V, sensor ini langsung terhubung ke ESP32.
Pin DHT22ESP32Catatan
VCC (+)3V3Bekerja dari 3,3 sampai 5 V
DATA (OUT)GPIO 4Pada sensor tanpa modul, tambahkan resistor 10 kΩ antara DATA dan 3V3
GND (−)GNDGround bersama

Di contoh ini kita memakai layanan standar Environmental Sensing (0x181A) beserta karakteristik suhu (0x2A6E) dan kelembapan (0x2A6F)-nya. Keunggulan UUID standar adalah aplikasi seperti nRF Connect bisa mengenali nilainya dan langsung menampilkannya sebagai “23.45 °C”. Standarnya meminta nilai dikirim bukan sebagai teks, melainkan sebagai bilangan bulat yang dikali 100: suhu 23,45 °C menjadi angka 2345 dan dikirim sebagai 2 byte, byte rendah lebih dulu (little-endian). Untuk membaca sensornya kita memakai DHT sensor library dari Adafruit, yang sudah kita bahas di panduan DHT11; kamu bisa menginstalnya dari Library Manager.

C++ (Arduino IDE)
1#include <BLEDevice.h>
2#include <BLEServer.h>
3#include <BLE2902.h>
4#include <DHT.h>
5
6#define DHT_PIN 4
7DHT dht(DHT_PIN, DHT22);
8
9BLEServer *sunucu = nullptr;
10BLECharacteristic *sicaklikKar = nullptr;
11BLECharacteristic *nemKar = nullptr;
12volatile bool bagli = false;
13bool oncekiBagli = false;
14unsigned long sonOlcum = 0;
15
16class SunucuOlaylari : public BLEServerCallbacks {
17  void onConnect(BLEServer *s) { bagli = true; }
18  void onDisconnect(BLEServer *s) { bagli = false; }
19};
20
21BLECharacteristic *karakteristikEkle(BLEService *servis, uint16_t uuid) {
22  BLECharacteristic *k = servis->createCharacteristic(
23    BLEUUID(uuid), BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
24  k->addDescriptor(new BLE2902());        // bildirim için gerekli / needed for notify
25  return k;
26}
27
28void setup() {
29  Serial.begin(115200);
30  dht.begin();
31
32  BLEDevice::init("BlueGrays-Sensor");
33  sunucu = BLEDevice::createServer();
34  sunucu->setCallbacks(new SunucuOlaylari());
35
36  BLEService *servis = sunucu->createService(BLEUUID((uint16_t)0x181A));  // Environmental Sensing
37  sicaklikKar = karakteristikEkle(servis, 0x2A6E);  // sıcaklık, 0,01 °C / temperature, 0.01 °C
38  nemKar = karakteristikEkle(servis, 0x2A6F);       // nem, %0,01 / humidity, 0.01 %
39  servis->start();
40
41  BLEAdvertising *yayin = BLEDevice::getAdvertising();
42  yayin->addServiceUUID(BLEUUID((uint16_t)0x181A));
43  yayin->setScanResponse(true);
44  BLEDevice::startAdvertising();
45}
46
47void loop() {
48  if (millis() - sonOlcum >= 2000) {      // DHT22 en fazla 2 sn'de bir / DHT22 at most every 2 s
49    sonOlcum = millis();
50    float t = dht.readTemperature();
51    float h = dht.readHumidity();
52    if (!isnan(t) && !isnan(h)) {
53      int16_t t100 = (int16_t)lroundf(t * 100);    // 23,45 °C -> 2345
54      uint16_t h100 = (uint16_t)lroundf(h * 100);  // %41,2 -> 4120
55      sicaklikKar->setValue((uint8_t *)&t100, 2);  // 2 bayt, düşük bayt önce / 2 bytes, little-endian
56      nemKar->setValue((uint8_t *)&h100, 2);
57      if (bagli) {
58        sicaklikKar->notify();
59        nemKar->notify();
60      }
61      Serial.printf("%.2f C  %.2f %%\n", t, h);
62    }
63  }
64
65  if (!bagli && oncekiBagli) {            // bağlantı koptu / disconnected
66    delay(500);
67    sunucu->startAdvertising();           // yeniden yayın / advertise again
68    oncekiBagli = false;
69  }
70  if (bagli && !oncekiBagli) oncekiBagli = true;
71}

Kode ini mengambil pembacaan setiap dua detik, karena DHT22 tidak bisa dibaca lebih sering dari itu. Nilainya ditulis dulu ke karakteristik dengan setValue(), sehingga ponsel juga mendapat pengukuran terbaru ketika membaca (READ) tanpa menunggu notifikasi. Kalau ponsel terhubung, notify() langsung mengirim nilai baru. Kalau nRF Connect tidak bisa menerjemahkan nilainya, kamu akan melihat byte mentahnya: untuk 23,45 °C layar menampilkan (0x) 29-09. Balik urutan byte-nya dan kamu mendapat 0x0929, yaitu 2345 dalam desimal; bagi dengan 100 dan kamu mendapat suhunya. Kamu perlu melakukan perhitungan yang sama ketika menulis aplikasimu sendiri. Mengirim nilai sebagai teks mungkin terlihat lebih mudah, tetapi format standar memakai lebih sedikit byte dan bekerja dengan aplikasi yang sudah ada.

Mengirim data tanpa koneksi juga bisa: ESP32 dapat menaruh hasil pembacaan langsung di kolom data produsen (manufacturer data) pada paket advertising-nya, dan setiap ponsel atau gateway di sekitarnya bisa membacanya tanpa terhubung. Cara ini disebut beacon, dan format terbuka BTHome yang dikenali Home Assistant bekerja dengan cara yang sama. Kalau ESP32 masuk ke deep sleep setelah setiap pembacaan, umur baterai bisa mencapai berbulan-bulan; lihat caranya di deep sleep ESP32.

Bluetooth ESP32 dengan iPhone (iOS)

Pertanyaan yang paling sering diajukan pengguna iPhone adalah kenapa ESP32 tidak muncul di pengaturan Bluetooth. Apple hanya membuka profil port serial Bluetooth Classic (SPP) untuk aksesori bersertifikat MFi, jadi kamu tidak bisa terhubung dari iPhone ke ESP32 yang menjalankan BluetoothSerial. Solusinya adalah BLE, dan contoh NUS serta sensor di atas bekerja dengan baik di iPhone. Ada beberapa hal yang perlu kamu ingat:

  • Mencari di Pengaturan: perangkat BLE tidak tercantum di pengaturan Bluetooth iOS; buat koneksinya di dalam aplikasi seperti nRF Connect, LightBlue, atau Bluefruit Connect.
  • Cache: iOS mengingat nama perangkat dan daftar layanannya. Kalau kamu masih melihat daftar lama setelah mengubah layanan di kodemu, matikan lalu nyalakan lagi Bluetooth ponselmu.
  • Tanpa alamat MAC: iOS tidak memperlihatkan alamat MAC perangkat kepada aplikasi dan memberi setiap perangkat pengenal buatannya sendiri; kalau kamu memakai beberapa ESP32, beri masing-masing nama yang berbeda.
  • Interval koneksi: jangan hapus baris setMinPreferred(0x06) dan setMaxPreferred(0x12); contoh-contoh Espressif juga menyarankannya untuk mengatasi masalah koneksi dengan iPhone.
  • Aplikasimu sendiri: di iOS kamu memakai Core Bluetooth dengan Swift; pairing tidak diperlukan, dan aplikasi langsung memindai berdasarkan UUID layanan.

BLE dengan Raspberry Pi Pico W dan MicroPython

Board Raspberry Pi Pico
Di keluarga Raspberry Pi Pico, Bluetooth hanya ada di board yang namanya mengandung huruf W (Pico W, Pico 2 W); Pico standar tidak punya koneksi nirkabel.

Di keluarga Raspberry Pi Pico, Bluetooth hanya tersedia di Pico W dan Pico 2 W. Chip CYW43439 di board-nya mendukung Bluetooth Classic dan BLE secara perangkat keras, tetapi MicroPython hanya memakai BLE (mulai versi 1.20). Kode di bawah adalah padanan MicroPython dari contoh NUS di ESP32; karena memakai UUID yang sama, kamu mengujinya dengan cara yang sama lewat nRF Connect dan Serial Bluetooth Terminal. Kamu harus sudah mem-flash build MicroPython yang dibuat untuk Pico W. Kode ini juga membaca sensor suhu internal board dan mengirimnya ke ponsel yang terhubung setiap 2 detik.

MicroPython
1import bluetooth
2import struct
3import time
4from machine import Pin, ADC
5from micropython import const
6
7_IRQ_CENTRAL_CONNECT = const(1)
8_IRQ_CENTRAL_DISCONNECT = const(2)
9_IRQ_GATTS_WRITE = const(3)
10_FLAG_READ = const(0x0002)
11_FLAG_WRITE_NO_RESPONSE = const(0x0004)
12_FLAG_WRITE = const(0x0008)
13_FLAG_NOTIFY = const(0x0010)
14
15# Nordic UART Service: ESP32 örneğiyle aynı UUID'ler / same UUIDs as the ESP32 example
16NUS_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
17TX = (bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_READ | _FLAG_NOTIFY)
18RX = (bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"), _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE)
19
20led = Pin("LED", Pin.OUT)       # Pico W dahili LED / Pico W onboard LED
21sensor = ADC(4)                 # dahili sıcaklık sensörü / internal temperature sensor
22
23ble = bluetooth.BLE()
24ble.active(True)
25((tx_h, rx_h),) = ble.gatts_register_services(((NUS_UUID, (TX, RX)),))
26baglanti = None
27
28
29def alan(tur, veri):            # uzunluk + tür + veri / length + type + data
30    return struct.pack("BB", len(veri) + 1, tur) + veri
31
32
33YAYIN = alan(0x01, b"\x06") + alan(0x09, b"BlueGrays-Pico")  # bayraklar + ad / flags + name
34YANIT = alan(0x07, bytes(NUS_UUID))                          # 128 bit UUID / 128-bit UUID
35
36
37def yayinla():
38    ble.gap_advertise(100000, adv_data=YAYIN, resp_data=YANIT)  # 100 ms aralık / interval
39
40
41def olay(event, data):
42    global baglanti
43    if event == _IRQ_CENTRAL_CONNECT:
44        baglanti = data[0]
45    elif event == _IRQ_CENTRAL_DISCONNECT:
46        baglanti = None
47        yayinla()                                 # yeniden görünür ol / advertise again
48    elif event == _IRQ_GATTS_WRITE and data[1] == rx_h:
49        komut = ble.gatts_read(rx_h).decode().strip()
50        if komut == "1":
51            led.on()
52        elif komut == "0":
53            led.off()
54
55
56ble.irq(olay)
57yayinla()
58
59while True:
60    if baglanti is not None:
61        volt = sensor.read_u16() * 3.3 / 65535
62        sicaklik = 27 - (volt - 0.706) / 0.001721   # veri sayfası formülü / datasheet formula
63        try:
64            ble.gatts_notify(baglanti, tx_h, "T:{:.1f}\n".format(sicaklik).encode())
65        except OSError:
66            pass                                    # bağlantı yeni koptu / just disconnected
67    time.sleep(2)

Karena modul bluetooth di MicroPython bersifat tingkat rendah, kita menyusun sendiri paket advertising-nya. Setiap kolom terdiri dari satu byte panjang, satu byte tipe, dan datanya: 0x01 adalah flag, 0x09 nama lengkap perangkat, dan 0x07 UUID layanan 128-bit. Karena paket advertising paling banyak memuat 31 byte, kolom UUID yang berukuran 18 byte kita taruh di scan response (resp_data). gatts_register_services() mengembalikan handle karakteristik-karakteristiknya; handle ini kita pakai untuk membaca nilai yang ditulis ponsel dan untuk mengirim notifikasi. Kalau kamu ingin kode yang lebih pendek, kamu bisa menginstal library aioble berbasis asyncio dengan mip. Untuk menjalankan kode ini di ESP32 dengan MicroPython, cukup ubah baris LED menjadi Pin(2, Pin.OUT) dan baca, misalnya, DHT11 sebagai ganti sensor suhu internal.

Wi-Fi dan Bluetooth bersamaan: memori, jangkauan, dan daya

Di ESP32, Wi-Fi dan Bluetooth berbagi radio 2,4 GHz yang sama. Chip menjalankan keduanya secara bergantian (time-sharing), jadi kamu bisa memakai keduanya bersamaan, tetapi masing-masing jadi sedikit lebih lambat, dan Bluetooth Classic yang digabung dengan lalu lintas Wi-Fi yang padat adalah skenario paling berat. Namun masalah sebenarnya biasanya ada di memori: stack Bluetooth berukuran besar, dan bersama Wi-Fi programnya bisa melebihi partisi aplikasi bawaan sebesar 1,2 MB (1.310.720 byte); bahkan contoh BLE di panduan ini saja sudah mengisi sekitar 80% ruang itu. Kalau kompilasi berakhir dengan “Sketch too big” atau “text section exceeds available space in board” dan kamu tidak memakai pembaruan lewat udara (OTA), pilih Huge APP (3MB No OTA/1MB SPIFFS) di Tools › Partition Scheme. Library NimBLE-Arduino, yang memakai lebih sedikit flash dan RAM, juga alternatif yang bagus.

Jangkauan bergantung pada desain antena board dan lingkungannya. Dengan ESP32 yang punya antena PCB di board-nya, harapkan 10–20 meter di dalam ruangan tergantung dindingnya, dan lebih jauh di ruang terbuka; modul dengan konektor antena eksternal (misalnya ESP32-WROOM-32U) menjangkau lebih jauh lagi. Menutup antena dengan kotak logam, baterai, atau kabel memangkas jangkauan secara drastis. Karena arus naik sebentar ketika radio menyala, kabel USB yang lemah bisa membuat board restart dengan pesan “Brownout detector was triggered”; solusinya ada di brownout dan Guru Meditation ESP32.

Ide proyek Bluetooth ESP32

  • Mobil robot Bluetooth: teruskan perintah F, B, L, dan R yang diterima lewat BluetoothSerial ke driver motor L298N; untuk bagian mekaniknya, lihat Mobil Robot Arduino.
  • Stopkontak pintar: ganti LED di contoh NUS dengan modul relay dan nyalakan lampu dari iPhone-mu.
  • Termometer nirkabel: tambahkan layar OLED ke contoh DHT22 supaya nilainya muncul di layar sekaligus di ponselmu.
  • Lampu suasana warna-warni: ubah warna LED strip WS2812B NeoPixel dengan nilai R, G, dan B yang dikirim dari ponselmu.
  • Aplikasi Android-mu sendiri: buat aplikasi remote dengan tombol dan slider di App Inventor dalam beberapa jam.
  • Komunikasi antara dua ESP32: untuk membuat dua board saling berbicara tanpa ponsel, coba ESP-NOW yang lebih sederhana dan lebih cepat sebagai ganti Bluetooth.

Masalah Bluetooth ESP32 yang sering muncul dan solusinya

Gejala / errorPenyebabSolusi
“Serial Bluetooth not available or not enabled” (Bluetooth serial tidak tersedia)Board yang terpilih adalah ESP32-S3, C3, C6, atau H2, yang tidak punya Bluetooth ClassicPakai ESP32 klasik atau beralih ke contoh BLE (NUS)
“conversion from 'String' to non-scalar type 'std::string' requested” (error konversi tipe)Kode yang ditulis untuk core 2.x (std::string) dikompilasi di core 3.xSimpan getValue().c_str() ke variabel String
“Sketch too big” (sketch terlalu besar)Bluetooth ditambah Wi-Fi melebihi partisi aplikasiPartition Scheme: pilih Huge APP atau pakai NimBLE-Arduino
iPhone tidak bisa menemukan ESP32Bluetooth Classic (SPP) tidak didukung di iOSPakai BLE dan sambungkan dari dalam aplikasi
Perangkat menghilang setelah koneksi pertamaAdvertising tidak dimulai lagi setelah koneksi terputusPanggil startAdvertising() setelah koneksi terputus
Tidak ada notifikasi yang masukNotifikasi belum dinyalakan di ponsel, atau BLE2902 tidak adaKetuk panah tiga di nRF Connect; tambahkan BLE2902 ke karakteristiknya
“read failed, socket might closed or timeout” (pembacaan gagal)Data pairing yang sudah usang, atau ponsel lain sudah terhubungLupakan perangkat di ponsel lalu lakukan pairing lagi; SPP hanya menerima satu koneksi dalam satu waktu
Bagian akhir pesan panjang terpotongMTU bawaan 23 byte, artinya 20 byte data per notifikasiPecah pesan menjadi potongan 20 byte atau izinkan MTU yang lebih besar dengan BLEDevice::setMTU(185)
Perubahan layanan tidak muncul di ponselPonsel menyimpan tabel GATT lama di cache-nyaMatikan lalu nyalakan Bluetooth ponsel; di Android, lupakan perangkatnya
Ketika menelusuri masalah koneksi, pilih Debug atau Verbose di Tools › Core Debug Level. Serial Monitor lalu menampilkan log koneksi, pemutusan, dan error dari stack Bluetooth, yang dengan cepat menunjukkan apakah masalahnya ada di ponsel atau di ESP32. Mengulangi tes yang sama di ponsel dengan nRF Connect juga memberi tahu apakah aplikasimu sendiri yang menjadi penyebabnya.

Dengan Bluetooth ESP32, mengubah ponselmu menjadi remote dan ESP32 menjadi sensor nirkabel atau perangkat pintar hanya butuh beberapa baris kode. BluetoothSerial adalah pilihan tepat untuk mulai cepat dengan Android, dan BLE untuk dukungan iPhone serta daya rendah. Kalau kamu ingin melanjutkan proyek ESP32 nirkabel, aplikasi BlueGrays punya proyek seperti LED, LED RGB, pengukuran suhu dengan DHT22, dan mobil kendali Bluetooth, masing-masing dengan skema rangkaian langkah demi langkah dan kode siap pakai.

Pertanyaan umum

Apakah ESP32 punya Bluetooth?

Ya. Chip ESP32 orisinal mendukung Bluetooth Classic (BR/EDR) dan BLE dengan Bluetooth 4.2, dan tidak butuh modul tambahan. ESP32-S3, C3, C6, dan H2 hanya mendukung BLE, sedangkan ESP32-S2 sama sekali tidak punya Bluetooth.

Berapa jangkauan Bluetooth ESP32?

Dengan ESP32 yang punya antena PCB di board-nya, biasanya kamu mendapat 10–20 meter di dalam ruangan; dinding, benda logam, dan arah antena memengaruhi jarak ini. Jangkauannya bertambah di ruang terbuka, dan modul dengan antena eksternal (misalnya WROOM-32U) menjangkau lebih jauh lagi.

Apakah ESP32 bisa terhubung ke iPhone lewat Bluetooth?

Bisa lewat BLE, tetapi tidak lewat Bluetooth Classic (BluetoothSerial), karena iOS hanya membuka profil port serial (SPP) untuk aksesori bersertifikat MFi. Di iPhone kamu bisa terhubung ke layanan BLE ESP32 dengan aplikasi seperti nRF Connect, LightBlue, atau Bluefruit Connect.

Apakah BluetoothSerial bekerja di ESP32-S3 dan ESP32-C3?

Tidak. Chip-chip ini tidak punya perangkat keras Bluetooth Classic, jadi BluetoothSerial tidak bisa dikompilasi. Untuk mendapatkan logika port serial yang sama lewat BLE, kamu bisa memakai Nordic UART Service (NUS); aplikasi seperti Serial Bluetooth Terminal mengenalinya.

Apakah ESP32 bisa memakai Wi-Fi dan Bluetooth bersamaan?

Bisa, keduanya bekerja bersama dengan bergantian memakai radio 2,4 GHz yang sama. Hanya saja kecepatannya sedikit turun dan programnya jadi lebih besar; kalau kamu mendapat error “Sketch too big”, pilih Huge APP di Partition Scheme atau pakai library NimBLE-Arduino yang lebih ringan.

Apakah aku butuh HC-05 dengan ESP32?

Biasanya tidak. Library BluetoothSerial di ESP32 klasik mengerjakan tugas HC-05 secara internal dan bekerja dengan aplikasi terminal yang sama. Kamu hanya butuh HC-05 di board tanpa Bluetooth (Arduino Uno, ESP8266, Pico standar, ESP32-S2) atau kalau sebuah proyek butuh Bluetooth Classic sementara kamu memakai ESP32 yang hanya punya BLE.

Lihat semua proyek
Kedipan LED Bawaan ESP32
#106Pemula

Kedipan LED Bawaan ESP32

Mengedipkan LED biru bawaan di ESP32 DevKit V1. Tidak perlu komponen eksternal! Proyek pertama yang sebaiknya kamu coba ketika membeli ESP32. GPIO 2 tersambung ke LED bawaan.

4.8(29)
53
LED Berkedip dengan ESP32
#103Pemula

LED Berkedip dengan ESP32

Mengajarkan penggunaan output GPIO dasar dengan ESP32. Kita membuat efek berkedip dengan menyalakan dan mematikan LED dari pin digital. Versi ESP32 dari proyek blink klasik di Arduino UNO; perintah blink dasar di lingkungan Arduino sama, tetapi susunan pin ESP32 dan level GPIO 3,3 V-nya berbeda dengan Arduino UNO.

4.7(37)
99
Kendali LED dengan Tombol Tekan ESP32 (Toggle)
#105Pemula

Kendali LED dengan Tombol Tekan ESP32 (Toggle)

Setiap kali tombol ditekan, keadaan LED berubah: jika menyala jadi mati, jika mati jadi menyala. Pola toggle klasik. Dengan debounce, kita membersihkan derau mekanis tombol tekan agar satu tekanan menghasilkan satu kali toggle.

4.6(39)
57
Kendalikan LED dari Ponselmu lewat Bluetooth
#34Pemula
Premium

Kendalikan LED dari Ponselmu lewat Bluetooth

Latihan kecil untuk menyalakan dan mematikan lampu kamarmu dari ponsel. Modul Bluetooth meneruskan huruf yang dikirim dari ponselmu ke Arduino; satu huruf menyalakan LED merah, huruf lain menyalakan L

4.8(68)
89
Mobil Kendali Bluetooth dengan L298N
#80Mahir
Premium

Mobil Kendali Bluetooth dengan L298N

Jadikan ponselmu remote dan kendarai mobil robotmu sendiri. Arduino menerima perintah dari aplikasi remote control di ponselmu lewat modul Bluetooth dan memutar roda dengan driver motor; mobil maju, m

5.0(38)
65
Kendali LED RGB ESP32
#111Pemula

Kendali LED RGB ESP32

Menghasilkan warna tanpa batas dengan satu LED RGB alih-alih 3 LED terpisah. Seperti di Arduino, kita membuat transisi pelangi dengan mengatur nilai R/G/B lewat analogWrite(). Teori warna (seperti merah+hijau=kuning) terlihat secara praktis.

4.6(29)
51
Suhu dan Kelembapan DHT22 ESP32 (dengan Layar LCD)
#117Menengah

Suhu dan Kelembapan DHT22 ESP32 (dengan Layar LCD)

Proyek ini mengukur suhu dan kelembapan sekitar dengan sensor DHT22 dan menampilkan datanya secara real-time di LCD I2C 16x2. Data dibaca dari sensor dengan protokol satu kabel lewat library Adafruit DHT. Proyek ini menjadi dasar untuk stasiun cuaca IoT, otomasi rumah pintar, atau sistem pemantauan lingkungan di rumah kaca. Komunikasi dengan DHT22 dilakukan lewat pin GPIO 4 ESP32.

4.8(38)
56
Kendali Strip NeoPixel WS2812B ESP32
#126Menengah
Premium

Kendali Strip NeoPixel WS2812B ESP32

LED addressable WS2812B (NeoPixel) adalah LED RGB pintar yang bisa dikendalikan secara berantai lewat satu pin data. Di proyek ini, kita mengendalikan strip WS2812B 8 LED dengan library Adafruit_NeoPixel untuk membuat animasi pelangi yang halus. Meskipun level logika 3,3V ESP32 bekerja lancar dengan sebagian besar komponen WS2812B, kita menunjukkan solusi dengan resistor seri 470Ω dan kapasitor 1000µF untuk berjaga-jaga jika tidak stabil. Animasinya dibuat dengan menggeser nilai hue.

4.9(19)
30