ESP32 OTA升级实战指南
这篇代码展示了一个完整的ESP32 OTA(Over-The-Air)固件升级系统实现方案。主要包括以下功能: WiFi连接管理:提供WiFi配置和连接功能 固件版本检查:通过HTTP请求检查服务器上的新版本 OTA升级执行:从指定URL下载并安装新固件 进度显示:打印升级进度和设备信息 系统每60秒自动检查一次更新,当发现新版本时会自动下载并安装。代码中包含了详细的注释和错误处理,支持HTTP/
/*
- ESP32 OTA (Over-The-Air) 固件升级完整示例
- 功能:通过HTTP服务器下载新固件并更新ESP32
- 作者:示例代码
- 日期:2025
*/
#include <WiFi.h> // WiFi库,用于连接无线网络
#include <HTTPClient.h> // HTTP客户端库,用于下载固件
#include <Update.h> // ESP32的OTA更新库
#include <WiFiClientSecure.h> // HTTPS安全连接库(如果需要)
// ==================== 配置参数 ====================
// WiFi配置
const char* ssid = “你的WiFi名称”; // 替换为你的WiFi SSID
const char* password = “你的WiFi密码”; // 替换为你的WiFi密码
// OTA固件服务器配置
const char* firmwareUrl = “http://192.168.1.100:8080/firmware.bin”; // 固件下载地址
const char* firmwareVersion = “1.0.0”; // 当前固件版本号
// OTA升级检查间隔(毫秒)
const unsigned long checkInterval = 60000; // 每60秒检查一次更新
unsigned long lastCheck = 0; // 上次检查时间戳
// ==================== 函数声明 ====================
void connectWiFi(); // 连接WiFi函数
void performOTAUpdate(); // 执行OTA升级函数
bool checkForUpdate(); // 检查是否有新版本
void printProgress(size_t progress, size_t total); // 打印升级进度
// ==================== 初始化设置 ====================
void setup() {
// 初始化串口通信,波特率115200
Serial.begin(115200);
// 等待串口就绪
delay(1000);
// 打印启动信息
Serial.println();
Serial.println(“=“);
Serial.println(“ESP32 OTA 固件升级系统”);
Serial.printf(“当前固件版本: %s\n”, firmwareVersion);
Serial.println(”=”);
// 连接到WiFi网络
connectWiFi();
// 打印ESP32的一些基本信息
Serial.println(“\n— 设备信息 —”);
Serial.printf(“芯片型号: %s\n”, ESP.getChipModel());
Serial.printf(“芯片核心数: %d\n”, ESP.getChipCores());
Serial.printf(“CPU频率: %d MHz\n”, ESP.getCpuFreqMHz());
Serial.printf(“Flash大小: %d MB\n”, ESP.getFlashChipSize() / (1024 * 1024));
Serial.printf(“可用堆内存: %d bytes\n”, ESP.getFreeHeap());
Serial.printf(“固件大小: %d bytes\n”, ESP.getSketchSize());
Serial.printf(“剩余OTA空间: %d bytes\n”, ESP.getFreeSketchSpace());
Serial.println(“-------------------\n”);
}
// ==================== 主循环 ====================
void loop() {
// 检查WiFi连接状态
if (WiFi.status() != WL_CONNECTED) {
Serial.println(“WiFi断开,尝试重新连接…”);
connectWiFi();
}
// 定时检查OTA更新
// 使用millis()避免delay()阻塞
if (millis() - lastCheck >= checkInterval) {
lastCheck = millis(); // 更新检查时间戳
Serial.println("\n[OTA] 检查固件更新...");
// 检查是否有新版本可用
if (checkForUpdate()) {
Serial.println("[OTA] 发现新版本,开始更新...");
performOTAUpdate(); // 执行OTA升级
} else {
Serial.println("[OTA] 当前已是最新版本");
}
}
// 这里可以添加你的主要业务逻辑
// 例如:读取传感器、控制设备等
delay(1000); // 延迟1秒,避免过快循环
}
// ==================== WiFi连接函数 ====================
void connectWiFi() {
Serial.println(“\n[WiFi] 开始连接WiFi…”);
Serial.printf(“[WiFi] SSID: %s\n”, ssid);
// 设置WiFi为Station模式(客户端模式)
WiFi.mode(WIFI_STA);
// 开始连接WiFi
WiFi.begin(ssid, password);
// 等待连接,最多等待20秒
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) {
delay(1000); // 每秒检查一次
Serial.print(“.”); // 打印连接进度点
timeout++;
}
// 检查连接结果
if (WiFi.status() == WL_CONNECTED) {
Serial.println(“\n[WiFi] 连接成功!”);
Serial.printf(“[WiFi] IP地址: %s\n”, WiFi.localIP().toString().c_str());
Serial.printf(“[WiFi] 信号强度: %d dBm\n”, WiFi.RSSI());
} else {
Serial.println(“\n[WiFi] 连接失败!”);
Serial.println(“[WiFi] 请检查WiFi名称和密码是否正确”);
}
}
// ==================== 检查更新函数 ====================
bool checkForUpdate() {
// 创建HTTP客户端对象
HTTPClient http;
// 构建版本检查URL(假设服务器有version.txt文件)
String versionUrl = String(firmwareUrl);
versionUrl.replace(“firmware.bin”, “version.txt”);
// 开始HTTP GET请求
http.begin(versionUrl);
int httpCode = http.GET(); // 发送GET请求
// 检查HTTP响应码
if (httpCode == HTTP_CODE_OK) {
// 读取服务器返回的版本号
String newVersion = http.getString();
newVersion.trim(); // 去除首尾空白字符
Serial.printf("[OTA] 服务器版本: %s\n", newVersion.c_str());
Serial.printf("[OTA] 当前版本: %s\n", firmwareVersion);
// 结束HTTP连接
http.end();
// 比较版本号(简单字符串比较)
// 实际应用中建议使用更严格的版本号比较逻辑
if (newVersion != firmwareVersion) {
return true; // 有新版本
}
} else {
Serial.printf(“[OTA] 版本检查失败,HTTP错误码: %d\n”, httpCode);
http.end();
}
return false; // 无新版本或检查失败
}
// ==================== OTA升级执行函数 ====================
void performOTAUpdate() {
Serial.println(“\n========== 开始OTA升级 ==========”);
// 创建WiFi客户端和HTTP客户端对象
WiFiClient client;
HTTPClient http;
// 设置HTTP超时时间(毫秒)
http.setTimeout(15000);
// 开始HTTP连接
Serial.printf(“[OTA] 连接到: %s\n”, firmwareUrl);
http.begin(client, firmwareUrl);
// 发送GET请求获取固件文件
int httpCode = http.GET();
// 检查HTTP响应码
if (httpCode == HTTP_CODE_OK) {
// 获取固件文件大小
int contentLength = http.getSize();
Serial.printf(“[OTA] 固件大小: %d bytes (%.2f KB)\n”,
contentLength, contentLength / 1024.0);
// 检查是否有足够的空间存储新固件
if (contentLength > 0) {
// 检查剩余OTA分区空间
if (contentLength > ESP.getFreeSketchSpace()) {
Serial.println("[OTA] 错误: OTA分区空间不足!");
http.end();
return;
}
// 获取HTTP流对象
WiFiClient* stream = http.getStreamPtr();
// 开始OTA更新过程
// Update.begin()参数:
// - contentLength: 固件大小
// - U_FLASH: 更新Flash区域(固件)
// - LED_BUILTIN: 指定LED引脚,更新时会闪烁
// - LOW: LED点亮的电平
if (!Update.begin(contentLength, U_FLASH)) {
Serial.println("[OTA] 错误: 无法开始OTA更新");
Update.printError(Serial); // 打印详细错误信息
http.end();
return;
}
Serial.println("[OTA] 开始下载固件...");
// 创建缓冲区用于接收数据
uint8_t buff[128] = { 0 };
size_t written = 0; // 已写入字节数
// 循环读取并写入固件数据
while (http.connected() && written < contentLength) {
// 获取可用数据大小
size_t available = stream->available();
if (available) {
// 读取数据到缓冲区
// 读取大小为缓冲区大小和可用数据的较小值
int c = stream->readBytes(buff,
((available > sizeof(buff)) ? sizeof(buff) : available));
// 将数据写入Flash
size_t bytesWritten = Update.write(buff, c);
if (bytesWritten != c) {
Serial.println("[OTA] 错误: 写入数据失败");
break;
}
written += bytesWritten; // 累加已写入字节数
// 打印进度(每10KB打印一次)
if (written % (1024 * 10) == 0 || written == contentLength) {
printProgress(written, contentLength);
}
}
delay(1); // 小延迟,让系统稳定
}
Serial.println(); // 换行
// 检查是否完整下载
if (written == contentLength) {
Serial.printf("[OTA] 下载完成,共 %d bytes\n", written);
} else {
Serial.printf("[OTA] 警告: 下载不完整 (%d / %d bytes)\n",
written, contentLength);
}
// 完成OTA更新
if (Update.end()) {
// 验证更新是否成功
if (Update.isFinished()) {
Serial.println("[OTA] ✓ 更新成功!");
Serial.println("[OTA] 3秒后重启设备...");
delay(3000);
// 重启ESP32以应用新固件
ESP.restart();
} else {
Serial.println("[OTA] ✗ 更新未完成");
Update.printError(Serial);
}
} else {
Serial.println("[OTA] ✗ 更新失败");
Update.printError(Serial);
}
} else {
Serial.println("[OTA] 错误: 固件大小无效");
}
} else if (httpCode == HTTP_CODE_NOT_MODIFIED) {
Serial.println(“[OTA] 固件未修改(304)”);
} else {
Serial.printf(“[OTA] HTTP错误码: %d\n”, httpCode);
}
// 关闭HTTP连接
http.end();
Serial.println(“========== OTA升级结束 ==========\n”);
}
// ==================== 进度打印函数 ====================
void printProgress(size_t progress, size_t total) {
// 计算百分比
int percent = (progress * 100) / total;
// 打印进度条
Serial.printf(“[OTA] 进度: %d%% (%d / %d bytes)\r”,
percent, progress, total);
// 如果完成,打印换行
if (progress == total) {
Serial.println();
}
}
/*
- ==================== 使用说明 ====================
-
- 准备工作:
-
- 修改WiFi SSID和密码
-
- 准备HTTP文件服务器(可以用Python: python -m http.server 8080)
-
- 编译生成.bin固件文件(Arduino IDE: 草图 -> 导出已编译的二进制文件)
-
- 服务器文件结构:
-
- firmware.bin (新固件文件)
-
- version.txt (版本号文件,如: 1.0.1)
-
- 分区表要求:
- ESP32必须使用支持OTA的分区表,Arduino IDE中选择:
- 工具 -> Partition Scheme -> “Minimal SPIFFS (1.9MB APP with OTA)”
- 或其他带OTA的分区方案
-
- 测试流程:
-
- 上传此代码到ESP32(版本1.0.0)
-
- 修改代码中的版本号为1.0.1
-
- 重新编译生成新的firmware.bin
-
- 将firmware.bin和version.txt(内容为1.0.1)放到HTTP服务器
-
- ESP32会自动检测并下载更新
-
- 安全建议:
-
- 生产环境使用HTTPS(需要WiFiClientSecure和证书)
-
- 添加固件签名验证
-
- 实现回滚机制
-
- 添加固件MD5校验
-
- 常见问题:
-
- “OTA分区空间不足”: 选择更大的APP分区或减小程序大小
-
- “更新失败”: 检查固件文件是否完整、网络是否稳定
-
- “无法开始OTA”: 检查分区表是否支持OTA
- ==================== 高级功能扩展 ====================
- 可以添加的功能:
-
- HTTPS支持(安全传输)
-
- 固件MD5校验(防篡改)
-
- 分段下载(支持大文件)
-
- 断点续传(网络中断恢复)
-
- 回滚功能(更新失败恢复旧版本)
-
- 远程触发更新(MQTT/HTTP API)
-
- 更新进度Web界面
-
- 批量设备管理
*/
- 批量设备管理
更多推荐



所有评论(0)