ESP32 WiFiManager MQTT parametreleriyle, başka birine teslim ettiğiniz bir cihaz telefondan yapılandırılabilir: cihaz bir captive portal açar, kullanıcı bir Wi-Fi ağı seçip broker ayarlarını doldurur ve cihaz her şeyi yeniden başlatmalar boyunca hatırlar. Bu eğitim, bu akışı WiFiManager kütüphanesi ve Preferences ile kurar; varsayılan olarak broker.connectmq.com kullanıldığı için kutudan çıktığı gibi çalışır.
Koda Gömülü Ayarların Sorunu
Çoğu MQTT örneği const char* WIFI_SSID = "..." ve const char* MQTT_PASS = "..." gibi satırlarla başlar. Masanızda bu sorun değildir, ancak her yeni ağ veya kimlik bilgisi değişikliği yeniden derleme ve yeniden yükleme anlamına gelir. Ayrıca firmware ikili dosyasının sırlar içermesi ve aynı ikili dosyayı iki müşteriye verememeniz anlamına gelir.
Bir provisioning portalı bunu çözer. İlk açılışta veya kullanıcı bir butona basılı tuttuğunda ESP32 kendi erişim noktasını başlatır. Bir telefonla ona bağlanmak, kullanıcının şunları girdiği bir web sayfası açar:
- Wi-Fi ağı ve parolası.
- MQTT broker host'u ve portu.
- MQTT client ID'si, kullanıcı adı ve parolası.
- Bu cihaz için bir topic öneki.
Cihaz değerleri kalıcı belleğe kaydeder, Wi-Fi ağına katılır ve broker'a bağlanır. Aynı ikili dosya herkes için çalışır.
Kütüphaneler ve Donanım
- tzapu'nun WiFiManager kütüphanesi (2.x); Arduino Library Manager'dan veya PlatformIO'dan
tzapu/WiFiManagerolarak kurulabilir. - MQTT için PubSubClient.
- Arduino-ESP32 core ile birlikte gelen ve anahtar–değer çiftlerini NVS flash'ta saklayan Preferences.
- Herhangi bir ESP32 kartı. Örnek, portalı yeniden açmak için çoğu DevKit'teki BOOT butonunu (GPIO 0) kullanır.
ESP32 WiFiManager MQTT Akışı Nasıl Çalışır?
| Durum | Ne olur |
|---|---|
| İlk açılış, kayıtlı Wi-Fi yok | Portal Sensor-Setup-xxxx erişim noktası olarak başlar |
| Kayıtlı Wi-Fi erişilebilir | Cihaz sessizce bağlanır, MQTT ayarlarını Preferences'tan yükler |
| Kayıtlı Wi-Fi erişilemez | Bağlantı denemesi başarısız olduktan sonra portal yeniden açılır |
| Açılışta BOOT butonu 3 sn basılı tutulur | Kullanıcının ayarları değiştirebilmesi için portal isteğe bağlı açılır |
| Portal 3 dakika kullanılmadan kalır | Cihaz yeniden başlar ve tekrar dener; böylece asla sonsuza kadar takılı kalmaz |
WiFiManager Wi-Fi kimlik bilgilerini kendisi saklar. MQTT alanları ise portala eklediğiniz ve kendiniz kalıcı hale getirdiğiniz özel parametrelerdir.
Eksiksiz Sketch
#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <PubSubClient.h>
const int CONFIG_BUTTON = 0; // BOOT button on most ESP32 DevKits
Preferences prefs;
WiFiClient net;
PubSubClient mqtt(net);
char mqttHost[64] = "broker.connectmq.com";
char mqttPort[6] = "1883";
char mqttClient[48] = "";
char mqttUser[48] = "";
char mqttPass[64] = "";
char topicPrefix[64] = "connectmq/demo/d93f2a";
bool shouldSave = false;
unsigned long lastPublish = 0;
void loadSettings() {
prefs.begin("mqtt", true);
prefs.getString("host", mqttHost, sizeof(mqttHost));
prefs.getString("port", mqttPort, sizeof(mqttPort));
prefs.getString("client", mqttClient, sizeof(mqttClient));
prefs.getString("user", mqttUser, sizeof(mqttUser));
prefs.getString("pass", mqttPass, sizeof(mqttPass));
prefs.getString("prefix", topicPrefix, sizeof(topicPrefix));
prefs.end();
if (strlen(mqttClient) == 0) { // never connect with an empty client ID
snprintf(mqttClient, sizeof(mqttClient), "esp32-%08x", (uint32_t)ESP.getEfuseMac());
}
}
void saveSettings() {
prefs.begin("mqtt", false);
prefs.putString("host", mqttHost);
prefs.putString("port", mqttPort);
prefs.putString("client", mqttClient);
prefs.putString("user", mqttUser);
prefs.putString("pass", mqttPass);
prefs.putString("prefix", topicPrefix);
prefs.end();
}
void runWifiManager(bool forcePortal) {
WiFiManager wm;
WiFiManagerParameter pHost("host", "MQTT host", mqttHost, sizeof(mqttHost) - 1);
WiFiManagerParameter pPort("port", "MQTT port (1883 or 8883)", mqttPort, sizeof(mqttPort) - 1);
WiFiManagerParameter pClient("client", "MQTT client ID", mqttClient, sizeof(mqttClient) - 1);
WiFiManagerParameter pUser("user", "MQTT username (optional)", mqttUser, sizeof(mqttUser) - 1);
WiFiManagerParameter pPass("pass", "MQTT password (optional)", mqttPass, sizeof(mqttPass) - 1,
"type='password'");
WiFiManagerParameter pPrefix("prefix", "Topic prefix", topicPrefix, sizeof(topicPrefix) - 1);
wm.addParameter(&pHost);
wm.addParameter(&pPort);
wm.addParameter(&pClient);
wm.addParameter(&pUser);
wm.addParameter(&pPass);
wm.addParameter(&pPrefix);
wm.setSaveParamsCallback([]() { shouldSave = true; });
wm.setConfigPortalTimeout(180);
char apName[32];
snprintf(apName, sizeof(apName), "Sensor-Setup-%04x", (uint16_t)ESP.getEfuseMac());
bool connected = forcePortal ? wm.startConfigPortal(apName)
: wm.autoConnect(apName);
if (!connected) {
ESP.restart(); // portal timed out: try again from scratch
}
if (shouldSave) {
strlcpy(mqttHost, pHost.getValue(), sizeof(mqttHost));
strlcpy(mqttPort, pPort.getValue(), sizeof(mqttPort));
strlcpy(mqttClient, pClient.getValue(), sizeof(mqttClient));
strlcpy(mqttUser, pUser.getValue(), sizeof(mqttUser));
strlcpy(mqttPass, pPass.getValue(), sizeof(mqttPass));
strlcpy(topicPrefix, pPrefix.getValue(), sizeof(topicPrefix));
saveSettings();
loadSettings(); // re-applies the client ID fallback
}
}
bool buttonHeld(unsigned long ms) {
pinMode(CONFIG_BUTTON, INPUT_PULLUP);
unsigned long start = millis();
while (digitalRead(CONFIG_BUTTON) == LOW) {
if (millis() - start > ms) return true;
delay(10);
}
return false;
}
void connectMqtt() {
while (!mqtt.connected()) {
bool ok = strlen(mqttUser) > 0
? mqtt.connect(mqttClient, mqttUser, mqttPass)
: mqtt.connect(mqttClient);
if (ok) {
String cmd = String(topicPrefix) + "/cmd";
mqtt.subscribe(cmd.c_str());
} else {
Serial.printf("MQTT failed state=%d, retrying\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
loadSettings();
delay(200);
runWifiManager(buttonHeld(3000));
mqtt.setServer(mqttHost, atoi(mqttPort));
Serial.printf("Broker %s:%s as %s\n", mqttHost, mqttPort, mqttClient);
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
if (millis() - lastPublish > 10000) {
lastPublish = millis();
String topic = String(topicPrefix) + "/telemetry";
String payload = "{\"uptime\":" + String(millis() / 1000) + "}";
mqtt.publish(topic.c_str(), payload.c_str());
}
}
Portalda Adım Adım
- Sketch'i yükleyin ve seri monitörü açın.
- Telefonunuzda
Sensor-Setup-xxxxadlı Wi-Fi ağına katılın. Çoğu telefon captive portal'ı otomatik olarak açar; açmazsa192.168.4.1adresine gidin. - Configure WiFi'a dokunun, ağınızı seçin ve parolasını girin.
- broker.connectmq.com'un public alanını kullanmak için varsayılanları bırakın veya kendi ayarlarınızı girin, ardından kaydedin.
- Cihaz ağınıza katılır, broker'a bağlanır ve
<prefix>/telemetrytopic'ine yayın yapmaya başlar.
Bir bilgisayardan doğrulayın:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-d93f2a -t 'connectmq/demo/d93f2a/#' -v
Varsayılan öneki koruyan her cihaz aynı topic'e yayın yapar; bu yüzden birden fazla cihaz kuruyorsanız portalda her birine kendi önekini verin.
Güvenilir Kılan Ayrıntılar
Portalı oluşturmadan önce yükleyin. Özel parametreler mevcut değerler varsayılan olacak şekilde oluşturulur; böylece portal boş bir form değil, kayıtlı olanı gösterir. Bir alanı düzenleyen kullanıcılar diğerlerini kaybetmez.
Asla boş bir client ID ile bağlanmayın. Kullanıcı alanı temizlerse loadSettings(), çipin MAC adresinden türetilen bir ID'ye geri döner. Boş veya yinelenen bir client ID, cihazların bağlanamamasının veya birbirini düşürmesinin başlıca nedenlerinden biridir; tam liste için ESP32'nin bir MQTT broker'a neden bağlanamadığı yazısına bakın.
Portal zaman aşımını kullanın. setConfigPortalTimeout() olmadan, router'ı geçici olarak kapalı olan bir cihaz sonsuza kadar portal modunda bekler. Zaman aşımıyla yeniden başlar ve kayıtlı ağı tekrar dener.
Sayıları portalda dizgi olarak saklayın, bir kez dönüştürün. WiFiManager parametreleri metin alanlarıdır. Portu depolamada dizgi olarak tutun ve istemciyi yapılandırırken atoi() ile dönüştürün.
Parola alanını gizleyin. Ek argüman type='password', portalda MQTT parolasını maskeler. Değer yine de açık erişim noktası üzerinden düz HTTP ile gönderilir; bu yüzden yalnızca güvenilir bir yerde yapılandırma yapın.
Kullanıcıların Girdiklerini Doğrulamak
Bir captive portal kullanıcının girdiği her şeyi kabul eder; bu yüzden firmware hatalarla başa çıkabilmelidir. Kaydettikten sonra yapılacak birkaç kontrol, bir cihazın hatalı ayarlarla kendini kullanılamaz hale getirmesini önler:
- Port: yalnızca 1 ile 65535 arasındaki değerleri kabul edin. Alan metin içerdiği için
atoi()0 döndürürse 1883'e geri dönün. - Host: boşlukları kırpın ve boş bir dizgiyi reddedin. Kullanıcılar sıklıkla
mqtt://broker.connectmq.comgibi bir URL yapıştırır; şemayı ve host adından sonraki her şeyi çıkarın. - Topic öneki: sondaki eğik çizgiyi kaldırın ve publish topic'lerinde izin verilmeyen wildcard'ları (
+ve#) reddedin. - Kimlik bilgileri: bir kullanıcı adı ayarlanmış ancak parola boşsa seri konsolda uyarı verin, çünkü broker bağlantıyı reddedecektir.
MQTT bağlantısı pozitif bir mqtt.state() koduyla (hatalı kimlik bilgileri veya yetkisiz) art arda başarısız olduğunda bu genellikle bir ağ sorunu değil, ayar sorunudur. İyi bir kalıp, bu başarısızlıkları saymak ve art arda birkaç kez olduktan sonra portalı otomatik olarak yeniden açmaktır; böylece kullanıcı bir butona veya seri kabloya ihtiyaç duymadan değerleri düzeltebilir.
Üretim Broker'ları İçin TLS Eklemek
1883 portu düz metindir ve public test alanı için uygundur. Özel bir broker için, kullanıcı 8883 portunu seçtiğinde WiFiClientSecure'a geçin:
#include <WiFiClientSecure.h>
WiFiClientSecure secureNet;
void configureTransport() {
if (atoi(mqttPort) == 8883) {
secureNet.setCACert(ISRG_ROOT_X1); // Let's Encrypt root, stored as a PEM string
mqtt.setClient(secureNet);
} else {
mqtt.setClient(net);
}
mqtt.setServer(mqttHost, atoi(mqttPort));
}
setup() içindeki düz setServer() satırı yerine configureTransport()'u çağırın. ConnectMQ ile kullanıcılar daha sonra broker'ları için oluşturulan client ID, kullanıcı adı ve parolayı portala girer (YOUR_CLIENT_ID, YOUR_USERNAME, YOUR_PASSWORD). Client ID bu kimlik bilgilerine bağlıdır; bu yüzden tam olarak verildiği şekilde girilmelidir. ESP32 MQTT SSL/TLS rehberi kök sertifikanın nasıl gömüleceğini açıklar.
Sorun Giderme
| Sorun | Olası neden | Çözüm |
|---|---|---|
| Portal hiç görünmüyor | Kayıtlı Wi-Fi hâlâ bağlanıyor | Reset sırasında BOOT butonunu basılı tutun veya bir kez wm.resetSettings() çağırın |
| Parametreler kaydedilmiyor | Kaydetme callback'i tetiklenmedi | setSaveParamsCallback() kullanın ve portal sayfasında Save'e basın |
| Değerler kesiliyor | Alan uzunluğu girdiden küçük | Tampon boyutunu ve parametre uzunluğunu artırın |
| Wi-Fi'a bağlanıyor ama MQTT'ye bağlanmıyor | Taşıma için yanlış port veya yanlış kimlik bilgileri | mqtt.state() değerini ve port/TLS kombinasyonunu kontrol edin |
| Telefon portalı açmıyor | Captive algılama engellenmiş | Elle 192.168.4.1 adresine gidin |
Sonraki Adımlar
Ayarlar yapılandırılabilir hale geldiğinde doğal sonraki adımlar uzaktan güncellemeler ve durum raporlamadır: MQTT üzerinden tetiklenen ESP32 OTA güncellemeleri USB olmadan yeni firmware göndermeyi gösterir; ESP32 IoT eğitimi de provisioning'i eksiksiz bir cihaz projesine yerleştirir.
Cihazları ConnectMQ İçin Yapılandırın
Varsayılan ayarlar ücretsiz public MQTT broker'ı gösterir; böylece yeni bir cihaz Wi-Fi'a katıldığı anda çalışır. Kurulumlar için ConnectMQ üzerinde özel bir broker oluşturun ve kimlik bilgilerini portala girin. ConnectMQ yönetilen bir MQTT broker'dır; bu yüzden cihazları yapılandıran kişilerin hiçbir zaman bir sunucu çalıştırması gerekmez. app.connectmq.com adresinde kredi kartı gerekmeden ücretsiz Serverless planıyla başlayın.
Sık Sorulan Sorular
WiFiManager Wi-Fi parolasını nerede saklar?
ESP32 Wi-Fi sürücüsü ağ kimlik bilgilerini NVS flash'ta saklar. Bu sketch'te MQTT alanları ayrı olarak, Preferences kullanılarak mqtt ad alanı altında saklanır.
WiFiManager portalını daha sonra nasıl yeniden açarım?
Bu sketch'te açılış sırasında BOOT butonunu üç saniye basılı tutun; bu startConfigPortal()'ı çağırır. Cihaz hâlâ erişilebilir olduğu sürece bunu bir MQTT komutuyla da tetikleyebilirsiniz.
WiFiManager'ı AsyncMqttClient ile kullanabilir miyim?
Evet. WiFiManager yalnızca provisioning'i yönetir. Geri döndüğünde, saklanan değerleri tercih ettiğiniz MQTT istemcisini yapılandırmak için kullanın.
Captive portal güvenli mi?
Portal düz HTTP üzerinden açık bir erişim noktası olarak çalışır; bu yüzden yakındaki herkes kurulum sırasında değerleri gözlemleyebilir. Güvenilir bir yerde yapılandırma yapın ve kurulum erişim noktasına bir parola koymayı değerlendirin.



