1 引言

产品功能实现后,就要对产品的维护进行考虑。产品出来后,卖了N台出去,如果突然发现自己一行代码写错了,怎么办,肯定不能去现场吧N台设备,免费出差旅游也累啊,所以一般需要有远程升级设备的功能,此篇为对ESP32 OTA实验经验总结。

2 问题

当前ESP32官方例程,以及网络上的经验例程,都是factory APP + 双OTA_x 方式。我选用的esp32是4M flash的,现在编译的固件大小是1.3M,按照上述的方式,需要用到的空间是:1.3M * 3 =3.9M。

只有4M大小flash的我,很明显,这看起来马上就不能用,也不够用了。所以,按照网上或官网那种分区的方式,肯定不行。

然后在继续查找经验过程中,发现有人没有用官方的factory APP+双OTA_X方式,而是直接双OTA_0和OTA_1,没有factory这个东东,省下了1倍固件大小的空间,所以只有4M flash的我,就采用这个方案了。

3 双OTA no factory APP实现

要做OTA升级,需要了解的内容是:

1.如何对flash划分分区?

2.升级机制是怎样的?

3.如何将固件从网络上download下来并写到flash中,以及标记相关参数?

对于问题1:如何对flash进行分区划分?

对这个问题,我一开始比较迷惑。因为esp32给出的ota升级的demo,是需要三个分区,即factory app+OTA1+OTA2,按照这算法,所需flash的大小是单个app的三倍。

然后我固件大小是1.3M,整块flash大小4M,这样算下来3*1.3 = 3.9M,显然不够,所以一开始迷惑了。官方怎么不给只有两个的,完全两个就搞定了,然后经过资料搜索,和尝试,最后确认两个OTA分区,不需要factory app也是可以的,所以就敲定了以下的分区划分。

对于问题2:升级机制是怎样的?

esp32 OTA的升级机制跟传统的机制是一样的,来回切换,就这样。。。。

对于问题3:如何将固件从网络上download下来并写到flash中,以及标记相关参数?

下载的话,我采用了httpget方式,将文件从http服务器下载下来。至于下载下来的文件写入flash和如何标记OTA_data相关参数,直接参考官方例程即可。整个搞下来其实不难,要花一点时间调试而已。

本实验采用的方式是模块化编程,所以下面给出的代码都是模块独立的,稍微移植就可以用。

#include <stdio.h>
#include <string.h>#include "esp_http_client.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "mqtt_client.h"#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_ota_ops.h"
#include "ht_led.h"#include "ht_console.h"
#include "ht_main.h"
#include "HTMngr.h"
#include "ht_ota.h"/
uint32_t            ghttpfileSize   = 0;
uint32_t            gProtofileSize  = 0;xQueueHandle       htOTA_evt_queue = NULL;
TimerHandle_t       gExitOTATaskTimeID = NULL;EventGroupHandle_t   ht_OTAEvt_group;            //全局事件句柄
const int HT_OTAEvt_OTA_UPDATE_BIT  = BIT0;        //ota事件
const int HT_OTAEvt_RUN_HTTP_BIT    = BIT1;        //启动httpextern EventGroupHandle_t   ht_MngrEvt_group;   //全局事件句柄
extern const int HT_MngrEvt_RebootSys_BIT;      //系统重启extern GlobalArgs g_SysParam;
//
esp_err_t htOTA_http_event_handle(esp_http_client_event_t *evt)
{HTOTA_queData_t tmpHTOTA_queData;//判断事件类型switch (evt->event_id){case HTTP_EVENT_ERROR:Console_Debug("HTTP_EVENT_ERROR");break;case HTTP_EVENT_ON_CONNECTED:Console_Debug("HTTP_EVENT_ON_CONNECTED");break;case HTTP_EVENT_HEADER_SENT:Console_Debug("HTTP_EVENT_HEADER_SENT");break;case HTTP_EVENT_ON_HEADER:Console_Debug("HTTP_EVENT_ON_HEADER");if(strstr((char *)evt->header_key, "Content-Length")){ghttpfileSize = atoi(evt->header_value);Console_Debug("update file %s:%d\r\n", evt->header_key, ghttpfileSize);}break;case HTTP_EVENT_ON_DATA://接收数据memset(&tmpHTOTA_queData , 0, sizeof(tmpHTOTA_queData));tmpHTOTA_queData.rcvLength = evt->data_len;memcpy(tmpHTOTA_queData.rcvBuffer ,evt->data, tmpHTOTA_queData.rcvLength);//发送数据if(xQueueSend(htOTA_evt_queue, &tmpHTOTA_queData, (1500 / portTICK_PERIOD_MS) ) != pdPASS) {Console_Error("xQueueSend failed\r\n");}break;case HTTP_EVENT_ON_FINISH:       Console_Debug("HTTP_EVENT_ON_FINISH");break;case HTTP_EVENT_DISCONNECTED:Console_Debug("HTTP_EVENT_DISCONNECTED");break;default:break;}return ESP_OK;
}int htOTA_runHttpGet(void)
{esp_http_client_handle_t client;//参数校验if(strlen((char*)g_SysParam.ota_url)<5){Console_Error("ota_url %s error!", g_SysParam.ota_url);return -1;}//配置http参数esp_http_client_config_t config = {.url = (char*)g_SysParam.ota_url,                      //目标文件.event_handler = htOTA_http_event_handle,                //http回调函数.buffer_size = (1024),                                   //指定接收缓存大小};client = esp_http_client_init(&config);Console_Debug("Starting htOTA_runHttpGet...");//发起http连接esp_http_client_perform(client); //发起http连接esp_http_client_close(client);esp_http_client_cleanup(client);return 0;
}void htOTA_setTargetFileSize(uint32_t utargetSize)
{gProtofileSize = utargetSize;
}void htOTA_queueTask(void *pvParameters)
{uint32_t uCnt = 0;uint32_t uLedCnt = 0;uint32_t rcvBinarySize = 0;esp_err_t err = 0;esp_ota_handle_t update_handle = 0 ;const esp_partition_t *update_partition = NULL;HTOTA_queData_t tmpHTOTA_queData = {0};//这里等待1s,等待mqtt回复完再启动vTaskDelay((1000) / portTICK_PERIOD_MS);Console_Debug("Starting OTA updating...");//获取当前boot位置const esp_partition_t *configured = esp_ota_get_boot_partition();//获取当前系统执行的固件所在的Flash分区const esp_partition_t *running = esp_ota_get_running_partition();if (configured != running) {Console_Debug("Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",configured->address, running->address);Console_Debug("(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");}Console_Debug("Running partition type %d subtype %d (offset 0x%08x)",running->type, running->subtype, running->address);//获取当前系统下一个(紧邻当前使用的OTA_X分区)可用于烧录升级固件的Flash分区update_partition = esp_ota_get_next_update_partition(NULL);Console_Debug("Writing to partition subtype %d at offset 0x%x",update_partition->subtype, update_partition->address);if(update_partition == NULL){Console_Error("update_partition == NULL...");goto iExit;}//启动OTA模块要进行烧写err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);if (err != ESP_OK) {Console_Error("esp_ota_begin failed, error=%d", err);goto iExit;}Console_Debug("esp_ota_begin succeeded");//启动启动httpxEventGroupSetBits(ht_OTAEvt_group, HT_OTAEvt_RUN_HTTP_BIT);//启动监控定时器xTimerStop(gExitOTATaskTimeID, 0);xTimerStart(gExitOTATaskTimeID,0);//在这里循环等待接收数据while(1){memset(&tmpHTOTA_queData, 0, sizeof(tmpHTOTA_queData));if(xQueueReceive(htOTA_evt_queue, &tmpHTOTA_queData, portMAX_DELAY)) {//检测文件大小是否正确if(ghttpfileSize != gProtofileSize){Console_Error("ghttpfileSize%d,  gProtofileSize:%d! error", ghttpfileSize, gProtofileSize);goto iExit;}//先判断是否数据为空if(tmpHTOTA_queData.rcvLength >0){uLedCnt++;htLed_SetRGBLight(RGBLED_MODE_Y, uLedCnt&0x1);uCnt++;if(uCnt%20==0){                   Console_Debug("file total size: (%d Byte), download...(%d %%)\r\n",ghttpfileSize, (rcvBinarySize*100/ghttpfileSize));}//写flasherr = esp_ota_write( update_handle, (const void *)tmpHTOTA_queData.rcvBuffer, tmpHTOTA_queData.rcvLength);if (err != ESP_OK) {Console_Error("Error: esp_ota_write failed! err=0x%x", err);goto iExit;}rcvBinarySize += tmpHTOTA_queData.rcvLength;if(rcvBinarySize >= gProtofileSize){Console_Debug("file total size: (%d Byte), download...(100 %%)\r\n",ghttpfileSize);Console_Error("Connection closed, all packets received");htLed_SetRGBLight(RGBLED_MODE_Y, 0);break;}}//重置监控定时器xTimerStop(gExitOTATaskTimeID, 0);xTimerStart(gExitOTATaskTimeID,0);}}Console_Debug("Total Write binary data length : %d", rcvBinarySize);//OTA写结束if (esp_ota_end(update_handle) != ESP_OK) {Console_Error("esp_ota_end failed!");goto iExit;}//升级完成更新OTA data区数据,重启时根据OTA data区数据到Flash分区加载执行目标(新)固件err = esp_ota_set_boot_partition(update_partition);if (err != ESP_OK) {Console_Error("esp_ota_set_boot_partition failed! err=0x%x", err);goto iExit;}Console_Debug("Prepare to restart system!");iExit://触发系统重启xEventGroupSetBits(ht_MngrEvt_group, HT_MngrEvt_RebootSys_BIT);vTaskDelete(NULL);return;
}void htOTA_EvtTask(void *pvParameters)
{uint8_t isRunOTATaskFlag = 0;EventBits_t uxBits;while(1){/* Wait a maximum of 100ms for either bit 0 or bit 4 to be set withinthe event group.    Clear the bits before exiting. */uxBits = xEventGroupWaitBits(ht_OTAEvt_group,   /* The event group being tested. */HT_OTAEvt_RUN_HTTP_BIT | HT_OTAEvt_OTA_UPDATE_BIT, /* The bits within the event group to wait for. */pdTRUE,      /* BIT_0 & BIT_4 should be cleared before returning. */pdFALSE,     /* Don't wait for both bits, either bit will do. */portMAX_DELAY );/* Wait a maximum of 100ms for either bit to be set. *///触发ota升级if( ( uxBits & HT_OTAEvt_RUN_HTTP_BIT ) != 0 ){Console_Debug("HT_OTAEvt_RUN_HTTP_BIT\r\n");htOTA_runHttpGet();}//启动ota升级线程if( ( uxBits & HT_OTAEvt_OTA_UPDATE_BIT ) != 0 ){Console_Debug("HT_OTAEvt_OTA_UPDATE_BIT, isRunOTATaskFlag:%d\r\n", isRunOTATaskFlag);if(isRunOTATaskFlag == 0){isRunOTATaskFlag = 1;//OTA接收数据处理函数xTaskCreate(htOTA_queueTask, "htOTA_queTsk", (6*1024), NULL, HT_TASK_HTOTA_queue_Priority, NULL);}}}//不会走到这里,走到这里需要删除taskvTaskDelete(NULL);return;
}//监控ota升级任务运行正常,如果服务器卡住,接收数据不完全卡住,则超时重启
static void htOTA_autoExitOTA_callback(void *arg)
{//触发系统重启xEventGroupSetBits(ht_MngrEvt_group, HT_MngrEvt_RebootSys_BIT);
}int htOTA_Init(void)
{//创建ota等待事件集ht_OTAEvt_group = xEventGroupCreate();//创建消息队列用以接收http收到的数据htOTA_evt_queue = xQueueCreate(2, sizeof(HTOTA_queData_t));if(htOTA_evt_queue == NULL){Console_Error("xQueueCreate failed\r\n");return -1;}//创建业务处理线程xTaskCreate(htOTA_EvtTask, "htOTA_EvtTask", (4*1024), NULL, HT_TASK_HTOTA_EVT_Priority, NULL);gExitOTATaskTimeID   = xTimerCreate("exitOTA", (15*1000 / portTICK_PERIOD_MS), pdFALSE, NULL, htOTA_autoExitOTA_callback);return 0;
}

本人支持开源精神,以供有需要的人学习,减少重复造轮子带来的浪费,希望各位码主多多支持开源,有好东西多分享,一同进步!

over!

58 ESP32 OTA升级(双OTA分区无factory APP)相关推荐

  1. android ota升级涉及的分区,Android OTA升级原理 - 实现流程(整理一)

    Android OTA实现流程分析 OTA升级概述 制作升级包 自动生成update.zip升级包 手动生成update升级包 增量包目录结构 升级包写入设备分区 Android的三种工作模式 Rec ...

  2. android怎么ota升级,Android OTA升级过程

    通过网络或直接本地获取到OTA升级包之后,通过程序就可开始Android的升级.本文描述这一过程. 在获取到OTA升级包之后,可以直接通过android.os.RecoverySystem.insta ...

  3. ESP32 学习日志(4)——OTA升级(1)-示例解析

    一.OTA简介 1.1 概述 ESP32应用程序可以在运行时通过Wi-Fi或以太网从特定的服务器下载新镜像,然后将其闪存到某些分区中,从而进行升级.在ESP-IDF中本文采用native_ota_ex ...

  4. AUTOSAR OTA升级

    一.OTA技术概念 随着高级辅助驾驶的发展和自动驾驶的引入,汽车变得越来越智能,这些智能汽车被软件控制,装有巨量的软件程序,当出现一个软件程序问题或者更新时,如果 按照传统的解决方式 ,那都将是一项很 ...

  5. Android-AB系统OTA升级介绍

    什么是OTA升级? OTA是Over-the-Air的简称,OTA升级可以理解为用户正常使用过程中进行升级,OTA 升级旨在升级基础操作系统.系统分区上安装的只读应用和/或时区规则. 什么是Andro ...

  6. Android OTA升级(1):编译升级全包

         Android原生系统中就已经支持OTA升级.所谓OTA升级就是通过空中接口获取升级包,然后更新系统固件.一般地,升级包无论如何获取,哪怕是直接TCard本地升级,也被称为OTA升级.    ...

  7. android的A/B到底是什么?OTA升级又是什么?

    最近在做AVB校验,学习的时候就看到AVB里面有A/B系统.就挺纳闷这个是什么? 这个有什么用?在学习的时候看到了这个和OTA升级有关系? 下面看看为什么需要A/B系统,A/B是什么? 在这里.感谢这 ...

  8. Android 7.0 OTA升级(高通)

    文章目录 1. Full OTA 方式升级介绍 1.1 Full OTA 制作第一步:生成 msm89xx-target_files-eng.XXX.zip 1.2 Full OTA 制作第二步:Mo ...

  9. Android OTA升级原理 - 实现流程(整理一)

    Android OTA实现流程分析 OTA升级概述 制作升级包 自动生成update.zip升级包 手动生成update升级包 增量包目录结构 升级包写入设备分区 Android的三种工作模式 Rec ...

最新文章

  1. 《Fabric 云存储的电子健康病历系统》(1)系统介绍
  2. what courses to choose at Cambridge?
  3. cad java_cad和java哪个工资高
  4. JSF基于事件的沟通:过时的方法
  5. [Android] 年年有鱼手机主题
  6. android中shape资源定义,Android中drawable使用Shape资源
  7. git rebase --skip_可冒充git大神的git tips
  8. 前置++与后置++的要点分析
  9. MySQL初始化安装部署
  10. JS 对象(Object)和字符串(String)互转方法
  11. Windows MongoDB下载 安装以及配置(一条龙到底)
  12. qq传输文件为什么服务器忙,qq传送离线 接收文件很慢怎么回事
  13. U盘格式化后容量变小了恢复教程
  14. Inkscape制作logo
  15. 解决Could not build wheels for pikepdf which use PEP 517 and cannot be installed directly
  16. 每一个圣人都有一个过去
  17. 基于webpack4搭建Vue服务端渲染(SSR)
  18. 计算机毕业设计Java某银行OA系统某银行OA系统演示2021(源代码+数据库+系统+lw文档)
  19. uni-app中自定义图表(canvas实现chart图表)开发篇(5)-圆环进度条添加动画效果
  20. android 自定义声音,如何在Android设备中添加自己的自定义声音

热门文章

  1. TELE 4662 PHOTONIC NETWORKS -Lecture 3: Light Sources and Detectors-半导体(Semiconductor)
  2. 【RCJ-2 AC220V 0.015A静态冲击继电器】
  3. elementUI 标记提示:图标显示待办业务数量
  4. 数据库的约束和设计(完整版)
  5. Linux fedora 19 x86_64 双显卡 关闭独显
  6. CAD看图软件如何才好用
  7. 圣诞礼物c语言代码大全,windows编程入门二:圣诞节的礼物——从“屏幕飘雪”程序说起...
  8. Excel清除隐藏的引号或空格
  9. LSM优化系列(五) -- 【SIGMOD‘19】X-engine 在电商场景下针对大规模事务处理的优化-- 强者恒强啊
  10. 基于ise14.7中的cmd设计