ESP32cam系列教程003:ESP32cam实现远程 HTTP_OTA 自动升级

news2024/11/26 21:43:36

文章目录

  • 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 方式:

  1. Arduino IDE :主要用于软件开发阶段,实现不接线固件烧录
  2. Web_OTA:通过 Web 浏览器手动提供应用程序更新模块
  3. HTTP_OTA:固件存放到 http 服务器端,设备自动判断是否需要联网下载固件升级

本文主要介绍:HTTP_OTA 的原理与实现。

2. ESP32cam HTTP_OTA 本地准备

2.1 HTTP OTA 升级原理

  1. 本地程序在开机连接 WIFI 后发送 http 请求获取远程服务器中的升级 json 文件。
  2. 通过对比 json 中的远程版本信息与本地的版本信息判断是否一致。
  3. 若远程版本信息与本地版本不一致,则本地需要更新程序。
  4. 通过 json 中的版本信息在远程服务器中拉取需要更新的程序的 .bin 文件。
  5. 依据下载下来的 .bin 自动完成版本的升级,然后自动重启开发板。
  6. 重复第一步获取远程 json 文件判断是否需要更新。

2.2 开发板本地基准程序(程序版本:1_0_0)

本地 1_0_0 版本程序主要内容如下:

  1. 当前版本(非常重要,升级依据)
  2. 远程升级的 json 链接与远程固件的文件夹链接
  3. 获取并解析 json 的函数 httpGETRequest
  4. 依据 json 判断是否需要更新的函数 isOrNotNeedUpdate
  5. 以及其他基础信息组成
#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 进行:

  1. 用 vscode 打开一个空白文件夹
  2. 在文件夹中新建目录 esp32,文件 index.html ,目录下新建文件 esp32_update.json
  3. esp32_update.json 中内容是 {"version":"1_0_1"} ,表明当前远程的版本为 1_0_1
  4. index.html 中为标准html结构文件
  5. index.html 界面中右键>Open with Live Server 打开

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

2.4.2 替换远程链接并将要升级的程序打包成 .bin 文件

  1. 将 arduino IDE 中的程序的远程链接替换成本地 HTTP 服务器链接

  2. 工具中 开发板和 Partition Scheme 选择如下图:

  3. 项目中选择导出已编译的二进制文件,导出的二进制文件在同级目录下。

  4. 将导出的 .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 的程序)测试升级是否成功

  1. 将 arduino IDE 中的程序的远程链接替换成本地 HTTP 服务器链接
  2. 将 2.4.1 节中的 esp32_update.json 内部版本改为 1_0_0 ,保证一开始不升级。
  3. 将程序烧录进 esp32 开发板中。然后打开串口监视器
  4. 串口调试器中显示不需要升级
  5. 将 2.4.1 节中的 esp32_update.json 内部版本改为 1_0_1 ,然后重启开发板。
  6. 由上图可知,开发板自动判断是否需要升级并自动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 快速自动升级服务。

注意:

  1. 本地 HTTP_OTA 升级时,本机电脑需要和 esp32 开发板连在同一个网络下,否则 esp32 开发板无法访问固件地址。
  2. 使用云服务厂商的对象云存储服务,对象云存储需要设置禁止缓存,否则可能会获取之前缓存的版本而不是最新版,导致不必要的错误。

本文首发于本人博客:https://blog.gitnote.cn/post/esp32cam_http_ota/
版权信息: CC BY-NC-SA 4.0 (自由转载-非商用-相同方式共享-保持署名)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring系列二:基于注解配置bean

文章目录 &#x1f497;通过注解配置bean&#x1f35d;基本介绍&#x1f35d;快速入门&#x1f35d;注意事项和细节 &#x1f497;自己实现Spring注解配置Bean机制&#x1f35d;需求说明&#x1f35d;思路分析&#x1f35d;注意事项和细节 &#x1f497;自动装配 Autowired&…

谷粒商城第八天-商品服务之品牌管理的整体实现(直接使用逆向生成的代码;含oss文件上传)

目录 一、总述 二、前端部分 2.1 创建好品牌管理菜单 2.2 复制组件 ​编辑2.3 复制api ​​​编辑 2.4 查看效果 ​编辑2.5 需要优化的地方 2.6 具体优化实现 2.6.1 优化一&#xff1a;将表格的状态列&#xff08;这里是是否显示列&#xff09;修改为开关&#xff…

Day15-作业(Maven高级)

作业1&#xff1a;完成苍穹外卖项目目录结构创建 说明&#xff1a; 1.sky-take-out 是父工程 2.sky-common/sky-pojo/sky-server 是子工程 3.sky-server模块引用了sky-common/sky-pojo模块 作业2&#xff1a;完成汇客CRM项目目录结构创建 说明&#xff1a; huike-paren…

判断变量是否为标量(没有维度,单个数值)numpy.isscalar()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断变量是否为标量 &#xff08;没有维度,单个数值&#xff09; numpy.isscalar() [太阳]选择题 请问关于以下代码表述错误的是&#xff1f; import numpy as np a 0 b np.array([0]) pri…

六、初始化和清理(3)

本章概要 成员初始化构造器初始化 初始化的顺序静态数据的初始化显式的静态初始化非静态实例初始化 成员初始化 Java 尽量保证所有变量在使用前都能得到恰当的初始化。对于方法的局部变量&#xff0c;这种保证会以编译时错误的方式呈现&#xff0c;所以如果写成&#xff1a…

初识Java - 概念与准备

本笔记参考自&#xff1a; 《On Java 中文版》 目录 写在第一行 Java的迭代与发展 Java的迭代 Java的参考文档 对象的概念 抽象 接口 访问权限 复用实现 继承 基类和子类 A是B和A像B 多态 单根层次结构 集合 参数化类型 对象的创建和生命周期 写在第一行 作为一…

Charles抓包工具使用(一)(macOS)

Fiddler抓包 | 竟然有这些骚操作&#xff0c;太神奇了&#xff1f; Fiddler响应拦截数据篡改&#xff0c;实现特殊场景深度测试&#xff08;一&#xff09; 利用Fiddler抓包调试工具&#xff0c;实现mock数据特殊场景深度测试&#xff08;二&#xff09; 利用Fiddler抓包调试工…

pytorch-gpu 极简安装

1、进入pytoch官网&#xff1a;PyTorch 找到pytorch-gpu版本&#xff0c;看到CUDA11.8、11.7、CPU&#xff0c;这里我选择安装CUDA11.8 2、下载CUDA Toolkit&#xff1a;CUDA Toolkit 11.8 Downloads | NVIDIA Developer 3、下载CUDANN&#xff1a;cuDNN Download | NVIDIA D…

TCP拥塞控制详解 | 1. 概述

网络传输问题本质上是对网络资源的共享和复用问题&#xff0c;因此拥塞控制是网络工程领域的核心问题之一&#xff0c;并且随着互联网和数据中心流量的爆炸式增长&#xff0c;相关算法和机制出现了很多创新&#xff0c;本系列是免费电子书《TCP Congestion Control: A Systems …

基于Spring Boot的鲜花销售网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的鲜花销售网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java springboot…

运算放大器(三):差分放大

一、定义 差分放大电路又称为差动放大电路&#xff0c;当该电路的两个输入端的电压有差别时&#xff0c;输出电压才有变动&#xff0c;因此称为差动。 二、电路结构 常用的差分放大电路如下 图1 所示&#xff0c; 图1 根据虚短和虚断可得&#xff1a;

C# Blazor 学习笔记(0):初识Blazor

文章目录 Blazor是什么适合人群 开始学习BlazorBlazor资源如何创建BlazorBlazor 基础知识介绍文件分布Razor和cshtml的区别Razor介绍 Blazor是什么 Blazor是微软推出的前端框架&#xff0c;有两种形式&#xff0c;以下以Blazor Server为主。具有一下特点 前端是用C#而不是JS前…

Linux编辑器 - vim使用

1.vim的基本概念 Vim是一个广泛使用的文本编辑器&#xff0c;它是在Unix和Linux系统中常用的命令行文本编辑器之一。 vim的主要三种模式 ( 其实有好多模式&#xff0c;目前掌握这 3 种即可 ), 分别是 命令模式 &#xff08; command mode &#xff09;、 插入模式 &#xff0…

仿第八区分发封装打包系统源码教程-轻松打包app

这个是最新的修改优化&#xff0c;优化了一些小bug不能用的问题&#xff0c;具体什么就不讲了&#xff0c;用过的应该有知道的。 大致功能&#xff1a; 支持一键安卓打包&#xff0c;IOS免签、绿标&#xff01;&#xff08;几乎媲美原生&#xff09; 拥有此款神器&#xff0…

AppCompatActivity.setContentView(与activity.setContentView区别)方法解读

AppCompatActivity.setContentView()与Activity.setContentView()主要的区别&#xff0c;Activity.setContentView直接将视图添加到Window上&#xff0c;AppCompatActivity.setContentView()借助AppCompatActivity的Delegate代理类&#xff0c;将要显示的视图加入到代理层视图&…

【网络原理】 (3) (网络层 IP协议 地址管理 路由选择 数据链路层 以太网 MTU 补充:DNS)

文章目录 网络层IP协议地址管理路由选择 数据链路层以太网MTU补充:DNS 网络层 IP协议 网络层的代表,IP协议. 4位版本号&#xff08;version&#xff09;&#xff1a;指定IP协议的版本&#xff0c;对于IPv4来说&#xff0c;就是4。4位头部长度&#xff08;header length&…

98. Python基础教程:try...except...finally语句

【目录】 文章目录 1. try...except...finally语法介绍2. try...except...finally执行顺序3. 捕获特定类型的异常4. 捕获所有类型的异常5. 实操练习-打开txt文件并输出文件内容 【正文】 在今天的课程中&#xff0c;我们将学习Python中的异常处理语句try...except...finally。 …

excel英语翻译让你的数据更容易被理解

从前有一个名叫小明的办公室职员&#xff0c;他每天都要处理大量的数据和报表。然而&#xff0c;由于工作需要&#xff0c;他经常收到来自不同国家的Excel表格&#xff0c;这些表格上的内容都是用各种各样的语言编写的&#xff0c;让他很难理解其中的意思。这时&#xff0c;小明…

Qt Creator 11 开放源码集成开发环境新增集成终端和 GitHub Copilot 支持

导读Qt 项目今天发布了 Qt Creator 11&#xff0c;这是一款开源、免费、跨平台 IDE&#xff08;集成开发环境&#xff09;软件的最新稳定版本&#xff0c;适用于 GNU/Linux、macOS 和 Windows 平台。 Qt Creator 11 的亮点包括支持标签、多外壳、颜色和字体的集成终端模拟器&am…

建模教程:如何利用3ds Max 和 After Effects 实现多通道渲染和后期合成

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建基本场景 步骤 1 打开 3ds Max。在 透视视口。 打开 3ds Max 步骤 2 做一个茶壶&#xff0c;放在飞机上。 制作茶壶 步骤 3 我在场景中应用了几个灯光。我选择了光线追踪阴影作为阴影。 光线追…