ESP32 ve Arduino

ESP32 MQTT ile OTA Güncelleme: HTTPS Firmware Güncellemelerini Tetikleyin ve Durumu Raporlayın

ESP32 firmware güncellemelerini bir MQTT komutuyla tetikleyin, ikili dosyayı HTTPUpdate ile HTTPS üzerinden indirin ve ilerlemeyi ve sonuçları retained bir durum topic'ine yayınlayın; böylece her cihazın hangi sürümü çalıştırdığını her zaman bilin.

HTTPS indirme sunucusuyla bir ESP32 MQTT ile OTA güncelleme akışında firmware komutu alan ESP32 cihazları
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

MQTT üzerinden ESP32 OTA güncellemesi, sahadaki cihazları muhafazayı açmadan yükseltmenizi sağlar: bir komut yayınlarsınız, cihaz yeni firmware'i HTTPS üzerinden indirir ve ilerlemesini bir durum topic'inde geri bildirir. Bu eğitim, bu akışı Arduino-ESP32'nin HTTPUpdate kütüphanesiyle adım adım kurar ve gerçek donanımda kullanmadan önce ihtiyaç duyduğunuz güvenlik önlemlerini gösterir.

OTA İçin Neden MQTT ve HTTPS Birlikte?

Firmware ikili dosyasının kendisini MQTT üzerinden göndermek cazip gelebilir, ancak bu nadiren iyi bir fikirdir. Firmware imajları yüzlerce kilobayttan birkaç megabayta kadar çıkar; birçok broker ve istemci kütüphanesi mesaj boyutlarını bunun çok altında sınırlar ve bir ikili dosyayı parçalara bölmek kendi birleştirme, sıralama ve sağlama toplamı mantığınızı yazmanız demektir.

Daha temiz ayrım, her protokolün iyi olduğu işi yapmasına izin vermektir:

  • MQTT küçük kontrol mesajlarını taşır: "bu URL'den 1.4.0 sürümüne güncelle" ile ilerleme ve sonuç raporları. Cihazlar zaten bağlı olduğu için komut NAT arkasında bile anında ulaşır.
  • HTTPS büyük ikili dosyayı bir web sunucusundan veya nesne depolamadan taşır; TLS, aralık yönetimi ve önbellekleme zaten çözülmüştür.

Cihazın hiçbir zaman açık bir gelen porta ihtiyacı olmaz ve her güncelleme denemesinin canlı kaydını broker'da tutarsınız.

Topic Tasarımı

Topic Yön Payload Retained
connectmq/demo/e47b10/<device>/ota/cmd Operatör yayınlar {"version":"1.4.0","url":"https://..."} Hayır
connectmq/demo/e47b10/<device>/ota/status ESP32 yayınlar {"state":"downloading","progress":42} Evet
connectmq/demo/e47b10/<device>/info ESP32 yayınlar {"version":"1.3.2"} Evet

Durum ve info topic'lerini retained tutmak, daha sonra abone olan herhangi bir aracın (örneğin bir dashboard veya bir dağıtımı kontrol eden bir betik) her cihazın güncel sürümünü ve son OTA sonucunu anında görmesi anlamına gelir.

Ön Koşullar

  • En az 4 MB flash'a ve iki OTA uygulama yuvası olan bir bölüm şemasına sahip bir ESP32. Varsayılan Arduino şemasında bunlar vardır; "Huge APP (no OTA)" şemasında yoktur.
  • HTTPUpdate'i içeren Arduino-ESP32 core 2.x veya 3.x.
  • PubSubClient ve ArduinoJson kütüphaneleri.
  • .bin dosyanızı sunan bir HTTPS sunucusu. Herhangi bir statik dosya barındırma hizmeti çalışır.

İkili dosyayı Arduino IDE'de Sketch > Export Compiled Binary ile dışa aktarın veya PlatformIO'dan .pio/build/<env>/firmware.bin dosyasını alın.

Eksiksiz ESP32 MQTT ile OTA Güncelleme Sketch'i

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPUpdate.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#define FW_VERSION "1.3.2"

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* ALLOWED_HOST = "https://firmware.example.com/";   // only accept URLs under this prefix

// Root CA of your firmware host in PEM format
const char* FIRMWARE_CA = R"EOF(
-----BEGIN CERTIFICATE-----
...paste the root CA of your download server here...
-----END CERTIFICATE-----
)EOF";

WiFiClient mqttNet;
PubSubClient mqtt(mqttNet);
String deviceId, topicCmd, topicStatus, topicInfo;

volatile bool otaRequested = false;
String otaUrl, otaVersion;
int lastReported = -1;

void publishStatus(const char* state, int progress = -1, const char* error = nullptr) {
  JsonDocument doc;
  doc["state"] = state;
  doc["from"] = FW_VERSION;
  if (otaVersion.length()) doc["to"] = otaVersion;
  if (progress >= 0) doc["progress"] = progress;
  if (error) doc["error"] = error;
  char buf[192];
  size_t n = serializeJson(doc, buf, sizeof(buf));
  mqtt.publish(topicStatus.c_str(), (const uint8_t*)buf, n, true);
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  JsonDocument doc;
  if (deserializeJson(doc, payload, length)) return;
  String url = doc["url"] | "";
  String version = doc["version"] | "";
  if (!url.startsWith(ALLOWED_HOST) || version.length() == 0) {
    publishStatus("rejected", -1, "url or version invalid");
    return;
  }
  if (version == FW_VERSION) {
    publishStatus("up_to_date");
    return;
  }
  otaUrl = url;
  otaVersion = version;
  otaRequested = true;              // do the work in loop(), not here
}

void onProgress(int current, int total) {
  int pct = total > 0 ? (current * 100) / total : 0;
  if (pct / 10 != lastReported / 10) {   // report every 10 %
    lastReported = pct;
    publishStatus("downloading", pct);
    mqtt.loop();
  }
}

void runOta() {
  otaRequested = false;
  lastReported = -1;
  publishStatus("starting");

  WiFiClientSecure https;
  https.setCACert(FIRMWARE_CA);

  httpUpdate.rebootOnUpdate(false);
  httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
  httpUpdate.onProgress(onProgress);

  t_httpUpdate_return result = httpUpdate.update(https, otaUrl);

  switch (result) {
    case HTTP_UPDATE_OK:
      publishStatus("success", 100);
      mqtt.loop();
      delay(500);
      ESP.restart();
      break;
    case HTTP_UPDATE_NO_UPDATES:
      publishStatus("no_update");
      break;
    case HTTP_UPDATE_FAILED:
    default:
      publishStatus("failed", -1, httpUpdate.getLastErrorString().c_str());
      break;
  }
}

void connectMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect(deviceId.c_str(), topicStatus.c_str(), 1, true,
                     "{\"state\":\"offline\"}")) {
      mqtt.subscribe(topicCmd.c_str());
      String info = String("{\"version\":\"") + FW_VERSION + "\"}";
      mqtt.publish(topicInfo.c_str(), info.c_str(), true);
    } else {
      delay(3000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);

  deviceId = "esp32-ota-" + String((uint32_t)ESP.getEfuseMac(), HEX);
  String base = "connectmq/demo/e47b10/" + deviceId;
  topicCmd = base + "/ota/cmd";
  topicStatus = base + "/ota/status";
  topicInfo = base + "/info";

  mqtt.setServer("broker.connectmq.com", 1883);
  mqtt.setBufferSize(512);
  mqtt.setCallback(onMessage);
  Serial.printf("Device %s running %s\n", deviceId.c_str(), FW_VERSION);
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();
  if (otaRequested) runOta();
}

USB üzerinden ilk yüklemeden sonra seri monitörden cihaz ID'sini okuyun ve bir güncelleme tetikleyin:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-e47b10 -t 'connectmq/demo/e47b10/#' -v

mosquitto_pub -h broker.connectmq.com -p 1883 -i pc-e47b10-pub \
  -t connectmq/demo/e47b10/esp32-ota-1a2b3c4d/ota/cmd \
  -m '{"version":"1.4.0","url":"https://firmware.example.com/sensor-1.4.0.bin"}'

starting, bir dizi downloading mesajı, success ve cihaz yeniden başlayıp bağlandıktan sonra yeni sürümü içeren bir info mesajı göreceksiniz.

Parçalar Nasıl Bir Araya Gelir?

Callback yalnızca isteği kaydeder. httpUpdate.update() tüm indirme boyunca engeller. Bunu MQTT callback'i içinde çalıştırmak mqtt.loop()'u kendi içinden engeller; bu yüzden sketch bir bayrak ayarlar ve güncellemeyi loop()'un başlatmasına izin verir. Bu aynı zamanda cihazın task watchdog'u tetiklemesini önler; nedenini MQTT projelerinde ESP32 task watchdog sıfırlamaları açıklar.

İlerleme MQTT bağlantısını canlı tutar. Uzun bir indirme sırasında ilerleme callback'i her %10'da bir yayın yapar ve mqtt.loop()'u çağırır; bu da broker'ın oturumu yarı yolda düşürmemesi için keepalive ping'lerini gönderir.

rebootOnUpdate(false) size son sözü bırakır. HTTPUpdate varsayılan olarak imaj yazılır yazılmaz yeniden başlar. Bunu devre dışı bırakmak, cihazın önce success yayınlamasına ve ardından kendini yeniden başlatmasına olanak tanır.

Last Will çökmeleri raporlar. Bağlantı çağrısı durum topic'inde retained bir offline will mesajı kaydeder; böylece güncelleme ortasında ölen bir cihaz "downloading" durumunda takılı kalmış gibi değil, çevrimdışı olarak görünür. ESP32 MQTT Last Will rehberi bu kalıbı ayrıntılı olarak ele alır.

Atlamamanız Gereken Güvenlik Önlemleri

Uzaktan güncellemeler cihazınızın yapabileceği en güçlü şeydir; bu da güncelleme komutunu en cazip hedef haline getirir. Bu akış gerçek bir kuruluma yaklaşmadan önce:

  1. Üretimde OTA'yı asla public bir topic'ten tetiklemeyin. ConnectMQ test broker'ının public alanı herkes tarafından okunabilir ve yazılabilir; bu, yer tutucu URL'li demo için sorun değildir ama gerçek cihazlar için uygun değildir. Kimlik bilgileri olan özel bir broker kullanın.
  2. İndirme sunucusunu izin listesine alın. Sketch, kendi HTTPS önekinizle başlamayan her URL'yi reddeder; böylece kötü niyetli bir komut cihazı rastgele bir ikili dosyaya yönlendiremez.
  3. TLS sertifikasını doğrulayın. Firmware sunucunuzun kök CA'sını setCACert() ile yükleyin. setInsecure(), ağ yolundaki herkesin imajı değiştirmesine izin verir.
  4. İmajlarınızı imzalayın. Daha güçlü garantiler için ESP-IDF secure boot ve imzalı uygulama doğrulamayı destekler; böylece bootloader sizin imzalamadığınız firmware'i reddeder. Bu, bir ESP-IDF derlemesi veya özel bir Arduino yapılandırması gerektirir.
  5. Sürümleri kontrol edin. Zaten yüklü olan sürüme yönelik komutları yok sayın ve açıkça işaretlenmedikçe sürüm düşürmeleri reddetmeyi değerlendirin.

Bir Filoya Dağıtmak

Adım Ne yapmalı
1 Yeni ikili dosyayı sürüm numaralı bir dosya adıyla yükleyin, eskisinin üzerine asla yazmayın
2 Bir test cihazını güncelleyin ve info yeni sürümü gösterene kadar durum topic'ini izleyin
3 Küçük bir grubu güncelleyin ve offline kalan cihazlara dikkat edin
4 Geri kalanına hepsini birden değil, gruplar halinde dağıtın
5 Gerekirse geri gönderebilmek için önceki ikili dosyayı erişilebilir tutun

Komutları her cihazın abone olduğu ortak bir topic'e değil, cihaz cihaz göndermek size bu kontrolü sağlar. Cihaz ID'lerinin listesini okuyan, komutu yayınlayan ve her success mesajını bekleyen küçük bir Python veya Node.js betiği genellikle ihtiyacınız olan tek araçtır.

Sorun Giderme

  • "Not Enough Space" ile HTTP_UPDATE_FAILED: ikili dosya OTA yuvasından büyük. Daha büyük uygulama bölümlerine sahip bir bölüm şeması seçin.
  • Yönlendirmeyle birlikte "Wrong HTTP Code": bazı sunucular bir CDN'e yönlendirir. Sketch strict yönlendirmeleri izler; yüklediğiniz CA'nın son sunucuyla da eşleştiğinden emin olun.
  • TLS el sıkışması başarısız oluyor: yanlış CA veya yetersiz heap. runOta() çağırmadan önce büyük tamponları serbest bırakın.
  • Cihaz eski firmware ile yeniden başlıyor: yeni imaj açılamadı. Önce ikili dosyayı USB üzerinden test edin.
  • Broker indirme sırasında cihazı düşürüyor: ilerleme callback'i mqtt.loop()'u çağıracak kadar sık çalışmıyor. Raporlama adımını küçültün.

OTA Akışınızı ConnectMQ Üzerinde Çalıştırın

Tüm akışı broker.connectmq.com'daki herkese açık MQTT test broker'ı üzerinde test edin, ardından komut topic'lerini ConnectMQ kimlik bilgilerinin arkasına taşıyın. ConnectMQ, çok kiracılı izolasyon, verilen kimlik bilgilerine bağlı client ID'ler ve 8883 portunda TLS sunan barındırılan bir MQTT broker'dır; böylece cihazlarınıza yalnızca sizin araçlarınız güncelleme komutu gönderebilir. app.connectmq.com adresinde kredi kartı olmadan ücretsiz Serverless planında bir broker oluşturun ve bir dağıtım sırasında her cihazın durum topic'ini izlemek için canlı topic gezginini kullanın. Bunun etrafındaki eksiksiz cihaz projesi için ESP32 IoT eğitimine bakın.

Sık Sorulan Sorular

Firmware ikili dosyasının kendisini MQTT üzerinden gönderebilir miyim?

Mümkündür, ancak nadiren buna değer. İkili dosyalar büyüktür, mesaj boyutları sınırlıdır ve kendi parçalama ve doğrulama mantığınıza ihtiyaç duyarsınız. Bunun yerine MQTT üzerinden bir URL gönderin ve ikili dosyayı HTTPS üzerinden indirin.

OTA güncellemesi sırasında elektrik kesilirse ne olur?

ESP32 yeni imajı etkin olmayan OTA yuvasına yazar ve açılış bölümünü yalnızca eksiksiz ve doğrulanmış bir yazmadan sonra değiştirir. İndirme ortasında elektrik kesilmesi eski firmware'i yerinde bırakır.

Cihazım neden sürekli "downloading" gösteriyor?

Muhtemelen düzgün bir bağlantı kesme olmadan çöktü veya gücü kesildi. Durum topic'inde retained bir Last Will ile broker, keepalive zaman aşımından sonra bu durumu "offline" ile değiştirir.

OTA komutu için QoS 1 gerekli mi?

QoS 1, cihaz bağlıyken teslimatı garanti ettiği için komutlar için iyi bir seçimdir. Zaten yüklü olan sürüme yönelik komutları yok sayarak işleyiciyi idempotent hale getirin.

İlgili yazılar

ESP32 NTP MQTT Zaman Damgası: Her Mesaja Doğru Zaman Ekleyin

ESP32 saatini NTP ile eşitleyin, ISO 8601 ve epoch milisaniye zaman damgalarını biçimlendirin ve bunları MQTT payload'larına ekleyin; böylece ölçümler geç yayınlansa bile gerçek alınma zamanlarını korur.

6 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.