ESP8266, bir sensörü Wi-Fi'ye taşımanın hâlâ en ucuz yollarından biridir ve PubSubClient, ESP8266 sketch'lerinin çoğunun kullandığı MQTT kütüphanesidir. Bu eğitim size NodeMCU veya Wemos D1 mini için eksiksiz bir sketch veriyor: bir MQTT broker'a bağlanıyor, telemetri publish ediyor, komut alıyor, bloklamadan yeniden bağlanıyor ve Last Will ile varlığını duyuruyor. Ardından bağlantıyı sertifika fingerprint'i ile TLS'e yükseltiyor ve bunun yerine ne zaman kök CA kullanmanız gerektiğini açıklıyor.
Test edilen kurulum: Arduino IDE 2.x veya arduino-cli, ESP8266 core 3.1.x, PubSubClient 2.8.
Araç zincirini kurun
- Arduino IDE'de Tercihler'i açın ve şu kart yöneticisi URL'sini ekleyin:
https://arduino.esp8266.com/stable/package_esp8266com_index.json. - Kart Yöneticisi → esp8266 by ESP8266 Community paketini kurun.
- Kütüphane Yöneticisi → Nick O'Leary'nin PubSubClient kütüphanesini kurun.
- Kart olarak NodeMCU 1.0 (ESP-12E Module) veya LOLIN(WEMOS) D1 R2 & mini seçin.
arduino-cli ile aynı adımlar şöyledir:
arduino-cli config add board_manager.additional_urls \
https://arduino.esp8266.com/stable/package_esp8266com_index.json
arduino-cli core update-index
arduino-cli core install esp8266:esp8266
arduino-cli lib install PubSubClientESP8266 yalnızca 2,4 GHz Wi-Fi destekler. Router'ınız iki bant için tek bir ortak SSID yayınlıyorsa bu genellikle çalışır; yalnızca 5 GHz yayın yapan bir ağ asla çalışmaz.
Eksiksiz sketch
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-wifi-password";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* MQTT_USER = "username";
const char* MQTT_PASS = "password";
WiFiClient net;
PubSubClient mqtt(net);
String clientId, baseTopic;
unsigned long lastAttempt = 0, retryDelay = 1000, lastPublish = 0;
void onMessage(char* topic, byte* payload, unsigned int length) {
String msg;
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
Serial.printf("[%s] %s\n", topic, msg.c_str());
if (msg == "on") digitalWrite(LED_BUILTIN, LOW); // dahili LED aktif-düşük çalışır
if (msg == "off") digitalWrite(LED_BUILTIN, HIGH);
}
bool connectMqtt() {
String status = baseTopic + "/status";
// connect(id, user, pass, willTopic, willQos, willRetain, willMessage)
if (!mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS,
status.c_str(), 1, true, "offline")) {
Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
return false;
}
mqtt.publish(status.c_str(), "online", true); // retained
mqtt.subscribe((baseTopic + "/led").c_str(), 1);
Serial.println("MQTT connected as " + clientId);
return true;
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
WiFi.persistent(false); // her açılışta flash'a yeniden yazma
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
clientId = "esp8266-" + String(ESP.getChipId(), HEX);
baseTopic = "testmqtt/esp8266/" + clientId;
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setBufferSize(512); // varsayılan, topic ve başlık dahil 256 bayttır
mqtt.setKeepAlive(30);
mqtt.setSocketTimeout(5); // başarısız bir connect en fazla ~5 sn bloklar
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
delay(10); // core Wi-Fi'yi kendisi yeniden bağlar
return;
}
if (!mqtt.connected()) {
unsigned long now = millis();
if (now - lastAttempt >= retryDelay) {
lastAttempt = now;
retryDelay = connectMqtt() ? 1000 : min(retryDelay * 2, 60000UL);
}
return;
}
mqtt.loop(); // sık çalışmalı: mesajları alır, ping gönderir
if (millis() - lastPublish >= 10000) {
lastPublish = millis();
char payload[96];
snprintf(payload, sizeof(payload),
"{\"rssi\":%d,\"heap\":%u,\"uptime\":%lu}",
WiFi.RSSI(), ESP.getFreeHeap(), millis() / 1000);
mqtt.publish((baseTopic + "/telemetry").c_str(), payload);
}
}Neden böyle yazıldı
- Bloklamayan yeniden bağlanma. Klasik örnek
while (!client.connected()) { ...; delay(5000); }kullanır; bu da broker'a ulaşılamadığı sürece butonlar, sensörler, hatta Wi-Fi yığını dahil her şeyi dondurur. Buradaloop()hemen döner ve üstel backoff ile (1 sn, 2 sn, 4 sn … 60 sn'ye kadar) yeniden dener; böylece bir filo yeniden başlayan bir broker'ı bombardımana tutmaz. ESP.getChipId()'den türetilen kalıcı client ID. Aynı ID'ye sahip iki kart birbirini sonsuz bir döngüde atar; client ID en iyi uygulamaları yazısına bakın.- Last Will + retained durum. Kartın gücü kesilirse broker, keep-alive süresi dolduğunda (burada 1,5 × 30 sn)
.../statustopic'ineofflinepublish eder. Sonradan abone olan herkes güncel durumu hemen görür. - Her bağlantıdan sonra abone olun. PubSubClient clean session kullanır, bu yüzden abonelikler yeniden bağlanmadan sonra korunmaz.
broker.connectmq.comgibi paylaşımlı test uç noktaları zaten session saklamaz; bu nedenle sketchconnectMqtt()içinde her seferinde yeniden abone olur. setBufferSize(512): PubSubClient'ın varsayılan buffer'ı topic dahil paketin tamamı için 256 bayttır. Uzun bir topic artı bir JSON payload bunu hızla aşar vepublish()başka hiçbir ipucu vermedenfalsedöndürür. Paket boyutu hesaplayıcı paketinizin gerçek boyutunu gösterir.setSocketTimeout(5):connect()'in kendisi bloklayıcıdır. Varsayılan 15 saniyelik timeout, watchdog reset'lerini veya donmuş bir arayüzü tetiklemeye yeter.
Sketch'i test edin
Kartı flash'layın ve Seri Monitör'ü 115200 baud'da açın. MQTT connected as esp8266-xxxxxx görmelisiniz. Bunun yerine state=-2 veya başka bir kod görürseniz aşağıdaki hata tablosuna geçin.
Ardından online MQTT istemcisini açın, broker.connectmq.com adresine bağlanın ve testmqtt/esp8266/# topic'ine abone olun. Önce retained online durumu, ardından her 10 saniyede bir telemetri JSON'u gelir. testmqtt/esp8266/<client-id>/led topic'ine on publish edin; LED yanar.
ESP8266'nızı tarayıcıda izleyin
Last Will'i test etmek için kartın fişini çekin ve yaklaşık 45 saniye bekleyin: durum topic'i offline olur. Kartı tekrar takın, online'a döner; yeniden bağlanma yolunuz çalışıyor demektir.
Fingerprint ile TLS eklemek
1883 portu MQTT şifresini düz metin olarak gönderir. ESP8266 core'u BearSSL içerir, dolayısıyla 8883 portunda TLS için yalnızca farklı bir istemci sınıfı gerekir. Sunucuyu doğrulamanın en hafif yolu SHA-1 fingerprint ile sertifika sabitleme (pinning)'dir.
Broker'ın güncel sertifikasının fingerprint'ini bilgisayarınızdan alın:
openssl s_client -connect broker.connectmq.com:8883 \
-servername broker.connectmq.com </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha1 \
| sed 's/.*=//; s/:/ /g'Ardından sketch'te üç şeyi değiştirin:
#include <WiFiClientSecure.h>
const uint16_t MQTT_PORT = 8883;
const char* FINGERPRINT = "AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD";
BearSSL::WiFiClientSecure net; // WiFiClient'ın yerine geçer
PubSubClient mqtt(net);
// setup() içinde, mqtt.setServer()'dan önce:
net.setFingerprint(FINGERPRINT);
mqtt.setSocketTimeout(10); // 80 MHz'lik bir çipte TLS el sıkışması yavaştırEl sıkışma, düz bir TCP bağlantısından belirgin şekilde uzun sürer ve ESP8266'nın sınırlı RAM'inin önemli bir kısmını kullanır. Telemetride ESP.getFreeHeap() değerini izleyin; bellek daralırsa BearSSL net.setBufferSizes(1024, 1024) ile daha küçük buffer'lar kullanabilir, ancak yalnızca broker TLS max fragment length uzantısını destekliyorsa. Önce BearSSL::WiFiClientSecure::probeMaxFragmentLength(MQTT_HOST, 8883, 1024) ile kontrol edin.
Fingerprint mi, kök CA mı?
| Yöntem | Nasıl | Artı / eksi |
|---|---|---|
setFingerprint() | Sunucu sertifikasının SHA-1'ini sabitler | En küçük ve en hızlı yöntem, ama her sertifika yenilemesinde bozulur |
setTrustAnchors() | Kök CA'yı sabitler (BearSSL::X509List) | Yenilemelerden etkilenmez; configTime() ile doğru saat gerektirir |
setInsecure() | Doğrulama yok | Şifreli ama her sunucuyu kabul eder; yalnızca hata ayıklama için |
Genel CA'ların sertifikaları düzenli olarak, çoğu zaman birkaç ayda bir yenilenir. Fingerprint sabitlenmiş bir filo, yenileme gününde state=-2 ile bağlanmayı bırakır ve her cihazı yeniden flash'lamanız gerekir. Uzun ömürlü her şey için bunun yerine kök CA'yı sabitleyin:
#include <time.h>
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
...broker'ınızın sertifikasını imzalayan CA'nın kök sertifikası...
-----END CERTIFICATE-----
)EOF";
BearSSL::X509List trustAnchor(ROOT_CA);
// setup() içinde, Wi-Fi bağlandıktan sonra:
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
while (time(nullptr) < 1700000000) delay(200); // NTP'yi bekle
net.setTrustAnchors(&trustAnchor);Geçerli bir saat olmadan BearSSL her sertifikayı süresi dolmuş veya henüz geçerli değil sayar. İstemci sertifikaları dahil TLS ile ilgili daha fazla ayrıntı ESP32 TLS rehberinde; BearSSL API'si farklı olsa da kavramlar aynıdır.
Hata kodları
mqtt.state() şu değerleri döndürür:
| state | Sabit | Tipik sebep |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Keep-alive süresi içinde ping yanıtı yok; zayıf Wi-Fi |
| -3 | MQTT_CONNECTION_LOST | Ağ koptu veya broker socket'i kapattı |
| -2 | MQTT_CONNECT_FAILED | TCP/TLS başarısız: yanlış host veya port, DNS, firewall, eskimiş fingerprint |
| -1 | MQTT_DISCONNECTED | Temiz bağlantı kesme veya hiç bağlanmamış |
| 1 | MQTT_CONNECT_BAD_PROTOCOL | Broker MQTT 3.1.1'i reddediyor |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client ID reddedildi |
| 3 | MQTT_CONNECT_UNAVAILABLE | Broker kullanılamıyor |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Yanlış kullanıcı adı veya şifre |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Kimlik doğrulandı ama bağlanma izni yok |
Pozitif değerler broker'dan gelen CONNACK dönüş kodlarıdır; bunları reason code aracında arayın. Negatif değerler kütüphanenin kendisinden gelir ve neredeyse her zaman bir ağ sorununa işaret eder. ESP32 broker bağlantısını test etme yazısındaki adım adım yöntem ESP8266'da da aynen çalışır.
Sonraki adımlar
- Daha yeni bir çipe mi geçiyorsunuz? ESP32 Arduino eğitimi aynı PubSubClient API'sini kullanır.
- QoS 1 publish veya farklı bir API tarzı mı gerekiyor? arduino-mqtt kütüphanesi ile karşılaştırın.
- Daha büyük payload'lar mı? MQTT ile ArduinoJson buffer'ları doğru boyutlandırmayı gösteriyor.
Güvenilir bir ESP8266 MQTT düğümü birkaç alışkanlığa dayanır: loop()'u asla bloklamayın, backoff ile yeniden bağlanın, her bağlantıda yeniden abone olun, buffer'ı gerçek paketlerinize göre boyutlandırın ve bir sonraki sertifika yenilemesinden sonra da çalışması gereken cihazlarda fingerprint yerine kök CA sabitleyin.
Sıkça sorulan sorular
ESP8266'mın TLS bağlantısı neden birkaç hafta sonra çalışmayı bırakıyor?
Sertifikayı setFingerprint() ile sabitlediyseniz, broker sertifikasını her yenilediğinde fingerprint değişir ve el sıkışma state -2 ile başarısız olur. Bunun yerine kök CA'yı setTrustAnchors() ile sabitleyin; yeni sertifikayı aynı CA imzaladığı sürece yenilemelerden etkilenmez.
PubSubClient, ESP8266'da QoS 1 ile publish edebilir mi?
Hayır. PubSubClient yalnızca QoS 0 ile publish eder. QoS 0 veya 1 ile abone olabilir, yani gelen mesajlar onaylanabilir; ancak giden mesajlar fire-and-forget'tır. QoS 1 publish gerekiyorsa arduino-mqtt gibi bir kütüphane kullanın veya uygulama seviyesinde bir onay mekanizması ekleyin.
PubSubClient state -2 ne anlama gelir?
MQTT_CONNECT_FAILED: broker'a TCP veya TLS bağlantısı açılamamıştır. Sorun MQTT'nin altındadır: Wi-Fi, DNS, yanlış port, bir firewall ya da eskimiş bir fingerprint veya 8883 portunda düz WiFiClient kullanımı gibi bir TLS el sıkışma hatası.


