TLS/SSL ile Şifreli MQTT Veri Yayını
Proje Amacı
Sıcaklık/nem verisini, sertifika tabanlı TLS/SSL şifrelemesiyle (WiFiClientSecure) bir MQTT broker'a güvenli şekilde yayınlamayı öğretir — düz metin (plaintext) MQTT'nin aksine veri yolda kimse tarafından okunamaz veya değiştirilemez. ESP32'nin donanımsal AES/SHA kripto hızlandırıcısı, TLS el sıkışmasını (handshake) makul bir sürede tamamlayacak işlem gücünü sağlar; 520KB SRAM'i ise TLS oturum arabelleklerini, sertifika zincirini ve şifreleme durumunu aynı anda bellekte tutabilir. Bu kapasite Arduino Uno'da bulunmaz: kartın sadece 2KB SRAM'i vardır ve tek bir TLS sertifikası bile bu belleğe sığmaz; kripto hızlandırma donanımı ve WiFi radyosu da zaten mevcut değildir, dolayısıyla şifreli bir ağ bağlantısı kurmanın önünde temel bir donanım sınırı vardır.
Gerekli Malzemeler
- ESP32 DevKit V1 (38 pin)
- DHT22 sıcaklık/nem sensörü
- 10kΩ direnç (pull-up, modülsüz DHT22 için)
- Breadboard
- Jumper kablolar
Gerekli Kütüphaneler
WiFiClientSecure.h (ESP32 core ile gelir), PubSubClient.h (Nick O'Leary — Library Manager'dan kurun, sürüm >= 2.8), DHT sensor library (Adafruit, >= 1.4.4) + Adafruit Unified Sensor bağımlılığı.
Pin Bağlantıları
| Bileşen Pini | Bağlantı |
|---|---|
| DHT22 VCC | 3V3 |
| DHT22 GND | GND |
| DHT22 DATA (10kΩ pull-up ile 3V3'e) | GPIO27 |
Devre Şeması
ESP32 Kodu
| 1 | #include <WiFiClientSecure.h> |
| 2 | #include <PubSubClient.h> |
| 3 | #include <DHT.h> |
| 4 | |
| 5 | #define DHTPIN 27 |
| 6 | #define DHTTYPE DHT22 |
| 7 | DHT dht(DHTPIN, DHTTYPE); |
| 8 | |
| 9 | const char* ssid = "WIFI_ADINIZ"; |
| 10 | const char* password = "WIFI_SIFRENIZ"; |
| 11 | const char* mqtt_server = "broker.ornek.com"; |
| 12 | const int mqtt_port = 8883; // TLS/SSL MQTT standart portu |
| 13 | |
| 14 | // Broker'ın kök CA sertifikası (PEM formatında, örnek yer tutucu). |
| 15 | // Gerçek projede kendi broker'ınızın CA sertifikasını buraya yapıştırın. |
| 16 | const char* root_ca = R"EOF( |
| 17 | -----BEGIN CERTIFICATE----- |
| 18 | BURAYA_GERCEK_KOK_SERTIFIKA_PEM_ICERIGI_GELECEK |
| 19 | -----END CERTIFICATE----- |
| 20 | )EOF"; |
| 21 | |
| 22 | WiFiClientSecure espClient; |
| 23 | PubSubClient client(espClient); |
| 24 | |
| 25 | void wifiBaglan() { |
| 26 | WiFi.begin(ssid, password); |
| 27 | Serial.print("WiFi'ye bağlanıyor"); |
| 28 | while (WiFi.status() != WL_CONNECTED) { delay(400); Serial.print("."); } |
| 29 | Serial.println("\nBağlandı: " + WiFi.localIP().toString()); |
| 30 | } |
| 31 | |
| 32 | void mqttBaglan() { |
| 33 | while (!client.connected()) { |
| 34 | Serial.print("MQTT (TLS) bağlantısı deneniyor..."); |
| 35 | String clientId = "esp32-" + String(random(0xffff), HEX); |
| 36 | if (client.connect(clientId.c_str())) { |
| 37 | Serial.println(" bağlandı."); |
| 38 | } else { |
| 39 | Serial.print(" başarısız, rc="); Serial.print(client.state()); |
| 40 | Serial.println(" 5 saniye sonra tekrar denenecek."); |
| 41 | delay(5000); |
| 42 | } |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | void setup() { |
| 47 | Serial.begin(115200); |
| 48 | dht.begin(); |
| 49 | wifiBaglan(); |
| 50 | espClient.setCACert(root_ca); // sertifika doğrulaması AÇIK, güvensiz setInsecure() KULLANILMAMALI |
| 51 | client.setServer(mqtt_server, mqtt_port); |
| 52 | } |
| 53 | |
| 54 | void loop() { |
| 55 | if (!client.connected()) mqttBaglan(); |
| 56 | client.loop(); |
| 57 | |
| 58 | static unsigned long sonGonderim = 0; |
| 59 | if (millis() - sonGonderim > 10000) { |
| 60 | sonGonderim = millis(); |
| 61 | float sicaklik = dht.readTemperature(); |
| 62 | float nem = dht.readHumidity(); |
| 63 | if (isnan(sicaklik) || isnan(nem)) { |
| 64 | Serial.println("DHT22 okunamadı, atlanıyor."); |
| 65 | return; |
| 66 | } |
| 67 | char payload[64]; |
| 68 | snprintf(payload, sizeof(payload), "{\"sicaklik\":%.1f,\"nem\":%.1f}", sicaklik, nem); |
| 69 | client.publish("devreai/sensor/oda1", payload); |
| 70 | Serial.println(String("Yayınlandı: ") + payload); |
| 71 | } |
| 72 | } |
Çalışma Açıklaması
setInsecure() ASLA üretimde kullanılmamalıdır — sertifika doğrulamasını tamamen kapatır ve araya girme (man-in-the-middle) saldırılarına açık hale getirir; bu örnek bilinçli olarak setCACert() ile doğru ve güvenli yolu gösterir. TLS handshake düz MQTT'ye göre 1-3 saniye ek gecikme ve biraz daha fazla enerji tüketimi getirir, bu beklenen ve normal bir bedeldir. Broker tarafında da istemci sertifikası doğrulaması isteniyorsa (mTLS), espClient.setCertificate()/setPrivateKey() ile cihaza özel bir istemci sertifikası da eklenmelidir.
Test Bilgisi
root_ca değişkenine broker'ınızın gerçek kök sertifikasını (openssl s_client ile alınabilir) tam ve eksiksiz yapıştırın. Kodu yükleyip Seri Monitör'de (115200 baud) WiFi ve ardından MQTT (TLS) 'bağlandı' mesajlarını sırayla doğrulayın. MQTT Explorer gibi bir masaüstü istemcisiyle aynı broker'a bağlanıp 'devreai/sensor/oda1' konusuna abone olun; 10 saniyede bir sıcaklık/nem içeren JSON verinin geldiğini ve değerlerin DHT22'yi elle ısıtıp/soğutarak gerçekten değiştiğini gözlemleyin.
Olası Sorunlar ve Çözümleri
'Bağlanılamadı, rc=-2' hatası genelde sertifika sorunudur: root_ca değişkeninin tam ve doğru PEM formatında (BEGIN/END satırları dahil, satır sonlarında eksik karakter olmadan) girildiğinden emin olun. Bağlantı çok yavaş açılıyor veya sertifika 'geçersiz tarih' hatası veriyorsa: ESP32'nin gerçek zaman saatinin (RTC) sertifika geçerlilik aralığını doğrulamak için doğru ayarlı olması gerekir, bağlantı öncesi kısa bir NTP senkronizasyonu (bkz. proje #08) sorunu çözer. Brown-out (ani, açıklanamayan reset) yaşıyorsanız: TLS handshake anlık yüksek akım çeker, USB güç kaynağınızın veya harici adaptörün en az 500mA verebildiğinden emin olun.
Alternatif Çözümler
Sertifikayı doğrudan koda gömmek yerine LittleFS/SPIFFS dosya sisteminden okumak (bkz. proje #16), sertifika güncellemesini yeniden derleme gerektirmeden yapılabilir hale getirir — özellikle sahadaki çok sayıda cihazda pratiktir. Kendinden imzalı sertifika yerine client-side sertifika (mTLS) ile karşılıklı kimlik doğrulama eklenerek broker'ın sadece yetkili cihazları kabul etmesi sağlanabilir. AWS IoT Core veya Azure IoT Hub gibi bulut MQTT servisleriyle doğrudan entegrasyon için ilgili servisin kendi cihaz sertifikası kayıt (provisioning) akışı kullanılabilir, bu da her cihaza benzersiz bir kimlik verir.
Benzer Projeler
Deep Sleep + RTC Memory ile Aylarca Pil Ömürlü Sensör İstasyonu
ESP32'yi çoğu zaman µA (mikroamper) seviyesinde akım çeken 'deep sleep' modunda tutup, sadece belirli aralıklarla uyanarak sensör okuyup WiFi ile veri gönderdikten sonra tekrar uyutmayı öğretir — bu tek teknik, pille çalışan bir sensörün ömrünü günlerden aylara çıkarabilecek kadar etkilidir. RTC memory (RTC_DATA_ATTR) ana işlemci ve RAM kapansa bile belirli veriyi (örneğin bir okuma sayacını) korur, böylece cihaz her uyanışta kaldığı yerden devam edebilir. Bu düzeydeki enerji verimliliği Arduino Uno'da yakalanamaz: kartın kendi USB-seri çipi ve gerilim regülatörü sürekli mA seviyesinde akım çeker (en 'derin' Uno uykusu bile ESP32 deep sleep'inin binlerce katı akım harcar) ve Uno'nun ana işlemcisi kapandığında hayatta kalan bir RTC memory kavramı da bulunmaz.
OTA Kablosuz Firmware Güncelleme Sistemi
ESP32'nin dahili çift OTA (Over-The-Air) flash partisyon şemasını kullanarak, sahaya kurulmuş bir cihaza tekrar USB kablosu takmadan tamamen kablosuz şekilde yeni firmware yüklemeyi öğretir — özellikle tavana, duvara veya erişimi zor bir noktaya monte edilmiş onlarca cihazı yönetirken zaman ve emek kazandıran pratik bir yetenektir. ESP32'nin flash belleği kalkınma sırasında bir 'factory' bölümü ile iki ayrı OTA partisyonuna bölünebilir; cihaz yeni kodu ağdan indirip o an boş olan partisyona yazar, bütünlük kontrolünden geçerse oradan açılış yapar ve önceki çalışan sürüm otomatik olarak yedek konumunda kalır, böylece yarım kalan veya bozuk bir güncelleme cihazı kullanılamaz hale getirmez. Bu iki katmanlı flash mimarisi ESP32'ye özgüdür: Arduino Uno'nun ATmega328P'sinde toplam flash belleği zaten 32KB'dir ve sketch kodunun kendisi bu alanın büyük bölümünü kapladığından ikinci bir tam firmware kopyasına yer kalmaz; kartta WiFi donanımı da bulunmadığından ağ üzerinden güncelleme almak zaten söz konusu değildir.
Watchdog + Otomatik Yeniden Bağlanma ile Endüstriyel Güvenilirlik
ESP-IDF'in görev bazlı donanımsal Task Watchdog Timer'ını (TWDT) kullanarak, ana döngü belirli bir süre 'nefes almazsa' (donarsa) cihazı otomatik olarak yeniden başlatmayı; ayrıca WiFi bağlantısı koptuğunda üstel geri çekilmeyle (exponential backoff) otomatik yeniden bağlanmayı öğretir — insansız çalışan sahadaki bir cihazın kendi kendini toparlaması için endüstriyel projelerde standart kabul edilen bir güvenilirlik desenidir. ESP32'nin FreeRTOS çekirdeği, her görevi (task) ayrı ayrı izleyebilen donanım destekli bir watchdog mekanizması sunar. Bu düzeyde bir izleme Arduino Uno'da kurulamaz: Uno'nun donanımsal watchdog'u (WDT) çok daha kaba bir genel zamanlayıcıdır, görev/task kavramı yoktur (bir RTOS çalıştıramaz) ve zaten yeniden bağlanacak bir WiFi bağlantısı da bulunmaz.