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更新。
具体修改步骤
添加依赖库
引入JSON解析和HTTP客户端库,用于处理Payload和下载固件:#include <ArduinoJson.h> #include <HTTPClient.h> #include <Update.h>修改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"); } } }实现固件下载与更新逻辑
添加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(); }移除无用代码
删除原代码中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
相关产品推荐
相关产品推荐

