ESP32-CAM OTA线上更新固件遇到的分区问题


对于之前写的文章 ESP32 通过HTTPS进行OTA更新固件(在platform上进行编码)中遇到的分区表的问题,我觉得有必要写一篇文章来解释一下到底怎么回事,我又是怎么解决的。

1、分区表

什么是分区表

分区表是 ESP32 划分内部 flash 闪存的清单,它将 flash 划分为多个不同功能的区域用于其他功能。

分区类型分为两种,分别为 “应用” 和 “数据”。如应用分为Factory程序,OTA程序等,又如数据分为校准数据、文件系统数据、参数存储数据等。

分区表的长度为 0xC00 字节(最多可以保存 95 条分区表条目)。分区表数据后还保存着该表的 MD5 校验和,用于验证分区表的完整性。此外,如果芯片使能了 “安全启动” 功能,则该分区表后还会保存签名信息。

2、OTA更新策略

首次进行OTA升级时,OTA Demo向OTA_0分区烧录目标固件,并在烧录完成后,更新OTA data分区数据并重启。系统重启时获取OTA data分区数据进行计算,决定此后加载OTA_0分区的固件执行(而不是默认的Factory App分区内的固件),从而实现升级。

同理,若某次升级后ESP32已经在执行OTA_0内的固件,此时再升级时OTA Demo就会向OTA_1分区写入目标固件。再次启动后,执行OTA_1分区实现升级。以此类推。

升级的目标固件始终在OTA_0 OTA_1两个分区之间交互烧录,不会影响到出厂时的Factory App固件。

在这里插入图片描述

问题:分区表设置问题

我之前写的文章中提到我从AWS S3中OTA更新固件不能烧录到ESP32-CAM分区中,不能更新固件。

返回的错误信息:ESP_ERR_OTA_PARTITION_CONFLICT,原因是分区保持着正确的运行固件,不能更新到这个这个地方。

所以我在想能不能编写两个程序轮流进行OTA更新动作,这样就能解决不能烧录到分区的问题。

3、解决方法

1.设置分区表

为了解决分区表设置问题,我们自己设置一个分区表,再在配置文件中加上就能解决问题。

注意:我是通过PlatformIO进行编程的。

(1). 在项目文件夹中新建partition.csv文件。

在这里插入图片描述

(2). 在项目文件夹的platformio.ini文件中添加编译命令。(别忘了保存
board_build.partitions = partition.csv
在这里插入图片描述

(3). 打开partition.csv,定义分区表。

根据自己需要定义分区表,比如我目前想进行OTA更新就需要两个分区(ota_0,ota_1),我就可以要定义连个分区,又比如我写的程序需要的存储空间大,我就要舍弃一个分区(ota_1)来增加另一个分区(ota_0)的大小。

开始定义分区表,我设置了两个分区。
在这里插入图片描述

2、编写两个用于OTA更新的程序

我编写的程序是两个闪灯程序,分别为闪白灯和闪红的(ESP32-CAM中有两个板载LED灯),这样就只要修改灯的引脚(红灯33,白灯4),就不用额外编写代码。

注意:红灯和白灯的 digitalWrite中的LOWHIGH参数是相反的不要忘了更改。

架构图:
在这里插入图片描述

  1. 将闪红灯程序的.bin文档上传AWS S3。
  2. 将闪白灯程序烧录到ESP32-CAM中。
  3. 打开手机热点(设置ssid、password)
  4. 开始OTA更新,下载完成后会看到由白灯闪烁变为红灯闪烁。

特别的是:为了控制什么时候开始OTA更新,我添加了按钮,这样每当我按下按钮时就开始OTA更新。

3、代码

我就上传红灯代码,红灯代码修改一下灯的引脚就行了。

注意:需要准备一个外接按钮,需要在代码中输入yourssid,yourpassword,AWS S3中文件的url还有AWS网站证书。在这篇文章中有写怎么获取ESP32 通过HTTPS进行OTA更新固件(在platform上进行编码)

#include <Arduino.h>
#include "time.h"
#include <WiFi.h>
#include <ezButton.h>
#include "HttpsOTAUpdate.h"
#include "esp_ota_ops.h"

#define BUTTON_PIN 12 //将按钮引脚绑定到GPIO 12
#define FLASH_LED 4
#define RED_LED_BUILTIN 33
#define DEBOUNCE_TIME  50
#define uS_TO_S_FACTOR 1000000 //1000000=1s
#define TIME_TO_SLEEP 5 //5s

//setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

static const char *ssid     = "yourssid";  // your network SSID (name of wifi network)
static const char *password = "yourpassword"; // your network password

static const char *url = ""; //state url of your firmware image


static const char *server_certificate = "-----BEGIN CERTIFICATE-----\n" \

"-----END CERTIFICATE-----";

static HttpsOTAStatus_t otastatus;

ezButton button(BUTTON_PIN);

void HttpEvent(HttpEvent_t *event)
{
    switch(event->event_id) {
        case HTTP_EVENT_ERROR:
            Serial.println("Http Event Error");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            Serial.println("Http Event On Connected");
            break;
        case HTTP_EVENT_HEADER_SENT:
            Serial.println("Http Event Header Sent");
            break;
        case HTTP_EVENT_ON_HEADER:
            Serial.printf("Http Event On Header, key=%s, value=%s\n", event->header_key, event->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            break;
        case HTTP_EVENT_ON_FINISH:
            Serial.println("Http Event On Finish");
            break;
        case HTTP_EVENT_DISCONNECTED:
            Serial.println("Http Event Disconnected");
            break;
    }
}

void setup() {
// put your setup code here, to run once:
pinMode(RED_LED_BUILTIN, OUTPUT); //在使用引脚输入输出之前,先配置此函数,引脚做什么用,模式是什么。
//ezButton button(BUTTON_PIN);

button.setDebounceTime(DEBOUNCE_TIME);

Serial.begin(115200); 
Serial.print("Attempting to connect to SSID: ");
WiFi.begin(ssid, password);

// attempt to connect to Wifi network:
while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
    digitalWrite(RED_LED_BUILTIN, LOW);
    delay(1000);
    digitalWrite(RED_LED_BUILTIN, HIGH);
}

Serial.print("Connected to ");
Serial.println(ssid);

}

unsigned int pressCount = 0;
void loop() {
// put your main code here, to run repeatedly:
/*
digitalWrite(RED_LED_BUILTIN, LOW); //让某个引脚输出高点品或者时低电平
delay(300);
digitalWrite(RED_LED_BUILTIN, HIGH);
delay(300);
*/
button.loop();

digitalWrite(RED_LED_BUILTIN, LOW);

if (button.isPressed()){
    Serial.print("noise");
    if (pressCount == 0){
    pressCount = 1;
    Serial.println("The button is pressed");
//      attachClick();
    //myClickFunction();
    HttpsOTA.onHttpEvent(HttpEvent);
    Serial.println("Starting OTA");
    HttpsOTA.begin(url, server_certificate);
    
    Serial.println("Please Wait it takes some time ...");
    }
    //attachClick();
}

if (pressCount == 1){
    otastatus = HttpsOTA.status();
    if(otastatus == HTTPS_OTA_SUCCESS) { 
        Serial.println("Firmware written successfully. To reboot device, call API ESP.restart() or PUSH restart button on device");
        //esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); //设置计时器5s后解除深度睡眠
        //gpio_deep_sleep_hold_dis();
        //esp_deep_sleep_start(); //进入深度睡眠模式
        ESP.restart();
    } else if(otastatus == HTTPS_OTA_FAIL) { 
        Serial.println("Firmware Upgrade Fail");
    }
    delay(1000);
}

}

4、问题:OTA更新完成之后红灯和白灯一起闪烁

我猜测的原因是ESP.restart()没有成功引导正确的运行的程序。

细心的大家也看到了我设置了定时器,在ESP32-CAM完成OTA更新之后进入睡眠,在5s后醒来,但是效果是本该亮的灯不亮,不该亮的灯亮了,效果还没不设置好,我也不知道什么原因,如果有知道的请在评论区指出,谢谢!。

参考资料

ESP32-Flash分区,基于PlatfromIO-Arduino:
https://blog.csdn.net/liahfdsaf/article/details/119010732

ESP32之 ESP-IDF 教学(十三)—— 分区表:
https://blog.csdn.net/m0_50064262/article/details/122279800

esp32 Flash分区与OTA功能简析:
https://blog.csdn.net/abc517789065/article/details/79891568

ESP32 通过HTTPS进行OTA更新固件(在platform上进行编码)
https://blog.csdn.net/qq_62819897/article/details/129761069

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值