android OTA升级(VAB分区)

Posted aspirs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android OTA升级(VAB分区)相关的知识,希望对你有一定的参考价值。

android OTA升级(VAB分区)

 

升级开始前,升级程序update_engine将要升级的slot标记为unsuccessful,调用boot_ctl的SetSlotAsUnbootable()

hardware/interfaces/boot/1.1/default/boot_control/libboot_control.cpp

316  bool BootControl::SetSlotAsUnbootable(unsigned int slot) 
317    if (slot >= kMaxNumSlots || slot >= num_slots_) 
318      // Invalid slot number.
319      return false;
320    
321  
322    bootloader_control bootctrl;
323    if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
324  
325    // The only way to mark a slot as unbootable, regardless of the priority is to
326    // set the tries_remaining to 0.
327    bootctrl.slot_info[slot].successful_boot = 0;
328    bootctrl.slot_info[slot].tries_remaining = 0;
329    return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
330  

 

58 ESP32 OTA升级(双OTA分区无factory APP)

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;		//启动http

extern 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");

	//启动启动http
	xEventGroupSetBits(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));
				
				//写flash
				err = 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 within
		the 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);
			
		
	

	//不会走到这里,走到这里需要删除task
	vTaskDelete(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! 

以上是关于android OTA升级(VAB分区)的主要内容,如果未能解决你的问题,请参考以下文章

Android11.0 OTA差分包升级失败kDownloadStateInitializationError

Android POTA升级包定制,移除不需要更新的分区,重新打包签名

Android POTA升级包定制,移除不需要更新的分区,重新打包签名

58 ESP32 OTA升级(双OTA分区无factory APP)

58 ESP32 OTA升级(双OTA分区无factory APP)

Android OTA升级