ESP32 HTTP OTA 本地测试教程
本教程将引导你在局域网内完成 ESP32 的 HTTP OTA(Over‑The‑Air)远程升级,实现“设备主动请求、服务器响应固件 URL、设备下载并自我更新”的完整流程。测试成功后,你可以轻松将方案扩展到公网,实现真正的远程升级。
1. 整体架构
-
ESP32 设备:定期向 Flask 服务器发送 HTTP GET 请求,携带设备 ID(MAC 地址)和当前固件版本。
-
Flask 服务器:根据设备 ID 查询“目标版本”,若目标版本高于当前版本,则返回固件下载 URL。
-
HTTP 文件服务器:提供固件文件(.bin)的下载服务。
-
流程:设备轮询 → 服务器返回更新指令 → 设备下载固件 → 设备烧录并重启 → 设备运行新固件。
2. 准备工作
硬件
-
ESP32 开发板(如 ESP32‑DevKitC) ×1
-
USB 数据线(用于首次烧录和调试)
-
电脑(Windows / macOS / Linux)与 ESP32 连接在同一路由器/局域网
软件
-
Arduino IDE(已安装 ESP32 开发板支持包)
-
Python 3.x(用于运行 Flask 服务和 HTTP 文件服务器)
-
库安装(在 Arduino IDE 中安装
ArduinoJson库,用于解析 JSON)
3. 设备端代码(ESP32)
创建新 Arduino 项目,复制以下代码。注意:首次烧录需通过 USB 线,后续升级可通过 OTA。
#include <WiFi.h>
#include <HTTPClient.h>
#include <Update.h>
#include <ArduinoJson.h>
// ========== 配置区 ==========
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
// 设备唯一标识(使用MAC地址)
String device_id = WiFi.macAddress();
// 当前固件版本(语义化版本,如 "1.0.0")
String current_version = "1.0.0";
// 检查更新的API地址(填写你的电脑IP和端口)
const char* check_url = "http://192.168.x.x:8080/check_update";
// 检查间隔(毫秒),测试时可设为30秒,正式使用建议1小时以上
const unsigned long check_interval = 30000; // 30秒,仅测试
// ==========================
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Device ID: ");
Serial.println(device_id);
}
void loop() {
static unsigned long last_check = 0;
if (millis() - last_check > check_interval) {
last_check = millis();
checkForUpdate();
}
// 你的其他业务代码(不要用长时间delay阻塞OTA)
}
void checkForUpdate() {
HTTPClient http;
String url = String(check_url) + "?device_id=" + device_id + "&version=" + current_version;
Serial.print("Checking update: ");
Serial.println(url);
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
Serial.println("Response: " + payload);
// 解析JSON
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
bool update = doc["update"];
if (update) {
String firmware_url = doc["url"].as<String>();
Serial.println("Firmware URL: " + firmware_url);
performOTA(firmware_url);
} else {
Serial.println("No update available.");
}
} else {
Serial.println("JSON parse failed");
}
} else {
Serial.printf("HTTP GET failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
void performOTA(String url) {
HTTPClient http;
http.setTimeout(10000); // 10秒超时
http.begin(url);
int httpCode = http.GET();
if (httpCode != 200) {
Serial.printf("Download failed, HTTP code: %d\n", httpCode);
http.end();
return;
}
int contentLength = http.getSize();
if (contentLength <= 0) {
Serial.println("Invalid content length");
http.end();
return;
}
Serial.printf("Starting OTA, size: %d bytes\n", contentLength);
if (!Update.begin(contentLength)) {
Serial.println("Not enough space to begin OTA");
http.end();
return;
}
size_t written = Update.writeStream(http.getStream());
if (written == contentLength) {
Serial.println("Written : " + String(written) + " bytes successfully");
if (Update.end()) {
Serial.println("OTA done, restarting...");
ESP.restart();
} else {
Serial.println("Error in Update.end()");
}
} else {
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?");
}
http.end();
}
关键点说明
-
使用
WiFi.macAddress()作为设备唯一标识,服务器据此区分不同设备。 -
当前版本号
current_version与服务器目标版本比较,决定是否更新。 -
使用
ArduinoJson库解析服务器返回的 JSON,避免字符串匹配的脆弱性。 -
performOTA()函数负责下载固件并写入 Flash,内置超时和错误处理。
4. 服务端代码(Flask)
在电脑上创建文件 ota_server.py,内容如下:
from flask import Flask, request, jsonify
app = Flask(__name__)
# 模拟数据库:设备ID -> 目标版本号
# 从串口监视器获取ESP32的MAC地址,替换为实际值
target_versions = {
"1C:69:20:2B:A7:AC": "2.0.0", # 示例:将该设备目标版本设为2.0.0
# 其他设备可继续添加
}
# 固件下载的基础URL(使用电脑IP和8000端口)
FIRMWARE_BASE_URL = "http://192.168.x.x:8000/" # 替换为你的电脑IP
@app.route('/check_update')
def check_update():
device_id = request.args.get('device_id')
current_ver = request.args.get('version')
print(f"Device: {device_id}, current: {current_ver}")
target = target_versions.get(device_id)
if target and target != current_ver:
firmware_url = FIRMWARE_BASE_URL + "firmware_" + target + ".bin"
return jsonify({"update": True, "url": firmware_url})
else:
return jsonify({"update": False})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)
说明
-
target_versions字典存储每个设备的“目标版本”。当设备上报的版本与目标版本不一致时,即触发更新。 -
固件 URL 按版本号拼接,例如
firmware_2.0.0.bin,请确保实际文件名与此一致。 -
Flask 监听所有网络接口(
0.0.0.0),端口8080。
5. 固件准备与文件服务
生成固件文件(.bin)
-
在 Arduino IDE 中打开你的项目(注意:固件中必须包含上述 OTA 代码)。
-
选择
项目→导出已编译的二进制文件。 -
项目文件夹下会生成
.bin文件,如ESP32_V3_02.ino.bin。 -
将文件重命名为
firmware_2.0.0.bin(版本号与服务器目标版本一致),并放入一个目录,例如C:\Users\YourName\Desktop\OTA\firmware\。
启动 HTTP 文件服务器
打开命令提示符(或终端),进入存放 .bin 文件的目录,执行:
python -m http.server 8000
此命令会在 http://你的电脑IP:8000 提供文件下载服务。
注意:电脑的防火墙需允许 8000 端口入站,否则 ESP32 无法下载固件。
6. 运行测试
-
启动 Flask 服务
在命令行中进入ota_server.py所在目录,执行:python ota_server.py看到类似
Running on http://0.0.0.0:8080的输出即表示服务启动。 -
启动 HTTP 文件服务器(如果还未启动)
在另一个命令行窗口,进入固件目录,执行:python -m http.server 8000 -
确认电脑 IP 地址
在命令行输入ipconfig(Windows)或ifconfig(Linux/macOS),找到当前网络适配器的 IPv4 地址,例如192.168.1.100。
确保 ESP32 的 WiFi 连接与电脑在同一局域网。 -
修改 ESP32 代码中的 IP
将check_url和FIRMWARE_BASE_URL中的 IP 改为你的电脑实际 IP,例如:const char* check_url = "http://192.168.1.100:8080/check_update";然后通过 USB 线将代码烧录到 ESP32。
-
观察串口监视器
打开 Arduino IDE 的串口监视器(波特率 115200),ESP32 启动后会连接 WiFi,并每隔 30 秒检查一次更新。
若target_versions中设备的版本与当前版本不同,则会触发 OTA 下载并重启。预期输出类似:
WiFi connected IP address: 192.168.1.50 Device ID: 1C:69:20:2B:A7:AC Checking update: http://192.168.1.100:8080/check_update?device_id=1C:69:20:2B:A7:AC&version=1.0.0 Response: { "update": true, "url": "http://192.168.1.100:8000/firmware_2.0.0.bin" } Firmware URL: http://192.168.1.100:8000/firmware_2.0.0.bin Starting OTA, size: 989232 bytes Written : 989232 bytes successfully OTA done, restarting... -
升级后验证
设备重启后,串口监视器应显示current_version变为2.0.0,且再次检查时服务器返回{"update":false}。
7. 常见问题排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 设备无法连接 WiFi | SSID/密码错误,或路由器信号弱 | 检查代码中的 WiFi 凭据,确保设备靠近路由器 |
| 检查更新时无响应或超时 | 电脑 IP 错误,或防火墙阻挡 8080 端口 | 确认电脑 IP 不变(可设为静态),暂时关闭防火墙测试 |
| 收到 JSON 但显示“No update available.” | JSON 解析失败(字符串匹配问题) | 确保使用了 ArduinoJson 库,并正确解析 doc["update"] |
下载固件时输出 Written only : 0/xxx |
ESP32 与电脑不在同一网段,或 8000 端口被防火墙拦截 | 检查 ESP32 的 IP 与电脑 IP 是否同网段;检查防火墙规则 |
| 下载后设备反复重启 | 新固件有 bug 或缺少 OTA 代码 | 回退到旧固件(USB 烧录),并检查新固件是否包含 ArduinoOTA.handle() 或 OTA 检查代码 |
8. 下一步:扩展到公网
本地测试成功后,你可以:
-
将 Flask 服务部署到具有公网 IP 的云服务器;
-
将固件上传至对象存储(如阿里云 OSS)获取公网 URL;
-
修改 ESP32 代码中的
check_url为公网地址; -
为服务添加基本安全措施(如 HTTPS、API Token)。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)