You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32基于ThingsBoard社区版OTA固件更新问题求助

ESP32结合ThingsBoard社区版OTA固件更新故障排查

问题现状

  • ESP32通过MQTT与ThingsBoard连接正常,能收到设备配置文件更新固件时的Payload通知,内容如下:
    {"fw_title":"OTA","fw_version":"1.4","fw_tag":"OTA 1.4","fw_size":823792,"fw_checksum_algorithm":"SHA256","fw_checksum":"a5a0c30171ae48e611efd254cd164f94427d128f57a0114d6e1eac7ba89bd7ec"}
    
  • ESP32收到通知后会提示OTA初始化,但无法获取.bin固件文件,导致更新失败。

ThingsBoard端配置

已完成边缘规则链关联根规则链、设备配置文件、设备及仪表盘的设置。

当前ESP32代码

// 打印接收的Payload
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>

// WiFi凭据
const char* ssid = "CURRENT";
const char* password = "11111111";

// ThingsBoard MQTT代理配置
const char* mqttServer = "111.93.67.82";
const int mqttPort = 7045;
const char* mqttAccessToken = "gGnF3jGV0pGp7uvuFlHz"; // 替换为你的设备访问令牌
const char* mqttTopic = "v1/devices/me/attributes";
const char* mqttPassword = ""; // 无密码则留空

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

bool otaUpdateRequested = false;

// OTA更新开始事件处理函数
void handleOTAStart() {
  Serial.println("OTA update started");
}

// OTA更新进度事件处理函数
void handleOTAProgress(unsigned int progress, unsigned int total) {
  Serial.printf("OTA update progress: %u%%\n", (progress / (total / 100)));
}

// OTA更新完成事件处理函数
void handleOTAEnd() {
  Serial.println("OTA update completed");
}

// OTA更新错误事件处理函数
void handleOTAError(ota_error_t error) {
  Serial.printf("OTA update error[%u]: ", error);
  if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
  else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
  else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
  else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
  else if (error == OTA_END_ERROR) Serial.println("End Failed");
}

void setupOTA() {
  // 设置OTA更新端口
  ArduinoOTA.setPort(3232);

  // 设置ESP32设备主机名
  ArduinoOTA.setHostname("ESP32_OTA-DEVICE");

  // 设置OTA事件处理函数
  ArduinoOTA.onStart(handleOTAStart);
  ArduinoOTA.onProgress(handleOTAProgress);
  ArduinoOTA.onEnd(handleOTAEnd);
  ArduinoOTA.onError(handleOTAError);

  // 如需密码请取消注释并设置
  // ArduinoOTA.setPassword("YOUR_OTA_PASSWORD");

  // 启动OTA服务器
  ArduinoOTA.begin();
}

void setupMQTT() {
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(mqttCallback);
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] Payload: ");
  // 打印Payload
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  
  // 检查是否为OTA更新主题
  if (strcmp(topic, "v1/devices/me/attributes") == 0) {
    if (length > 0) {
      // 此处处理接收的二进制数据
      // 简化处理,仅设置OTA更新标志
      Serial.println("OTA update requested");
      otaUpdateRequested = true;
    } else {
      Serial.println("Empty payload received");
    }
  } else {
    // 必要时处理其他主题
  }
}

void reconnectMQTT() {
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect(mqttAccessToken, mqttAccessToken, mqttPassword)) {
      Serial.println("connected");
      mqttClient.subscribe(mqttTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

// setup() 和 loop() 函数...
void setup() {
  Serial.begin(115200);
  Serial.println("Booting");

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  setupOTA();
  setupMQTT();
}

void loop() {
  if (!mqttClient.connected()) {
    reconnectMQTT();
  }
  mqttClient.loop();
  
  if (otaUpdateRequested) {
    Serial.println("Initiating OTA update...");
    otaUpdateRequested = false; // 重置标志
    ArduinoOTA.begin(); // 启动OTA更新
  }
  ArduinoOTA.handle(); // 处理OTA更新流程
  // 你的主应用代码写在这里
}

解决方案

核心问题分析

当前代码使用的ArduinoOTA是本地网络OTA服务,而ThingsBoard的OTA机制是通过HTTP/HTTPS提供固件下载链接,二者不兼容。需要修改代码,从Payload中解析固件下载URL,通过HTTP请求获取.bin文件并执行Flash更新。

具体修改步骤

  1. 添加依赖库
    引入JSON解析和HTTP客户端库,用于处理Payload和下载固件:

    #include <ArduinoJson.h>
    #include <HTTPClient.h>
    #include <Update.h>
    
  2. 修改MQTT回调函数,解析固件信息
    在回调中解析JSON Payload,提取固件下载URL、校验值和大小:

    void mqttCallback(char* topic, byte* payload, unsigned int length) {
      Serial.print("Message arrived [");
      Serial.print(topic);
      Serial.print("] Payload: ");
      for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
      }
      Serial.println();
      
      if (strcmp(topic, "v1/devices/me/attributes") == 0 && length > 0) {
        DynamicJsonDocument doc(1024);
        deserializeJson(doc, payload, length);
        
        // 提取固件关键信息
        const char* fwUrl = doc["fw_url"];
        const char* fwChecksum = doc["fw_checksum"];
        size_t fwSize = doc["fw_size"];
        
        if (fwUrl != nullptr) {
          Serial.println("Starting firmware download...");
          performOTAUpdate(fwUrl, fwChecksum, fwSize);
        } else {
          Serial.println("Firmware URL not found in payload");
        }
      }
    }
    
  3. 实现固件下载与更新逻辑
    添加performOTAUpdate函数,处理HTTP下载和Flash写入:

    void performOTAUpdate(const char* url, const char* checksum, size_t size) {
      HTTPClient http;
      http.begin(wifiClient, url);
      
      int httpCode = http.GET();
      if (httpCode == HTTP_CODE_OK) {
        if (http.getSize() == size) {
          // 初始化OTA更新
          if (!Update.begin(size)) {
            Serial.println("OTA begin failed");
            Update.printError(Serial);
            http.end();
            return;
          }
          
          // SHA256校验需自行实现,此处示例用MD5(若TB配置为MD5可直接用)
          if (checksum != nullptr && strcmp(doc["fw_checksum_algorithm"], "MD5") == 0) {
            Update.setMD5(checksum);
          }
          
          // 写入固件数据
          size_t written = Update.writeStream(http.getStream());
          if (written == size) {
            Serial.println("Firmware written successfully");
            if (Update.end(true)) { // 结束更新并自动重启
              Serial.println("OTA update completed, rebooting...");
            } else {
              Serial.println("OTA end failed");
              Update.printError(Serial);
            }
          } else {
            Serial.println("Firmware write failed");
            Update.printError(Serial);
          }
        } else {
          Serial.println("Firmware size mismatch");
        }
      } else {
        Serial.printf("HTTP request failed, code: %d\n", httpCode);
      }
      http.end();
    }
    
  4. 移除无用代码
    删除原代码中ArduinoOTA相关的所有函数、事件处理和loop中的ArduinoOTA.handle()调用,因为不再使用本地OTA服务。

ThingsBoard端补充配置

确保规则链配置正确生成包含fw_url的Payload:

  • 规则链中添加固件更新请求节点,配置生成属性更新消息,URL格式为http://<TB_HOST>:8080/api/v1/<DEVICE_ACCESS_TOKEN>/firmware
  • 设备配置文件中正确上传.bin固件,开启设备固件更新权限

内容的提问来源于stack exchange,提问作者U_S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:14:56