ESP8285 RTOS SDK OTA

一、官方资源说明

官方指南:空中升级 (OTA) - ESP32 - — ESP-IDF 编程指南 v4.3.6 文档,虽然是正对ESP32的,但是原理是一样的。

官方参考例程:esp-idf\ESP8266_RTOS_SDK\examples\system\ota\,其中包含两个例程,一个是simple_ota_example,另一个是native_ota。simple_ota_example例程调用的是封装之后函数,只需要将固件的下载地址传入函数即可自动完成升级,而native_ota调用的是原生的函数,有更多的过程,详细展示了OTA的下载、存储过程以及校验。这里以simple_ota_example为例进行讲解。

二、修改配置文件

1、修改Partition Table

使用带ota分区的Partition Table,在flash区域中划出两块app存储空间,一块区域是当前运行的app区,另一块区域是新下载的app存放的区域。进入到工程目录中,执行make menuconfig,然后选择

Partition Table  --->

        Partition Table (Single factory app, no OTA)  --->  

                        (X) Factory app, two OTA definitions

2、允许http方式进行ota

此方式不需要证书,更简单。

 Component config  ---> 

        ESP HTTPS OTA  --->

                 [*] Allow HTTP for OTA (WARNING: ONLY FOR TESTING PURPOSE, READ HELP)  

三、编写代码

1、升级流程

上电->进入app_main()->创建升级任务(simple_ota_example_task)->执行任务->等待wifi连接并获取IP地址->http请求服务器上的固件版本号->与当前运行版本号进行比较->如果服务器上的固件版本号更高->下载新的固件->下载完成之后重启设备。

2、代码

包含头文件

#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"

定义一个版本号变量

const char *AppVer = "1.1.0";

版本比较函数

/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
int edition_compare(const char* pszStr1, const char* pszStr2)
{
	if (pszStr1 == NULL || pszStr2 == NULL) {
		return 0;
	}
	int nCurPos = 0, nCapPos=-1;
	const char* pszTmp1 = pszStr1;
	const char* pszTmp2 = pszStr2;
	while ((*pszTmp1 != '\0') && (*pszTmp2 != '\0') && (*pszTmp1 == *pszTmp2)) {	
		nCurPos++;									//找到第一个处不相同出现的位置
		pszTmp1++;
		pszTmp2++;
		if (*pszTmp1 == '.') {
			nCapPos = nCurPos;						//记录最近的‘.’的位置
		}
	}
	if (*pszTmp1 == '\0' && *pszTmp2 == '\0') { // 两个字符串相等
		return 0;
	} else if(*pszTmp1 == '\0'){
		return -1;
	} else if(*pszTmp2 == '\0'){
		return 1;
	}else{ // 两个字符串不相等,比较大小
		pszTmp1 = pszStr1 + nCapPos + 1;
		pszTmp2 = pszStr2 + nCapPos + 1;

		int pszNub1=strtol(pszTmp1,NULL,10);
		int pszNub2=strtol(pszTmp2,NULL,10);

		return (pszNub1 - pszNub2);
	}
}

创建设备事件,用于等待网络连接

const int CONNECTED_BIT = BIT0;

s_device_event_group = xEventGroupCreate();

 event_handler()函数中加入s_device_event_group的设置

/**
  * @brief  event_handler.
  * @param  None.
  * @retval None.
  */
static void event_handler(void* arg, esp_event_base_t event_base,
						  int32_t event_id, void* event_data)
{
	if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 
	{
		esp_wifi_connect();
	}
	else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 
	{	
		xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
		xEventGroupClearBits(s_device_event_group, WIFI_CONNECTED_BIT);
		esp_wifi_connect();
	}
	else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
	{
		xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
		xEventGroupSetBits(s_device_event_group, WIFI_CONNECTED_BIT);
	}
	else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) 
	{
		ESP_LOGI(TAG, "Scan done");
	}
	else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) 
	{
		ESP_LOGI(TAG, "Found channel");
	} 
	else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) 
	{
		ESP_LOGI(TAG, "Got SSID and password");

		smartconfig_event_got_ssid_pswd_t* evt = (smartconfig_event_got_ssid_pswd_t*)event_data;
		wifi_config_t wifi_config;
		uint8_t ssid[33] = { 0 };
		uint8_t password[65] = { 0 };
		uint8_t rvd_data[33] = { 0 };

		bzero(&wifi_config, sizeof(wifi_config_t));
		memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
		memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
		wifi_config.sta.bssid_set = evt->bssid_set;

		if (wifi_config.sta.bssid_set == true) {
			memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
		}

		memcpy(ssid, evt->ssid, sizeof(evt->ssid));
		memcpy(password, evt->password, sizeof(evt->password));
		ESP_LOGI(TAG, "SSID:%s", ssid);
		ESP_LOGI(TAG, "PASSWORD:%s", password);
		if (evt->type == SC_TYPE_ESPTOUCH_V2) {
			ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
			ESP_LOGI(TAG, "RVD_DATA:%s", rvd_data);
		}

		ESP_ERROR_CHECK(esp_wifi_disconnect());
		ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
		ESP_ERROR_CHECK(esp_wifi_connect());
	} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
		xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
	}
}

http事件处理函数 

/**
  * @brief  Main.
  * @param  None.
  * @retval None.
  */
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return ESP_OK;
}

任务函数

/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
static void simple_ota_example_task(void * pvParameter)
{
	EventBits_t uxBits = xEventGroupWaitBits(s_device_event_group, WIFI_CONNECTED_BIT, pdFALSE, false, portMAX_DELAY ); 

	if( (uxBits & WIFI_CONNECTED_BIT) == WIFI_CONNECTED_BIT )
	{
		ESP_LOGI(TAG, "Starting OTA example...");

		char local_response_buffer[101] = {0};
		
	    esp_http_client_config_t config0 = {
			.url = "http://www.huochaigun.top/hello-world/version.html",
	        .event_handler = _http_event_handler,
//	        .user_data = local_response_buffer,        // Pass address of local buffer to get response
	        .buffer_size = 100
	    };
	    esp_http_client_handle_t client = esp_http_client_init(&config0);		

	    // GET
	    esp_err_t err = esp_http_client_perform(client);
	    if (err == ESP_OK) {
	        ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
	                esp_http_client_get_status_code(client),
	                esp_http_client_get_content_length(client));
	    } else {
	        ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
	    }
		
		esp_http_client_read_response( client, local_response_buffer, 100 );
		local_response_buffer[ esp_http_client_get_content_length(client) ] = '\0';
		
		ESP_LOGI(TAG,"http response:%s\n", local_response_buffer );
		
		esp_http_client_cleanup(client);

		if( edition_compare(local_response_buffer, AppVer) > 0 )
		{
			esp_http_client_config_t config = {

				.url = "http://www.huochaigun.top/hello-world/hello-world.bin",
				.event_handler = _http_event_handler,
			};
			esp_err_t ret = esp_https_ota(&config);
			if (ret == ESP_OK) {
				esp_restart();
			} else {
				ESP_LOGE(TAG, "Firmware Upgrades Failed");
			}
		}
		else
		{
			ESP_LOGI(TAG, "The current software is the latest version");
		}
	}

	vTaskDelete( NULL );
}

app_main()中加入以下代码创建任务 

xTaskCreate(simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);

 四、编译固件

执行make之后,会新生成4个bin文件,将4个bin文件下载到设备即可。

0xd000 ota_data_initial.bin
0x0 bootloader.bin
0x10000 hello-world.bin
0x8000 partitions_two_ota.bin

五、上传新的应用到服务器

可修改代码再次编译应用,然后将hello-world.bin上传到服务器。如将

const char *AppVer = "1.1.0";

改为:

const char *AppVer = "1.1.1";

 再次编译应用,编译指令:make app

将新生成的hello-world.bin放到服务器的目录下,如:/htdocs/hello-world,可使用FTP工具上传。

version.html的内容是应用的版本号,内容:

1.1.1

可提供有偿技术支持。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火柴棍mcu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值