咨询ESP32如何通过HTTP multipart/form-data实现OTA固件接收
ESP32 OTA 基于 HTTP multipart/form-data 实现方案
一、HTTPClient.h 是否支持 multipart/form-data?
ESP32 原生的 HTTPClient.h 没有封装 multipart/form-data 的现成解析/构建逻辑,但完全可以手动拼接请求、解析响应实现需求。如果嫌手动处理麻烦,推荐用 ESPAsyncWebServer 或带扩展的 ArduinoHttpClient,能大幅减少重复代码。
二、接收 multipart/form-data 固件的通用流程
- 解析服务器响应头,提取
Content-Type中的boundary分隔符(比如----WebKitFormBoundary7MA4YWxkTrZu0gW) - 逐字节读取响应体,用 boundary 分割各个表单块
- 识别包含固件文件的块(通常带有
filename参数),跳过块头的描述信息,提取核心文件内容 - 将文件内容写入 ESP32 的 OTA 分区(用官方
Update类) - 验证写入的固件完整性,成功后重启设备
三、代码示例(ESP32 作为客户端接收固件)
#include <WiFi.h> #include <HTTPClient.h> #include <Update.h> const char* ssid = "你的WiFi名称"; const char* password = "你的WiFi密码"; const char* updateServerUrl = "http://你的服务器地址/ota/update"; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi 已连接"); // 步骤1:检查是否有更新 if (checkForUpdate()) { Serial.println("检测到可用更新,开始下载固件..."); // 步骤3-5:请求并接收固件,完成OTA更新 if (downloadAndUpdateFirmware()) { Serial.println("更新完成,即将重启设备"); ESP.restart(); } else { Serial.println("更新失败,请检查服务器或网络"); } } else { Serial.println("当前已是最新版本"); } } // 检查更新逻辑:向服务器发送当前版本,获取更新状态 bool checkForUpdate() { HTTPClient http; bool hasUpdate = false; if (http.begin(updateServerUrl)) { http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // 替换成你的版本号和请求参数 int httpCode = http.POST("action=check¤t_ver=1.0.0"); if (httpCode == HTTP_CODE_OK) { String resp = http.getString(); // 假设服务器返回"YES"表示有更新,可根据实际调整 if (resp.trim() == "YES") { hasUpdate = true; } } http.end(); } return hasUpdate; } // 下载并写入固件:解析multipart响应,写入OTA分区 bool downloadAndUpdateFirmware() { HTTPClient http; bool updateSuccess = false; if (http.begin(updateServerUrl)) { http.addHeader("Content-Type", "application/x-www-form-urlencoded"); int httpCode = http.POST("action=download"); // 请求服务器返回固件 if (httpCode == HTTP_CODE_OK) { // 从响应头提取multipart分隔符 String contentType = http.header("Content-Type"); int boundaryIdx = contentType.indexOf("boundary="); if (boundaryIdx == -1) { Serial.println("未找到multipart分隔符,无法解析"); http.end(); return false; } String boundary = "--" + contentType.substring(boundaryIdx + 9); WiFiClient* stream = http.getStreamPtr(); bool isFileBlock = false; size_t writtenBytes = 0; String tempLine = ""; // 初始化OTA更新 if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { Update.printError(Serial); http.end(); return false; } // 逐字节解析multipart数据 while (stream->connected() && (http.connected() || stream->available())) { if (stream->available()) { char c = stream->read(); tempLine += c; // 检测到分隔符,切换块状态 if (tempLine.endsWith(boundary)) { tempLine = ""; isFileBlock = false; } // 当前处于文件块,写入OTA分区 else if (isFileBlock) { if (Update.write(&c, 1) != 1) { Update.printError(Serial); http.end(); return false; } writtenBytes++; } // 检测到固件文件块的标识,准备开始写入 else if (tempLine.indexOf("filename=\"firmware.bin\"") != -1) { isFileBlock = true; // 跳过块头的空行,定位到文件内容起始位置 while (stream->available()) { char tempChar = stream->read(); if (tempChar == '\n' && stream->peek() == '\n') { stream->read(); break; } } tempLine = ""; } } } // 结束OTA更新并验证 if (Update.end(true)) { Serial.printf("固件写入完成,总大小:%d bytes\n", writtenBytes); updateSuccess = true; } else { Update.printError(Serial); } } else { Serial.printf("请求固件失败,HTTP状态码:%d\n", httpCode); } http.end(); } return updateSuccess; } void loop() { // 空循环,OTA完成后会重启 }
四、实用库推荐
- ESPAsyncWebServer:不管是作为客户端解析multipart响应,还是作为服务器接收OTA请求,这个库都内置了完善的multipart处理工具类,能省去手动解析的繁琐工作。
- ArduinoHttpClient:部分版本扩展了multipart表单的构建与解析方法,比原生HTTPClient更易用,注意选择适配ESP32的版本。
五、OTA流程优化建议
- 版本校验:在检查更新时,务必带上当前固件的版本号(如MD5或语义化版本),避免无效的下载请求。
- 固件完整性校验:让服务器返回固件的SHA256/MD5值,ESP32下载完成后自行校验,防止固件损坏导致设备变砖。
- 异常重试:添加网络断开、写入失败等场景的重试逻辑,提升OTA的可靠性。
内容的提问来源于stack exchange,提问作者rnjsrkdmf
相关产品推荐
相关产品推荐

