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?.

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.
| Fitur | Bluetooth Classic (SPP) | Bluetooth Low Energy (BLE) |
|---|---|---|
| Library Arduino | BluetoothSerial (sudah ada di paket ESP32) | BLEDevice (sudah ada di paket ESP32) atau NimBLE-Arduino |
| Model data | Aliran byte terus-menerus, seperti port serial | Tabel GATT yang terdiri dari layanan dan karakteristik |
| Jumlah data | Tinggi, cocok untuk aliran terus-menerus | Paket kecil; bawaannya 20 byte per notifikasi |
| Konsumsi daya | Terus tinggi selama terhubung | Sangat rendah; bisa berjalan berbulan-bulan dengan baterai |
| Pairing | Dilakukan di pengaturan Bluetooth ponsel | Tidak perlu; koneksi dibuat di dalam aplikasi |
| Android | Didukung | Didukung |
| iPhone (iOS) | Tidak didukung | Didukung |
| ESP32 yang mendukung | Hanya ESP32 orisinal (WROOM-32, WROVER) | ESP32, S3, C3, C6, dan H2 (bukan S2) |
| Penggunaan umum | Terminal serial, mobil robot, pengganti HC-05 | Sensor, 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 / chip | Bluetooth Classic | BLE | Catatan |
|---|---|---|---|
| ESP32 (WROOM-32, WROVER, DevKit V1) | Ya | Ya (4.2) | Satu-satunya keluarga ESP32 yang bisa menjalankan BluetoothSerial |
| ESP32-S2 | Tidak | Tidak | Hanya Wi-Fi |
| ESP32-S3 | Tidak | Ya (Bluetooth 5) | BluetoothSerial tidak bisa dikompilasi, pakai BLE |
| ESP32-C3 | Tidak | Ya (Bluetooth 5) | Board BLE kecil dan murah |
| ESP32-C6 | Tidak | Ya (Bluetooth 5) | Juga punya Wi-Fi 6, Zigbee, dan Thread |
| ESP32-H2 | Tidak | Ya (Bluetooth 5) | Tanpa Wi-Fi; fokus pada Zigbee dan Thread |
| ESP8266 (NodeMCU, Wemos D1) | Tidak | Tidak | Butuh HC-05 atau HM-10 untuk Bluetooth |
| Raspberry Pi Pico W / Pico 2 W | Ada di perangkat keras | Ya | MicroPython hanya mendukung BLE |
| Raspberry Pi Pico / Pico 2 | Tidak | Tidak | Butuh modul Bluetooth eksternal |
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:
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
- Setelah meng-upload, buka Serial Monitor di 115200 baud dan cari pesan “Eslesmeye hazir / Ready to pair”.
- 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.
- Instal aplikasi Serial Bluetooth Terminal buatan Kai Morich dari Google Play.
- Di aplikasi, buka Devices dari menu samping dan pilih BlueGrays-ESP32 di tab Bluetooth Classic.
- Ketuk ikon colokan di bilah atas; begitu “Connected” muncul di layar, kamu sudah terhubung.
- 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.
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.
| Istilah | Arti | Contoh di panduan ini |
|---|---|---|
| Advertising (siaran) | Paket pendek tempat perangkat mengumumkan nama dan layanannya | Advertising dengan nama “BlueGrays-BLE” |
| Layanan (service) | Grup yang menyatukan karakteristik-karakteristik yang saling berkaitan | Layanan Nordic UART, Environmental Sensing (0x181A) |
| Karakteristik | Satu nilai yang dibaca atau ditulis | Perintah LED, suhu (0x2A6E) |
| READ | Ponsel membaca nilainya kapan pun ia mau | Suhu yang terakhir diukur |
| WRITE / WRITE_NR | Ponsel menulis nilai (dengan atau tanpa respons) | Menulis “1” menyalakan LED |
| NOTIFY | ESP32 memberi tahu ponsel dengan sendirinya ketika nilainya berubah | Suhu baru setiap 2 detik |
| Deskriptor 0x2902 (CCCD) | Sakelar yang menyalakan dan mematikan notifikasi | Tombol panah tiga di nRF Connect |
| UUID | Nomor identitas layanan dan karakteristik | 6E400001-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 / karakteristik | UUID | Format data |
|---|---|---|
| Battery Service / Battery Level | 0x180F / 0x2A19 | 1 byte, persen (0–100) |
| Environmental Sensing / Temperature | 0x181A / 0x2A6E | Bilangan bulat bertanda 2 byte, 0,01 °C |
| Environmental Sensing / Humidity | 0x181A / 0x2A6F | Bilangan bulat tak bertanda 2 byte, 0,01% |
| Heart Rate / Heart Rate Measurement | 0x180D / 0x2A37 | Byte flag + nilai detak jantung |
| Nordic UART Service (NUS) | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E | RX 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.
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.
Menguji BLE dengan nRF Connect langkah demi langkah
- Instal aplikasi gratis nRF Connect for Mobile dari Nordic Semiconductor di ponselmu; aplikasinya tersedia untuk Android dan iPhone.
- 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.
- 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.
- Ketuk CONNECT. Layar yang terbuka menampilkan Nordic UART Service; kalau kamu memakai UUID buatanmu sendiri, di sini akan tertulis “Unknown Service”.
- Ketuk ikon panah tiga ke bawah di sebelah karakteristik TX (6E400003) untuk menyalakan notifikasi.
- 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)

| Pin DHT22 | ESP32 | Catatan |
|---|---|---|
| VCC (+) | 3V3 | Bekerja dari 3,3 sampai 5 V |
| DATA (OUT) | GPIO 4 | Pada sensor tanpa modul, tambahkan resistor 10 kΩ antara DATA dan 3V3 |
| GND (−) | GND | Ground 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.
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.
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

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.
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 / error | Penyebab | Solusi |
|---|---|---|
| “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 Classic | Pakai 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.x | Simpan getValue().c_str() ke variabel String |
| “Sketch too big” (sketch terlalu besar) | Bluetooth ditambah Wi-Fi melebihi partisi aplikasi | Partition Scheme: pilih Huge APP atau pakai NimBLE-Arduino |
| iPhone tidak bisa menemukan ESP32 | Bluetooth Classic (SPP) tidak didukung di iOS | Pakai BLE dan sambungkan dari dalam aplikasi |
| Perangkat menghilang setelah koneksi pertama | Advertising tidak dimulai lagi setelah koneksi terputus | Panggil startAdvertising() setelah koneksi terputus |
| Tidak ada notifikasi yang masuk | Notifikasi belum dinyalakan di ponsel, atau BLE2902 tidak ada | Ketuk 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 terhubung | Lupakan perangkat di ponsel lalu lakukan pairing lagi; SPP hanya menerima satu koneksi dalam satu waktu |
| Bagian akhir pesan panjang terpotong | MTU bawaan 23 byte, artinya 20 byte data per notifikasi | Pecah pesan menjadi potongan 20 byte atau izinkan MTU yang lebih besar dengan BLEDevice::setMTU(185) |
| Perubahan layanan tidak muncul di ponsel | Ponsel menyimpan tabel GATT lama di cache-nya | Matikan lalu nyalakan Bluetooth ponsel; di Android, lupakan perangkatnya |
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.
Proyek untuk dibuat setelah membaca ini
Lihat semua proyek
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.

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.

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.

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

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

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.

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.

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.
Artikel lainnya
Aplikasi BlueGrays
Rakit rangkaian ini langkah demi langkah di ponselmu
Skema pengkabelan, kode, dan daftar komponen dalam satu layar. 200+ proyek Arduino, ESP32, dan Pico siap dibuat.
Pindai dengan ponselmu
Arahkan kamera ke kode ini dan toko aplikasi di ponselmu akan terbuka.
atau buka toko aplikasi

