ESP32 Bluetooth Kullanımı: Classic ve BLE ile Telefondan Kontrol
ESP32 Bluetooth rehberi: BluetoothSerial ile telefondan LED kontrolü, BLE sunucu, servis ve UUID, nRF Connect testi, sensör verisi, iPhone uyumu ve sık hatalar.
ESP32 Bluetooth, kartın içinde hazır gelen kablosuz bağlantıyı kullanarak ESP32'yi hiçbir ek modül takmadan telefona, tablete ya da bilgisayara bağlamanı sağlar. Klasik ESP32 iki farklı Bluetooth türünü birlikte destekler: seri port gibi çalışan Bluetooth Classic ve çok az enerji harcayan Bluetooth Low Energy (BLE). Arduino Uno'da telefonla konuşmak için ayrı bir HC-05 modülü bağlaman gerekirken ESP32'de birkaç satır kod yeterlidir. Bu rehberde iki yöntemi de adım adım kuracak, telefondan LED yakacak, kendi BLE servisini yazacak ve sensör verisini telefona göndereceksin.
Hangi yöntemi seçeceğin telefonuna ve kartına bağlıdır. Android telefonun ve klasik bir ESP32 DevKit kartın varsa en hızlı başlangıç BluetoothSerial kütüphanesidir. iPhone kullanıyorsan ya da kartın ESP32-S3 veya ESP32-C3 ise klasik Bluetooth işine yaramaz; BLE kullanman gerekir. Yazının sonunda aynı BLE projesini Raspberry Pi Pico W ve MicroPython ile de bulacaksın. ESP32'yi ilk kez kullanıyorsan önce ESP32 Nedir? yazısındaki Arduino IDE kurulumunu tamamla.

ESP32 Bluetooth nedir? Classic ve BLE farkı
Bluetooth Classic, kablosuz kulaklıklarda ve HC-05 gibi modüllerde kullanılan eski ve tanıdık türdür. ESP32 bunu SPP (Serial Port Profile) ile kullanır: telefon ile kart arasında görünmez bir seri kablo varmış gibi baytlar sırayla akar. Cihazı önce telefonun Bluetooth ayarlarından eşleştirirsin, sonra bir terminal uygulamasıyla bağlanırsın. BLE ise 2010'da Bluetooth 4.0 ile gelen ve pille aylarca çalışan cihazlar için tasarlanmış ayrı bir sistemdir. BLE'de sürekli akan bir veri hattı yoktur; ESP32 içinde değerlerin durduğu küçük bir tablo (GATT) tutar, telefon bu tablodaki değerleri okur, yazar ya da değiştiklerinde haber almak için abone olur. İsimleri benzese de iki tür birbiriyle konuşamaz: BLE için yazılmış bir uygulama klasik Bluetooth cihazına bağlanamaz.
| Özellik | Bluetooth Classic (SPP) | Bluetooth Low Energy (BLE) |
|---|---|---|
| Arduino kütüphanesi | BluetoothSerial (ESP32 paketinde hazır) | BLEDevice (ESP32 paketinde hazır) ya da NimBLE-Arduino |
| Veri modeli | Seri port gibi kesintisiz bayt akışı | Servis ve karakteristiklerden oluşan GATT tablosu |
| Veri miktarı | Yüksek, sürekli akışa uygun | Küçük paketler; varsayılan olarak bildirim başına 20 bayt |
| Enerji tüketimi | Bağlıyken sürekli yüksek | Çok düşük; pille aylarca çalışabilir |
| Eşleştirme | Telefonun Bluetooth ayarlarından yapılır | Gerekmez; bağlantı uygulamanın içinden kurulur |
| Android | Desteklenir | Desteklenir |
| iPhone (iOS) | Desteklenmez | Desteklenir |
| Destekleyen ESP32'ler | Yalnızca ilk ESP32 (WROOM-32, WROVER) | ESP32, S3, C3, C6 ve H2 (S2 hariç) |
| Tipik kullanım | Seri terminal, robot araba, HC-05 yerine | Sensörler, giyilebilir cihazlar, akıllı ev, işaretçiler (beacon) |
Pratik kural şöyle: Android telefondan yazı gönderip almak ve hızlıca sonuç görmek istiyorsan Classic; iPhone desteği, düşük enerji ya da yeni nesil ESP32 kartları söz konusuysa BLE. Yeni bir projeye başlıyorsan BLE'yi öğrenmeye değer, çünkü her iki telefon türünde çalışır ve Espressif'in Bluetooth'lu yeni yongalarının hepsinde bulunur. Daha önce HC-05 ile çalıştıysan HC-05 Bluetooth Modülü yazısındaki komut mantığının BluetoothSerial'da birebir aynı olduğunu göreceksin.
Hangi ESP32 kartında hangi Bluetooth var?
ESP32 ailesindeki her yonga aynı Bluetooth donanımına sahip değildir. En sık yapılan hata, internette bulunan bir BluetoothSerial örneğini ESP32-S3 ya da ESP32-C3 kartına yüklemeye çalışmaktır. Espressif klasik Bluetooth'u yalnızca ilk ESP32 yongasına koymuştur; sonraki yongaların Bluetooth'u olanlar yalnızca BLE destekler. Kartının hangi yongayı taşıdığını modülün metal kapağındaki yazıdan (örneğin ESP32-WROOM-32 ya da ESP32-S3-WROOM-1) öğrenebilirsin.
| Kart / yonga | Bluetooth Classic | BLE | Not |
|---|---|---|---|
| ESP32 (WROOM-32, WROVER, DevKit V1) | Var | Var (4.2) | BluetoothSerial çalışan tek ESP32 ailesi |
| ESP32-S2 | Yok | Yok | Yalnızca Wi-Fi |
| ESP32-S3 | Yok | Var (Bluetooth 5) | BluetoothSerial derlenmez, BLE kullan |
| ESP32-C3 | Yok | Var (Bluetooth 5) | Küçük ve ucuz BLE kartları |
| ESP32-C6 | Yok | Var (Bluetooth 5) | Wi-Fi 6, Zigbee ve Thread de var |
| ESP32-H2 | Yok | Var (Bluetooth 5) | Wi-Fi yok; Zigbee ve Thread odaklı |
| ESP8266 (NodeMCU, Wemos D1) | Yok | Yok | Bluetooth için HC-05 ya da HM-10 gerekir |
| Raspberry Pi Pico W / Pico 2 W | Donanımda var | Var | MicroPython yalnızca BLE destekler |
| Raspberry Pi Pico / Pico 2 | Yok | Yok | Harici Bluetooth modülü gerekir |
Bluetooth Classic: BluetoothSerial ile telefondan LED kontrolü
BluetoothSerial kütüphanesi ESP32'yi telefona bir HC-05 modülü gibi tanıtır ve Serial nesnesiyle aynı komutları kullanır: available(), read(), print() ve println(). Kütüphane ESP32 kart paketiyle birlikte gelir, ayrıca kurman gerekmez. Devre için çoğu DevKit kartında GPIO 2'ye bağlı mavi dahili LED yeterlidir; harici bir LED kullanacaksan onu GPIO 2'ye 220 Ω'luk bir dirençle bağla. Direnç değerini LED direnci hesaplayıcı ile kontrol edebilir, ESP32'nin diğer güvenli pinlerini Pinout aracında görebilirsin. Araçlar > Kart menüsünde ESP32 Dev Module seçili olduğundan emin ol ve şu kodu yükle:
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}Kod, telefondan gelen her satırı bir komut olarak okur. readStringUntil() satır sonu karakteri gelene kadar bekler, trim() ise telefonun eklediği satır başı (CR) karakterini ve boşlukları temizler; bu satırı unutursan "1" gönderdiğinde kart aslında "1" ve görünmez bir CR karakteri alır ve hiçbir komut eşleşmez. Komutlar küçük harfe çevrildiği için "AC" ile "ac" aynı sonucu verir. En alttaki iki satır Seri Monitör'e yazdığın her şeyi telefona aktarır; böylece bilgisayar ile telefon arasında kablosuz bir sohbet kurabilirsin. Kodun başındaki #error kontrolleri ise yanlış kart seçildiğinde derlemenin anlaşılır bir mesajla durmasını sağlar.
Serial Bluetooth Terminal ile telefondan bağlanmak
- Kodu yükledikten sonra Seri Monitör'ü 115200 baud hızında aç ve "Eslesmeye hazir" yazısını gör.
- Android telefonunda Ayarlar > Bluetooth menüsünde yeni cihazları tara ve BlueGrays-ESP32 adını seçerek eşleştir. ESP32 varsayılan olarak PIN sormadan eşleşir; telefon bir onay kodu gösterirse onayla.
- Google Play'den Kai Morich'in Serial Bluetooth Terminal uygulamasını kur.
- Uygulamada sol menüden Devices bölümüne gir ve Bluetooth Classic sekmesinde BlueGrays-ESP32'yi seç.
- Üst çubuktaki fiş simgesine dokun; ekranda "Connected" yazısı çıkınca bağlantı tamamdır.
- Alttaki kutuya 1 yazıp gönder: LED yanar ve ekranda "LED acik" cevabı görünür. 0 ile LED söner, durum ile son hâlini öğrenirsin.
BLE nasıl çalışır? Servis, karakteristik ve UUID
BLE'de iki rol vardır. ESP32 sunucu (peripheral) rolündedir: değerleri saklar ve en fazla 31 baytlık küçük yayın (advertising) paketlerini kısa aralıklarla (ESP32'de varsayılan olarak 20–40 ms'de bir) göndererek çevresine "buradayım" der. Telefon ise istemci (central) rolündedir: ortamı tarar, cihazı bulur ve bağlanır. Bağlantıdan sonra telefon ESP32'nin GATT (Generic Attribute Profile) tablosunu okur. Bu tabloyu bir dosya dolabı gibi düşünebilirsin: her çekmece bir servis, çekmecedeki her dosya bir karakteristiktir. Karakteristik tek bir değeri (örneğin sıcaklığı ya da LED komutunu) ve bu değerle neler yapılabileceğini belirten özellikleri taşır.
| Terim | Anlamı | Bu yazıdaki örnek |
|---|---|---|
| Yayın (advertising) | Cihazın adını ve servislerini duyurduğu kısa paket | "BlueGrays-BLE" adıyla yayın |
| Servis | Birbiriyle ilgili karakteristikleri bir arada tutan grup | Nordic UART servisi, Environmental Sensing (0x181A) |
| Karakteristik | Okunan ya da yazılan tek bir değer | LED komutu, sıcaklık (0x2A6E) |
| READ | Telefon değeri istediği an okur | Son ölçülen sıcaklık |
| WRITE / WRITE_NR | Telefon değer yazar (cevaplı ya da cevapsız) | "1" yazınca LED yanar |
| NOTIFY | ESP32 değer değişince telefona kendisi haber verir | 2 saniyede bir yeni sıcaklık |
| Tanımlayıcı 0x2902 (CCCD) | Bildirimleri açıp kapatan anahtar | nRF Connect'teki üçlü ok düğmesi |
| UUID | Servis ve karakteristiklerin kimlik numarası | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E |
Her servis ve karakteristik bir UUID ile tanınır. Bluetooth SIG tarafından tanımlanmış standart özellikler kısa, 16 bitlik numaralar kullanır: pil seviyesi servisi 0x180F, sıcaklık karakteristiği 0x2A6E gibi. Standart bir karşılığı olmayan kendi verilerin için 128 bitlik rastgele bir UUID üretmelisin; bunun için Python'da import uuid; print(uuid.uuid4()) yazman yeterlidir. Kendi UUID'ini seçerken xxxxxxxx-0000-1000-8000-00805F9B34FB kalıbına uyan bir değer kullanma, çünkü bu kalıp Bluetooth SIG'in 16 bitlik numaralarına ayrılmıştır. Hazır uygulamalarla sorunsuz çalışmak istiyorsan herkesin tanıdığı UUID'leri kullanmak daha da pratiktir:
| Servis / karakteristik | UUID | Veri biçimi |
|---|---|---|
| Battery Service / Battery Level | 0x180F / 0x2A19 | 1 bayt, yüzde (0–100) |
| Environmental Sensing / Temperature | 0x181A / 0x2A6E | 2 bayt işaretli tam sayı, 0,01 °C |
| Environmental Sensing / Humidity | 0x181A / 0x2A6F | 2 bayt işaretsiz tam sayı, %0,01 |
| Heart Rate / Heart Rate Measurement | 0x180D / 0x2A37 | Bayrak baytı + nabız değeri |
| Nordic UART Service (NUS) | 6E400001-B5A3-F393-E0A9-E50E24DCCA9E | RX 6E400002 (yazma), TX 6E400003 (bildirim) |
ESP32 BLE sunucu: telefondan LED kontrolü (Nordic UART)
İlk BLE projemizde ESP32'yi bir Nordic UART Service (NUS) sunucusuna çeviriyoruz. NUS, Nordic Semiconductor'ın tanımladığı ama fiilen standart hâline gelmiş bir "BLE üzerinden seri port" servisidir: telefon RX karakteristiğine yazar, ESP32 de TX karakteristiğinden bildirim gönderir. Avantajı, pek çok hazır uygulamanın onu tanımasıdır; nRF Connect, Serial Bluetooth Terminal'in Bluetooth LE sekmesi ve iPhone'daki Bluefruit Connect aynı kodla çalışır. Yani bu bölümdeki kod hem Android hem iPhone ile, üstelik ESP32-S3 ve C3 gibi yalnızca BLE destekleyen kartlarda da çalışır. BLE kütüphanesi ESP32 kart paketinin içindedir, ayrıca bir şey kurman gerekmez.
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}Kodun en önemli kısmı geri çağırma (callback) sınıflarıdır. onConnect ve onDisconnect bağlantı durumunu, onWrite ise telefonun RX karakteristiğine yazdığı değeri yakalar. Bu fonksiyonlar Bluetooth yığınının kendi görevinde çalıştığı için içlerinde uzun işler yapmak yerine yalnızca bir değişkeni değiştiriyoruz; asıl işi loop() yapıyor. TX karakteristiğine eklenen BLE2902 tanımlayıcısı, telefonun bildirimleri açabilmesi için gereklidir. Bağlantı koptuğunda ESP32 yayını kendiliğinden yeniden başlatmaz; loop() içindeki startAdvertising() satırı olmasa cihaz ilk bağlantıdan sonra bir daha bulunamaz. setMinPreferred ve setMaxPreferred satırları ise iPhone'larla bağlantıyı kolaylaştıran tercih edilen bağlantı aralığı bilgisini yayına ekler.
nRF Connect ile BLE testi adım adım
- Telefonuna Nordic Semiconductor'ın ücretsiz nRF Connect for Mobile uygulamasını kur; Android ve iPhone sürümü vardır.
- Android'de uygulama Bluetooth ve Yakındaki cihazlar izni ister (Android 11 ve öncesinde konum izni ve açık konum servisi); izin vermezsen tarama listesi boş kalır.
- Scanner sekmesinde SCAN düğmesine bas ve listede BlueGrays-BLE adını bul. Yanındaki RSSI değeri sinyal gücüdür: -50 dBm çok yakın, -90 dBm menzilin sınırı demektir.
- CONNECT düğmesine dokun. Açılan ekranda Nordic UART Service görünür; kendi ürettiğin UUID'leri kullansaydın burada "Unknown Service" yazardı.
- TX karakteristiğinin (6E400003) yanındaki üçlü aşağı ok simgesine dokunarak bildirimleri aç.
- RX karakteristiğinin (6E400002) yanındaki yukarı ok simgesine dokun, veri türü olarak Text (UTF-8) seç, 1 yaz ve gönder. LED yanar ve TX satırında "LED acik" yazısı belirir.
Bu test, telefon uygulaması yazmadan önce ESP32 tarafının doğru çalıştığını kanıtlamanın en hızlı yoludur. Bir sorun yaşarsan önce nRF Connect ile dene: cihaz listede yoksa sorun yayındadır, bağlanıyor ama servis görünmüyorsa servisin start() çağrısı unutulmuştur, yazdığın hâlde LED yanmıyorsa onWrite içindeki karşılaştırmaya bakmalısın. Aynı kodu Serial Bluetooth Terminal'in Bluetooth LE sekmesinden de deneyebilirsin; uygulama NUS servisini tanır ve klasik Bluetooth'taki gibi yazışırsın. Kendi Android uygulamanı yapmak istersen App Inventor ile Arduino Bluetooth yazısına göz at.
BLE ile sensör verisi yayınlamak (DHT22)

| DHT22 pini | ESP32 | Not |
|---|---|---|
| VCC (+) | 3V3 | 3,3–5 V arasında çalışır |
| DATA (OUT) | GPIO 4 | Çıplak sensörde DATA ile 3V3 arasına 10 kΩ direnç ekle |
| GND (−) | GND | Ortak toprak |
Bu örnekte standart Environmental Sensing servisini (0x181A) ve onun sıcaklık (0x2A6E) ile nem (0x2A6F) karakteristiklerini kullanıyoruz. Standart UUID'lerin güzelliği, nRF Connect gibi uygulamaların değeri tanıyıp doğrudan "23.45 °C" biçiminde gösterebilmesidir. Standart, değerlerin metin olarak değil, 100 ile çarpılmış tam sayı olarak gönderilmesini ister: 23,45 °C sıcaklık 2345 sayısına dönüşür ve 2 bayt hâlinde, düşük bayt önce (little-endian) gönderilir. Sensörü okumak için DHT11 yazısında anlattığımız Adafruit DHT sensor library kullanılıyor; Kütüphane Yöneticisi'nden kurabilirsin.
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}Kod her iki saniyede bir ölçüm yapar, çünkü DHT22 daha sık okunamaz. Değerler önce setValue() ile karakteristiğe yazılır; böylece telefon bildirim beklemeden okuma (READ) yaptığında da son ölçümü alır. Telefon bağlıysa notify() yeni değeri anında gönderir. nRF Connect değeri çözemezse ham bayt olarak görürsün: 23,45 °C için ekranda (0x) 29-09 yazar. Baytları ters çevirirsen 0x0929 elde edersin; bu da onluk sistemde 2345'tir ve 100'e bölünce sıcaklığı verir. Kendi uygulamanı yazarken aynı hesabı yapman gerekir. Değeri metin olarak göndermek daha kolay görünse de standart biçim hem daha az bayt harcar hem de hazır uygulamalarla uyumludur.
iPhone (iOS) ile ESP32 Bluetooth
iPhone kullananların en sık sorduğu soru, ESP32'nin neden Bluetooth ayarlarında görünmediğidir. Apple, klasik Bluetooth'un seri port profilini (SPP) yalnızca MFi sertifikalı aksesuarlara açar; bu yüzden BluetoothSerial ile çalışan bir ESP32'ye iPhone'dan bağlanamazsın. Çözüm BLE'dir ve yukarıdaki NUS ile sensör örnekleri iPhone'da sorunsuz çalışır. Dikkat etmen gereken birkaç nokta var:
- Ayarlarda arama: BLE cihazları iOS'un Bluetooth ayarlarında listelenmez; bağlantıyı nRF Connect, LightBlue ya da Bluefruit Connect gibi bir uygulamanın içinden kur.
- Önbellek: iOS cihazın adını ve servis listesini hatırlar. Koddaki servisleri değiştirdikten sonra hâlâ eski liste görünüyorsa telefonun Bluetooth'unu kapatıp aç.
- MAC adresi yok: iOS uygulamalara cihazın MAC adresini göstermez, her cihaza kendi ürettiği bir kimlik verir; birden fazla ESP32 kullanıyorsan her birine farklı bir ad ver.
- Bağlantı aralığı: setMinPreferred(0x06) ve setMaxPreferred(0x12) satırlarını silme; Espressif'in örneklerinde de bu satırlar iPhone bağlantı sorunlarına karşı önerilir.
- Kendi uygulaman: iOS'ta Swift ile Core Bluetooth kullanırsın; eşleştirme gerekmez, uygulama doğrudan servis UUID'sine göre tarama yapar.
Raspberry Pi Pico W ve MicroPython ile BLE

Raspberry Pi Pico ailesinde Bluetooth yalnızca Pico W ve Pico 2 W kartlarında vardır. Kart üzerindeki CYW43439 yongası donanım olarak hem klasik Bluetooth'u hem BLE'yi destekler, ancak MicroPython yalnızca BLE'yi kullanır (1.20 sürümünden itibaren). Aşağıdaki kod ESP32'deki NUS örneğinin MicroPython karşılığıdır; aynı UUID'leri kullandığı için nRF Connect ve Serial Bluetooth Terminal ile aynı şekilde test edilir. Karta Pico W için hazırlanmış MicroPython sürümünü yüklemiş olmalısın. Kod ayrıca kartın dahili sıcaklık sensörünü okuyup bağlı telefona 2 saniyede bir gönderir.
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)MicroPython'daki bluetooth modülü düşük seviyeli olduğu için yayın paketini kendimiz oluşturuyoruz. Her alan bir uzunluk baytı, bir tür baytı ve veriden oluşur: 0x01 bayraklar, 0x09 cihazın tam adı, 0x07 ise 128 bitlik servis UUID'sidir. Yayın paketi en fazla 31 bayt alabildiği için 18 baytlık UUID alanını tarama yanıtına (resp_data) koyduk. gatts_register_services() karakteristiklerin tutamaklarını (handle) döndürür; telefonun yazdığı değeri bu tutamakla okur, bildirimi de yine bu tutamakla göndeririz. Daha kısa kod yazmak istersen asyncio tabanlı aioble kütüphanesini mip ile kurabilirsin. Kodu ESP32'deki MicroPython'da çalıştırmak için LED satırını Pin(2, Pin.OUT) yapman ve dahili sıcaklık sensörü yerine örneğin bir DHT11 okuman yeterlidir.
Wi-Fi ve Bluetooth birlikte: bellek, menzil ve güç
ESP32'de Wi-Fi ve Bluetooth aynı 2,4 GHz radyoyu paylaşır. Yonga ikisini zaman paylaşımıyla çalıştırır; yani ikisi aynı anda kullanılabilir ama her birinin hızı biraz düşer, klasik Bluetooth ile yoğun Wi-Fi trafiği bir arada en zorlu senaryodur. Asıl sorun ise genellikle bellektir: Bluetooth yığını büyüktür ve Wi-Fi ile birleşince program varsayılan 1,2 MB'lık (1.310.720 bayt) uygulama alanını aşabilir; bu yazıdaki BLE örneği bile tek başına bu alanın yaklaşık %80'ini doldurur. Derleme "Sketch too big" ya da "text section exceeds available space in board" hatasıyla biterse ve kablosuz güncelleme (OTA) kullanmıyorsan Araçlar > Partition Scheme menüsünden Huge APP (3MB No OTA/1MB SPIFFS) seçeneğini seç. Daha az flaş ve RAM harcayan NimBLE-Arduino kütüphanesi de iyi bir alternatiftir.
Menzil, kartın anten tasarımına ve ortama bağlıdır. Kart üzerinde PCB anteni olan bir ESP32 ile ev içinde duvarlara bağlı olarak 10–20 metre beklenir, açık alanda mesafe artar; harici anten konnektörlü modüllerle (örneğin ESP32-WROOM-32U) daha da uzağa ulaşılır. Antenin üstünü metal bir kutu, pil ya da kablolarla kapatmak menzili ciddi biçimde düşürür. Radyo açılırken akım kısa süreliğine yükseldiği için zayıf bir USB kablosu kartın "Brownout detector was triggered" mesajıyla yeniden başlamasına yol açabilir; çözümleri ESP32 Brownout ve Guru Meditation yazısında bulabilirsin.
ESP32 Bluetooth proje fikirleri
- Bluetooth robot araba: BluetoothSerial ile gelen F, B, L ve R komutlarını L298N motor sürücüsüne aktar; mekanik kısım için Arduino Robot Araba Yapımı yazısına bak.
- Akıllı priz: NUS örneğindeki LED'in yerine bir röle modülü bağla ve lambayı iPhone'dan aç.
- Kablosuz termometre: DHT22 örneğine bir OLED ekran ekle; değer hem ekranda hem telefonda görünsün.
- Renkli ortam ışığı: Telefondan gönderilen R, G ve B değerleriyle WS2812B NeoPixel şeridin rengini değiştir.
- Kendi Android uygulaman: Düğmeli ve kaydırıcılı bir kumanda uygulamasını App Inventor ile birkaç saatte hazırla.
- İki ESP32 arasında haberleşme: Telefon olmadan iki kartı konuşturmak için Bluetooth yerine daha basit ve hızlı olan ESP-NOW yöntemini dene.
ESP32 Bluetooth sık hataları ve çözümleri
| Belirti / hata | Nedeni | Çözüm |
|---|---|---|
| "Serial Bluetooth not available or not enabled" | Seçili kart ESP32-S3, C3, C6 ya da H2; klasik Bluetooth yok | Klasik ESP32 kullan ya da BLE (NUS) örneğine geç |
| "conversion from 'String' to non-scalar type 'std::string' requested" | Çekirdek 2.x için yazılmış (std::string kullanan) kod 3.x çekirdekte derleniyor | Sonucu getValue().c_str() ile String türünde bir değişkene ata |
| "Sketch too big" | Bluetooth ve Wi-Fi birlikte uygulama alanını aştı | Partition Scheme: Huge APP seç ya da NimBLE-Arduino kullan |
| iPhone ESP32'yi bulamıyor | Klasik Bluetooth (SPP) iOS'ta desteklenmez | BLE kullan ve bağlantıyı bir uygulamanın içinden kur |
| İlk bağlantıdan sonra cihaz bir daha görünmüyor | Bağlantı kopunca yayın yeniden başlatılmadı | Kopmadan sonra startAdvertising() çağır |
| Bildirim gelmiyor | Telefonda bildirim açılmadı ya da BLE2902 eklenmedi | nRF Connect'te üçlü oka bas, karakteristiğe BLE2902 ekle |
| "read failed, socket might closed or timeout" | Eski eşleştirme bilgisi ya da başka bir telefon zaten bağlı | Cihazı telefonda unutup yeniden eşleştir; SPP aynı anda tek bağlantı kabul eder |
| Uzun mesajın sonu kesiliyor | Varsayılan MTU 23 bayt, yani bildirim başına 20 bayt veri | Mesajı 20 baytlık parçalara böl ya da BLEDevice::setMTU(185) ile daha büyük MTU'ya izin ver |
| Servis değişikliği telefonda görünmüyor | Telefon eski GATT tablosunu önbellekte tutuyor | Telefonun Bluetooth'unu kapatıp aç; Android'de cihazı unut |
ESP32 Bluetooth sayesinde telefonu bir kumandaya, ESP32'yi de kablosuz bir sensöre ya da akıllı bir cihaza çevirmek birkaç satırlık bir iş. Android ile hızlı bir başlangıç için BluetoothSerial, iPhone desteği ve düşük enerji için BLE en doğru seçimdir. ESP32 ile kablosuz projelere devam etmek istersen BlueGrays uygulamasında LED, RGB LED, DHT22 ile sıcaklık ölçümü ve Bluetooth kontrollü araba gibi projeleri adım adım devre şeması ve hazır koduyla bulabilirsin.
Sıkça sorulan sorular
ESP32'de Bluetooth var mı?
Evet. Klasik ESP32 yongası Bluetooth 4.2 ile hem klasik Bluetooth'u (BR/EDR) hem de BLE'yi destekler ve ek modül gerektirmez. ESP32-S3, C3, C6 ve H2 yalnızca BLE destekler; ESP32-S2'de ise Bluetooth hiç yoktur.
ESP32 Bluetooth menzili ne kadar?
Kart üzerinde PCB anteni olan bir ESP32 ile ev içinde genellikle 10–20 metre menzil elde edilir; duvarlar, metal eşyalar ve antenin yönü bu mesafeyi etkiler. Açık alanda menzil artar, harici antenli modüller (örneğin WROOM-32U) daha da uzun mesafe sağlar.
ESP32 iPhone'a Bluetooth ile bağlanır mı?
BLE ile bağlanır, klasik Bluetooth (BluetoothSerial) ile bağlanmaz; çünkü iOS seri port profilini (SPP) yalnızca MFi sertifikalı aksesuarlara açar. iPhone'da nRF Connect, LightBlue ya da Bluefruit Connect gibi bir uygulamayla ESP32'nin BLE servisine bağlanabilirsin.
ESP32-S3 ve ESP32-C3'te BluetoothSerial çalışır mı?
Hayır. Bu yongalarda klasik Bluetooth donanımı olmadığı için BluetoothSerial derlenmez. Aynı seri port mantığını BLE üzerinde kurmak için Nordic UART Service (NUS) kullanabilirsin; Serial Bluetooth Terminal gibi uygulamalar bu servisi tanır.
ESP32'de Wi-Fi ve Bluetooth aynı anda çalışır mı?
Evet, ikisi aynı 2,4 GHz radyoyu zaman paylaşımıyla kullanarak birlikte çalışır. Ancak hız biraz düşer ve program boyutu büyür; "Sketch too big" hatası alırsan Partition Scheme menüsünden Huge APP seçeneğini seç ya da daha hafif olan NimBLE-Arduino kütüphanesini kullan.
ESP32 ile HC-05 kullanmaya gerek var mı?
Genellikle hayır. Klasik ESP32'deki BluetoothSerial kütüphanesi HC-05'in yaptığı işi dahili olarak yapar ve aynı terminal uygulamalarıyla çalışır. HC-05 yalnızca Bluetooth'u olmayan kartlarda (Arduino Uno, ESP8266, standart Pico, ESP32-S2) ya da klasik Bluetooth gereken bir projede yalnızca BLE'li bir ESP32 kullanıyorsan gerekir.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
ESP32 Dahili LED Yakma
ESP32 DevKit V1'in üzerindeki dahili (built-in) mavi LED'i yanıp söndürmek. Hiçbir harici bileşen gerekmez! ESP32 satın aldığında ilk denemen gereken proje. GPIO 2 dahili LED'e bağlıdır.

ESP32 ile LED Yakma
ESP32 ile temel GPIO çıkışı kullanımını öğretir. Bir LED'i dijital pinden açıp kapatarak yanıp sönme efekti elde ederiz. Arduino UNO'daki klasik blink projesinin ESP32 versiyonu; Arduino ortamındaki temel blink komutları aynıdır, ancak ESP32'nin pin yerleşimi ve 3,3 V GPIO seviyeleri Arduino UNO'dan farklıdır.

ESP32 Buton ile LED Kontrol (Toggle)
Butona her basıldığında LED'in durumu değişir: açıksa kapanır, kapalıysa açılır. Klasik toggle (geçiş) pattern'i. Debounce (sıçrama önleme) ile butonun mekanik gürültüsünü temizleyerek tek basışta tek değişim sağlarız.

Bluetooth İle Telefondan Led Yakma
Telefonundan odadaki ışıkları açıp kapatmanın küçük bir provası. Bluetooth modülü telefondan gelen harfleri Arduino'ya iletir; bir harf kırmızı LED'i, başka bir harf yeşil LED'i yakar, aynı harflerin

L298N ile Bluetooth Kontrollü Araba
Telefonunu kumandaya çevirip kendi robot arabanı sür. Arduino, Bluetooth modülü üzerinden telefondaki bir kumanda uygulamasından gelen komutları alır ve motor sürücüyle tekerlekleri döndürür; araba il

ESP32 RGB LED Kontrolü
3 ayrı LED yerine tek RGB LED ile sınırsız renk üretme. Arduino'daki gibi analogWrite() ile R/G/B değerlerini ayarlayarak gökkuşağı geçişi yaparız. Renk teorisi (red+green=yellow gibi) pratik olarak görülür.

ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)
DHT22 sensörü ile ortam sıcaklığını ve nem oranını ölçüp 16x2 I2C LCD ekranda gerçek zamanlı olarak gösteren bir projedir. Adafruit DHT kütüphanesi kullanılarak tek kablo (one-wire) protokolü ile sensörden veri okunur. Bu proje, IoT hava istasyonu, akıllı ev otomasyonu veya seralarda ortam takip sistemlerinin temelini oluşturur. ESP32'nin GPIO 4 pini üzerinden DHT22 ile haberleşme sağlanır.

ESP32 WS2812B NeoPixel Şerit Kontrolü
WS2812B (NeoPixel) adreslenebilir LED'ler, tek bir veri pini üzerinden zincirleme kontrol edilebilen akıllı RGB LED'lerdir. Bu projede 8 LED'lik bir WS2812B şeridi Adafruit_NeoPixel kütüphanesi ile kontrol edip akıcı bir gökkuşağı animasyonu üreteceğiz. ESP32'nin 3.3V mantık seviyesi çoğu WS2812B partisinde sorunsuz çalışsa da, kararsızlık durumunda 470Ω seri direnç ve 1000µF kondansatör çözümünü göstereceğiz. Animasyon, HSV renk uzayında ton (hue) değerini kaydırarak elde edilir.
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

