文章目录
- 1.什么是 OTA
- 2. ESP32cam HTTP_OTA 本地准备
- 2.1 HTTP OTA 升级原理
- 2.2 开发板本地基准程序(程序版本:1_0_0)
- 2.3 开发板升级程序(程序版本:1_0_1)
- 2.4 本地 HTTP_OTA 升级测试
- 2.4.1 本地运行一个 HTTP 服务
- 2.4.2 替换远程链接并将要升级的程序打包成 `.bin` 文件
- 2.4.3 替换远程链接并烧录基准程序(版本为:1_0_0 的程序)测试升级是否成功
 
 
- 3. HTTP_OTA 升级展望
- 3.1 后期版本更新可通过 HTTP_OTA 实现
- 3.2 借助网络云平台实现远程 HTTP_OTA 升级
 
本教程是 ESP32cam 的系列教程之三,使用 Arduino IDE 对 ESP32cam 开发板进行开发。
本教程代码同样使用与其他 ESP32 开发板。
1.什么是 OTA
OTA 即空中下载技术(Over-the-Air Technology),其可以安全方便地升级设备的固件或软件。远程升级还可以大大降低成本,节省资源,它已成为物联网设备和产品制造商的关键技术之一。
ESP32 开发板支持 3 种 OTA 方式:
- Arduino IDE :主要用于软件开发阶段,实现不接线固件烧录
- Web_OTA:通过 Web 浏览器手动提供应用程序更新模块
- HTTP_OTA:固件存放到 http 服务器端,设备自动判断是否需要联网下载固件升级
本文主要介绍:HTTP_OTA 的原理与实现。
2. ESP32cam HTTP_OTA 本地准备
2.1 HTTP OTA 升级原理
- 本地程序在开机连接 WIFI 后发送 http 请求获取远程服务器中的升级 json 文件。
- 通过对比 json 中的远程版本信息与本地的版本信息判断是否一致。
- 若远程版本信息与本地版本不一致,则本地需要更新程序。
- 通过 json 中的版本信息在远程服务器中拉取需要更新的程序的 .bin文件。
- 依据下载下来的 .bin自动完成版本的升级,然后自动重启开发板。
- 重复第一步获取远程 json 文件判断是否需要更新。
2.2 开发板本地基准程序(程序版本:1_0_0)
本地 1_0_0 版本程序主要内容如下:
- 当前版本(非常重要,升级依据)
- 远程升级的 json 链接与远程固件的文件夹链接
- 获取并解析 json 的函数 httpGETRequest
- 依据 json 判断是否需要更新的函数 isOrNotNeedUpdate
- 以及其他基础信息组成
#include <WiFi.h>
#include <HTTPClient.h>
#include <ESP32httpUpdate.h>
#include <Arduino_JSON.h>
/**********根据实际修改**********/
const char* wifi_ssid = "TP-LINK_1760";   // WIFI名称,区分大小写,不要写错
const char* wifi_password = "987654321";  // WIFI密码
// 特别重要,升级依据!!!
// 设置当前代码版本 格式 1_0_0
char* version = "1_0_0";
//远程固件链接,只支持http
const char* baseUpdateUrl = "http://example.cn/esp32/";
const char* updateJson = "http://example.cn/esp32/esp32_update.json";
// esp32_update.json
// {
//     "version":"1_0_1"
// }
/**********根据实际修改**********/
int need_ota_update = 0;
int i = 0;
String jsonBuffer;
// 获取远程 json 升级文件
String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
  String payload = "";
  //连接目标网址
  http.begin(client, serverName);
  //发送HTTP站点请求
  int httpCode = http.GET();
  if (httpCode > 0) {
    Serial.printf("[HTTP] GET... code: %d\n", httpCode);
    payload = http.getString();
  } else {
    Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();  //关闭连接
  //返回获得的数据用于Json处理
  return payload;
}
// 依据json文件中版本号与本地版本号,判断是否需要进行更新
void isOrNotNeedUpdate(){
  // 获取远程的升级 json ,判断内部版本与本地是否相同,判断是否需要升级
  jsonBuffer = httpGETRequest(updateJson);
  Serial.println(jsonBuffer);
  //将解析的Json对象值储存在Jsonu缓冲区中
  JSONVar myObject = JSON.parse(jsonBuffer);
  Serial.println(myObject);
  // Serial.println(myObject["version"]);
  const char* ota_version = myObject["version"];
  // Serial.println(ota_version);
  Serial.println("---");
  Serial.print("远程版本: ");
  Serial.println(ota_version);
  Serial.print("本地版本: ");
  Serial.println(version);
  // char * 与 const char * 比较
  // 判断远程版本与本地版本是否相同
  if (String(version) == String(ota_version)) {
    need_ota_update = 0;
    Serial.println("无需升级。。。");
  } else {
    need_ota_update = 1;
    Serial.println("需要升级。。。");
    Serial.print("OTA 升级地址为:");
    // 升级的完整链接, 例如:http://example.cn/esp32/esp32_1_0_1.bin
    String fullUpdateUrl = String(baseUpdateUrl) + "esp32_" + ota_version + ".bin";
    Serial.println(String(fullUpdateUrl));
    // 获取远程 bin 文件进行升级
    t_httpUpdate_return ret = ESPhttpUpdate.update(fullUpdateUrl);
    Serial.println(ret);
    switch (ret) {
      case HTTP_UPDATE_FAILED:
        Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
        break;
      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("HTTP_UPDATE_NO_UPDATES");
        break;
      case HTTP_UPDATE_OK:
        Serial.println("HTTP_UPDATE_OK");
        break;
      default:
        Serial.println(ret);
    }
    // version=(char *)ota_version;
  }
  need_ota_update = 0;
}
void setup() {
  Serial.begin(115200);  //波特率115200
  Serial.print("Connection WIFI");
  WiFi.begin(wifi_ssid, wifi_password);    //连接wifi
  while (WiFi.status() != WL_CONNECTED) {  //等待连接wifi
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  // 调用判断是否需要升级函数
  isOrNotNeedUpdate();
}
void loop() {
// 主程序
  Serial.println(i);
  i++;
  delay(2000);
}
2.3 开发板升级程序(程序版本:1_0_1)
本测试升级程序如下,仅仅在程序版本与主程序中做了调整,以便更清楚的看出是否OTA升级成功。
#include <WiFi.h>
#include <HTTPClient.h>
#include <ESP32httpUpdate.h>
#include <Arduino_JSON.h>
/**********根据实际修改**********/
const char* wifi_ssid = "TP-LINK_1760";   // WIFI名称,区分大小写,不要写错
const char* wifi_password = "987654321";  // WIFI密码
// 特别重要,升级依据!!!
// 设置当前代码版本 格式 1_0_0
char* version = "1_0_1";
//远程固件链接,只支持http
const char* baseUpdateUrl = "http://example.cn/esp32/";
const char* updateJson = "http://example.cn/esp32/esp32_update.json";
// esp32_update.json
// {
//     "version":"1_0_1"
// }
/**********根据实际修改**********/
int need_ota_update = 0;
int i = 0;
String jsonBuffer;
// 获取远程 json 升级文件
String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
  String payload = "";
  //连接目标网址
  http.begin(client, serverName);
  //发送HTTP站点请求
  int httpCode = http.GET();
  if (httpCode > 0) {
    Serial.printf("[HTTP] GET... code: %d\n", httpCode);
    payload = http.getString();
  } else {
    Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();  //关闭连接
  //返回获得的数据用于Json处理
  return payload;
}
// 依据json文件中版本号与本地版本号,判断是否需要进行更新
void isOrNotNeedUpdate(){
  // 获取远程的升级 json ,判断内部版本与本地是否相同,判断是否需要升级
  jsonBuffer = httpGETRequest(updateJson);
  Serial.println(jsonBuffer);
  //将解析的Json对象值储存在Jsonu缓冲区中
  JSONVar myObject = JSON.parse(jsonBuffer);
  Serial.println(myObject);
  // Serial.println(myObject["version"]);
  const char* ota_version = myObject["version"];
  // Serial.println(ota_version);
  Serial.println("---");
  Serial.print("远程版本: ");
  Serial.println(ota_version);
  Serial.print("本地版本: ");
  Serial.println(version);
  // char * 与 const char * 比较
  // 判断远程版本与本地版本是否相同
  if (String(version) == String(ota_version)) {
    need_ota_update = 0;
    Serial.println("无需升级。。。");
  } else {
    need_ota_update = 1;
    Serial.println("需要升级。。。");
    Serial.print("OTA 升级地址为:");
    // 升级的完整链接, 例如:http://example.cn/esp32/esp32_1_0_1.bin
    String fullUpdateUrl = String(baseUpdateUrl) + "esp32_" + ota_version + ".bin";
    Serial.println(String(fullUpdateUrl));
    // 获取远程 bin 文件进行升级
    t_httpUpdate_return ret = ESPhttpUpdate.update(fullUpdateUrl);
    Serial.println(ret);
    switch (ret) {
      case HTTP_UPDATE_FAILED:
        Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
        break;
      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("HTTP_UPDATE_NO_UPDATES");
        break;
      case HTTP_UPDATE_OK:
        Serial.println("HTTP_UPDATE_OK");
        break;
      default:
        Serial.println(ret);
    }
    // version=(char *)ota_version;
  }
  need_ota_update = 0;
}
void setup() {
  Serial.begin(115200);  //波特率115200
  Serial.print("Connection WIFI");
  WiFi.begin(wifi_ssid, wifi_password);    //连接wifi
  while (WiFi.status() != WL_CONNECTED) {  //等待连接wifi
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  // 调用判断是否需要升级函数
  isOrNotNeedUpdate();
}
void loop() {
// 主程序
  Serial.println(i);
  Serial.println("OTA 升级成功");
  i++;
  delay(2000);
}
2.4 本地 HTTP_OTA 升级测试
2.4.1 本地运行一个 HTTP 服务
这里使用 vscode 进行:
- 用 vscode 打开一个空白文件夹
- 在文件夹中新建目录 esp32,文件index.html,目录下新建文件esp32_update.json
- esp32_update.json中内容是- {"version":"1_0_1"},表明当前远程的版本为- 1_0_1
- index.html中为标准html结构文件
- 在 index.html界面中右键>Open with Live Server 打开

- 替换 127.0.0.1为本地192.168.1.XXX并拼接/esp32/esp32_update.json,如下图所示

2.4.2 替换远程链接并将要升级的程序打包成 .bin 文件
 
-  将 arduino IDE 中的程序的远程链接替换成本地 HTTP 服务器链接 
  
-  工具中 开发板和 Partition Scheme选择如下图:
  
-  项目中选择导出已编译的二进制文件,导出的二进制文件在同级目录下。 
  
-  将导出的 .bin文件重命名为esp32_1_0_x.bin样式,并复制到 2.4.1 节中的 esp32目录中,保证使用http://192.168.1.x/esp32/esp32_1_0_x.bin能够下载到该文件。
  
2.4.3 替换远程链接并烧录基准程序(版本为:1_0_0 的程序)测试升级是否成功
- 将 arduino IDE 中的程序的远程链接替换成本地 HTTP 服务器链接
  
- 将 2.4.1 节中的 esp32_update.json内部版本改为 1_0_0 ,保证一开始不升级。
- 将程序烧录进 esp32 开发板中。然后打开串口监视器
- 串口调试器中显示不需要升级
  
- 将 2.4.1 节中的 esp32_update.json内部版本改为 1_0_1 ,然后重启开发板。
  
- 由上图可知,开发板自动判断是否需要升级并自动OTA升级成功。
3. HTTP_OTA 升级展望
3.1 后期版本更新可通过 HTTP_OTA 实现
通过第二节可知,可以通过 HTTP_OTA 实现 esp32 开发板的隔空升级,这样可以在一台设备上测试好了程序后,上传 .bin 文件到第 2.4.1 节中的 HTTP 服务器文件夹中,实现其他开发板批量升级。
3.2 借助网络云平台实现远程 HTTP_OTA 升级
第 2.4 节是使用本地 HTTP 服务器进行升级的,我们也可以使用云服务厂商的对象云存储服务,将需要升级的 .bin 文件与 esp32_update.json 放到云服务厂商的对象云存储服务中,使用提供的公网域名替换程序代码中的远程固件连接,真正实现远程 OTA 快速自动升级服务。
注意:
- 本地 HTTP_OTA 升级时,本机电脑需要和 esp32 开发板连在同一个网络下,否则 esp32 开发板无法访问固件地址。
- 使用云服务厂商的对象云存储服务,对象云存储需要设置禁止缓存,否则可能会获取之前缓存的版本而不是最新版,导致不必要的错误。
本文首发于本人博客:https://blog.gitnote.cn/post/esp32cam_http_ota/
 版权信息: CC BY-NC-SA 4.0 (自由转载-非商用-相同方式共享-保持署名)


















