Rehberler · 12 dk okuma

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 geliştirme kartı Bluetooth ve BLE
ESP32 geliştirme kartı: Wi-Fi, Bluetooth Classic ve BLE aynı yonganın içindedir; telefona bağlanmak için harici bir Bluetooth modülü gerekmez.

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.

ÖzellikBluetooth Classic (SPP)Bluetooth Low Energy (BLE)
Arduino kütüphanesiBluetoothSerial (ESP32 paketinde hazır)BLEDevice (ESP32 paketinde hazır) ya da NimBLE-Arduino
Veri modeliSeri port gibi kesintisiz bayt akışıServis ve karakteristiklerden oluşan GATT tablosu
Veri miktarıYüksek, sürekli akışa uygunKüçük paketler; varsayılan olarak bildirim başına 20 bayt
Enerji tüketimiBağlıyken sürekli yüksekÇok düşük; pille aylarca çalışabilir
EşleştirmeTelefonun Bluetooth ayarlarından yapılırGerekmez; bağlantı uygulamanın içinden kurulur
AndroidDesteklenirDesteklenir
iPhone (iOS)DesteklenmezDesteklenir
Destekleyen ESP32'lerYalnızca ilk ESP32 (WROOM-32, WROVER)ESP32, S3, C3, C6 ve H2 (S2 hariç)
Tipik kullanımSeri terminal, robot araba, HC-05 yerineSensö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 / yongaBluetooth ClassicBLENot
ESP32 (WROOM-32, WROVER, DevKit V1)VarVar (4.2)BluetoothSerial çalışan tek ESP32 ailesi
ESP32-S2YokYokYalnızca Wi-Fi
ESP32-S3YokVar (Bluetooth 5)BluetoothSerial derlenmez, BLE kullan
ESP32-C3YokVar (Bluetooth 5)Küçük ve ucuz BLE kartları
ESP32-C6YokVar (Bluetooth 5)Wi-Fi 6, Zigbee ve Thread de var
ESP32-H2YokVar (Bluetooth 5)Wi-Fi yok; Zigbee ve Thread odaklı
ESP8266 (NodeMCU, Wemos D1)YokYokBluetooth için HC-05 ya da HM-10 gerekir
Raspberry Pi Pico W / Pico 2 WDonanımda varVarMicroPython yalnızca BLE destekler
Raspberry Pi Pico / Pico 2YokYokHarici Bluetooth modülü gerekir
Kart menüsünde ESP32-S3, C3, C6 ya da H2 seçiliyken BluetoothSerial kodunu derlersen, Espressif örneklerindeki (ve aşağıdaki kodumuzdaki) kontrol satırı sayesinde işlem "Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip." hatasıyla durur. Bu bir kurulum ya da kütüphane sorunu değildir; yongada klasik Bluetooth donanımı yoktur. Böyle bir kartın varsa doğrudan aşağıdaki BLE (Nordic UART) örneğine geç.

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:

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}

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

  1. Kodu yükledikten sonra Seri Monitör'ü 115200 baud hızında aç ve "Eslesmeye hazir" yazısını gör.
  2. 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.
  3. Google Play'den Kai Morich'in Serial Bluetooth Terminal uygulamasını kur.
  4. Uygulamada sol menüden Devices bölümüne gir ve Bluetooth Classic sekmesinde BlueGrays-ESP32'yi seç.
  5. Üst çubuktaki fiş simgesine dokun; ekranda "Connected" yazısı çıkınca bağlantı tamamdır.
  6. 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.
Serial Bluetooth Terminal'in altındaki M1 ve M2 makro düğmelerine uzun basarak "1" ve "0" komutlarını kaydedersen LED'i tek dokunuşla yakıp söndürebilirsin. Uygulama ayarlarındaki Send > Newline seçeneği varsayılan olarak CR+LF'tir; koddaki trim() fazladan gelen CR karakterini zaten temizler. LED yerine bir röle bağlayarak aynı kodla lamba ya da priz kontrol edebilirsin; ayrıntılar Röle Modülü Nedir? yazısında.

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.

TerimAnlamıBu yazıdaki örnek
Yayın (advertising)Cihazın adını ve servislerini duyurduğu kısa paket"BlueGrays-BLE" adıyla yayın
ServisBirbiriyle ilgili karakteristikleri bir arada tutan grupNordic UART servisi, Environmental Sensing (0x181A)
KarakteristikOkunan ya da yazılan tek bir değerLED komutu, sıcaklık (0x2A6E)
READTelefon değeri istediği an okurSon ölçülen sıcaklık
WRITE / WRITE_NRTelefon değer yazar (cevaplı ya da cevapsız)"1" yazınca LED yanar
NOTIFYESP32 değer değişince telefona kendisi haber verir2 saniyede bir yeni sıcaklık
Tanımlayıcı 0x2902 (CCCD)Bildirimleri açıp kapatan anahtarnRF Connect'teki üçlü ok düğmesi
UUIDServis 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 / karakteristikUUIDVeri biçimi
Battery Service / Battery Level0x180F / 0x2A191 bayt, yüzde (0–100)
Environmental Sensing / Temperature0x181A / 0x2A6E2 bayt işaretli tam sayı, 0,01 °C
Environmental Sensing / Humidity0x181A / 0x2A6F2 bayt işaretsiz tam sayı, %0,01
Heart Rate / Heart Rate Measurement0x180D / 0x2A37Bayrak baytı + nabız değeri
Nordic UART Service (NUS)6E400001-B5A3-F393-E0A9-E50E24DCCA9ERX 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.

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}

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.

Bu yazıdaki kodlar ESP32 Arduino çekirdeğinin (kart paketi) 2.0.11 sürümüyle derlendi. Bu sürümde getValue() gibi BLE fonksiyonları std::string döndürür ve internetteki örneklerin çoğu da bu türü kullanır. Çekirdek 3.0 ile BLE fonksiyonları Arduino'nun String türüne geçti; bu yüzden std::string ile yazılmış kodlar 3.x'te "conversion from 'String' to non-scalar type 'std::string' requested" hatası verir. Bizim koddaki gibi sonucu String türünde bir değişkene atar ve sonuna .c_str() eklersen satır hem 2.x hem 3.x çekirdekte derlenir.

nRF Connect ile BLE testi adım adım

  1. Telefonuna Nordic Semiconductor'ın ücretsiz nRF Connect for Mobile uygulamasını kur; Android ve iPhone sürümü vardır.
  2. 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.
  3. 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.
  4. 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ı.
  5. TX karakteristiğinin (6E400003) yanındaki üçlü aşağı ok simgesine dokunarak bildirimleri aç.
  6. 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 sıcaklık ve nem sensörü
DHT22 sıcaklığı 0,1 °C çözünürlükle ölçer; 3,3 V ile çalışabildiği için ESP32'ye doğrudan bağlanır.
DHT22 piniESP32Not
VCC (+)3V33,3–5 V arasında çalışır
DATA (OUT)GPIO 4Çıplak sensörde DATA ile 3V3 arasına 10 kΩ direnç ekle
GND (−)GNDOrtak 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.

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}

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.

Bağlantı kurmadan veri göndermek de mümkündür: ESP32 ölçümü doğrudan yayın paketinin üretici verisi (manufacturer data) alanına koyabilir, çevredeki her telefon ya da ağ geçidi de bağlanmadan bu değeri okuyabilir. Bu yönteme beacon denir; Home Assistant'ın tanıdığı açık BTHome biçimi de böyle çalışır. ESP32 her ölçümden sonra derin uykuya girerse pil ömrü aylara çıkar; bunun için ESP32 Deep Sleep yazısına bakabilirsin.

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 kartı
Raspberry Pi Pico ailesinde Bluetooth yalnızca adında W olan kartlarda (Pico W, Pico 2 W) bulunur; standart Pico'da kablosuz bağlantı yoktur.

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.

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)

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 / hataNedeniÇözüm
"Serial Bluetooth not available or not enabled"Seçili kart ESP32-S3, C3, C6 ya da H2; klasik Bluetooth yokKlasik 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 derleniyorSonucu 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ıyorKlasik Bluetooth (SPP) iOS'ta desteklenmezBLE kullan ve bağlantıyı bir uygulamanın içinden kur
İlk bağlantıdan sonra cihaz bir daha görünmüyorBağlantı kopunca yayın yeniden başlatılmadıKopmadan sonra startAdvertising() çağır
Bildirim gelmiyorTelefonda bildirim açılmadı ya da BLE2902 eklenmedinRF 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 kesiliyorVarsayılan MTU 23 bayt, yani bildirim başına 20 bayt veriMesajı 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üyorTelefon eski GATT tablosunu önbellekte tutuyorTelefonun Bluetooth'unu kapatıp aç; Android'de cihazı unut
Bağlantı sorunlarını çözerken Araçlar > Core Debug Level menüsünden Debug ya da Verbose seç. Seri Monitör'de Bluetooth yığınının bağlantı, kopma ve hata kayıtlarını görürsün; sorunun telefonda mı yoksa ESP32'de mi olduğunu bu kayıtlar hızlıca gösterir. Telefon tarafında da aynı testi nRF Connect ile tekrarlamak, sorunun kendi uygulamandan mı kaynaklandığını anlamanı sağlar.

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.

Tüm projeleri gör
ESP32 Dahili LED Yakma
#106Başlangıç

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.

4.8(29)
53
ESP32 ile LED Yakma
#103Başlangıç

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.

4.7(37)
99
ESP32 Buton ile LED Kontrol (Toggle)
#105Başlangıç

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.

4.6(39)
57
Bluetooth İle Telefondan Led Yakma
#34Başlangıç
Premium

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

4.8(68)
89
L298N ile Bluetooth Kontrollü Araba
#80İleri
Premium

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

5.0(38)
65
ESP32 RGB LED Kontrolü
#111Başlangıç

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.

4.6(29)
51
ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)
#117Orta

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.

4.8(38)
56
ESP32 WS2812B NeoPixel Şerit Kontrolü
#126Orta
Premium

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.

4.9(19)
30