ESP32入门基础之空中升级(OTA)

Posted while(1)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32入门基础之空中升级(OTA)相关的知识,希望对你有一定的参考价值。

文章目录


参考资料:

  1. 空中升级 (OTA)编程指南
  2. OTA 例程说明

1 OTA简介

2 OTA操作流程

3 OTA操作实例

3.1 使用http实现简单的OTA升级

参考例程:examples\\system\\ota\\simple_ota_example

3.1.1 例程简介

3.1.2 在本地生成简单的HTTP服务器

  1. 选择要升级的程序(bin文件);

  2. 打开命令行界面,并进入到包含要升级的程序的目录文件夹下;

  3. 输入命令 python2 -m SimpleHTTPServer 8070(有些可能是python -m SimpleHTTPServer 8070),并执行;

  4. 保持命令行模式软件不要关闭

  5. 在浏览器输入http://xxx.xxx.xxx.xxx:8070,验证http服务器是否成功;
    命令行界面也有提示输出,执行的是GET操作;

  6. 在接下来的实验中也要保持命令行模式不要关闭。

3.1.3 配置simple_ota_example例程

  1. 不要关闭http服务器的命令行界面;
  2. 重新打开一个命令行界面,进入simple_ota_example例程, 使用 idf.py menuconfig 指令进入菜单配置模式;
  3. 配置好串口、所连接的wif、确保分区表有两个OTA分区
  4. 配置要升级的程序的URL
  5. 最后编译程序并烧录到ESP32开发板
  6. 到此说明程序升级完成

3.1.4 程序分析

  1. 芯片初始化及wifi连接
void app_main(void)

    /*省略了代码,主要作用是芯片初始化、wifi连接等*/
    xTaskCreate(&simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);

void simple_ota_example_task(void *pvParameter)

    ESP_LOGI(TAG, "Starting OTA example");

    esp_http_client_config_t config = 
        .url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL, //这里即升级程序的URL,在菜单中配置
        .cert_pem = (char *)server_cert_pem_start,  //https所需要的认证,但这里是http,所以并不需要用到
        .event_handler = _http_event_handler,       
    ;
    esp_err_t ret = esp_https_ota(&config);  //连接http,并下载升级程序
    if (ret == ESP_OK) 
        esp_restart();      //下载程序成功,重启即执行升级程序
     else 
        ESP_LOGE(TAG, "Firmware upgrade failed");
    
    //一切正常的话不会执行到这里,如下升级程序下载失败则执行下列程序
    while (1) 
        vTaskDelay(1000 / portTICK_PERIOD_MS);
		ESP_LOGI(TAG, "while ---");
    

感觉这个例程没有太大的实用性,只是帮助了解OTA的基本操作。

3.2 使用阿里云物联网平台实现简单的OTA升级

3.2.1 新建工程

  1. 选择 app-MqttToAliyun 作为基础工程,在此基础上修改;
  2. 更改工程名为 app-AliyunOTA;

3.2.2 工程菜单配置

  1. 执行命令 idf.py menuconfig
  2. 修改分区表,如下
  3. 修改Flash size,如下Flash size 默认为2MB,但第二步分区表选择官方提供的OTA分区表(Factory app,two OTA definitions),所以需要4MB以上的空间,如果实际的ESP32开发板Flash size 小于4MB,则可以自定义分区表。
  4. 其余参数,如wifi、端口、波特率可根据实际情况定义

3.2.3 将升级程序上传到阿里云物联网平台

参考资料:阿里云物联网平台->设备端OTA升级

  1. 该工程是以 app-MqttToAliyun为基础,所以已经实现了ESP32设备与阿里云之间的通信。

  2. 打开阿里云物联网平台,定位到 “物联网平台/监控运维/OTA 升级 ” ,点击 “添加升级包”,如下填完参数,最后点击下方“确认”,升级包随便选择一个工程的.bin文件

  3. 升级包上传完成

  4. 点击右方“批量升级”
    点击左下角 下一步

  5. 到这里阿里云物联网平台就配置完了,只要ESP32开发板连接到阿里云平物联网平台的 ESP8266-TEST产品下的dev-esp32设备,就会收到阿里云物联网平台推送的JSON数据,(当然ESP32开发板要实现了OTA程序)

3.2.4 下载阿里云物理网平台TLS根证书配置工程

  1. 因为阿里云物联网平台使用的是https协议,所以需要TLS证书,下载地址点击这里
  2. 将阿里云服务器的TLS根证书替换到工程中的server_certs目录(如果没有则新建该目录),
  3. 进入 main 目录下,打开 CMakeLists.txt 文件,添加 root.crt: EMBED_TXTFILES $project_dir/server_certs/root.crt
  4. 打开 component.mk 文件,添加 root.crt: COMPONENT_EMBED_TXTFILES := $PROJECT_PATH/server_certs/root.crt
  5. 注意在程序中,也需要做相应的修改,如下
extern const uint8_t server_root_crt_start[] asm("_binary_root_crt_start");  //注意该地方需要修改
extern const uint8_t server_root_crt_end[]   asm("_binary_root_crt_end");

	  esp_http_client_config_t config = 	  
	      .url = url_buf,
		  .cert_pem = (char *)server_root_crt_start, 
		  .event_handler = _http_event_handler,
	  ;

3.2.5 工程编写函数

  1. 工程配置完成后,编译工程,下载程序到ESP32设备,ESP32成功连接到阿里云物联网平台后,会收到阿里云推送的OTA升级的JSON格式数据,该数据包含OTA升级包的URL、版本号等信息
    该数据由如下函数接口打印:
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)

    esp_mqtt_client_handle_t client = event->client;
    int  msg_id;
	
    // your_context_t *context = event->context;
    switch (event->event_id) 
        case MQTT_EVENT_CONNECTED:
            break;
        case MQTT_EVENT_DISCONNECTED:
            break;
        case MQTT_EVENT_SUBSCRIBED:
            break;
        case MQTT_EVENT_UNSUBSCRIBED:
            break;
        case MQTT_EVENT_PUBLISHED:
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "MQTT_EVENT_DATA");
            printf("TOPIC=%.*s\\r\\n", event->topic_len, event->topic);  //打印主题
            printf("DATA=%.*s\\r\\n", event->data_len, event->data);	   //打印数据
            break;
        case MQTT_EVENT_ERROR:
            break;
        default:
            break;
    
    return ESP_OK;

  1. 该OTA升级的JSON数据,是按如下格式打印的,详细请参考:阿里云物联网平台设备端OTA升级
  2. 将该OTA升级的JSON数据使用JSON数据在线解析工具解析,如下
  3. 在工程中将OTA的JSON数据解析,并根据解析的URL下载升级包,最后重启ESP32
#define   AliyunSubscribeTopic_ota_upgrade  "/ota/device/upgrade/a1tUbQR2faQ/dev-esp32"
#define   AliyunPublishTopic_ota_inform     "/ota/device/inform/a1tUbQR2faQ/dev-esp32"

static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)

    esp_mqtt_client_handle_t client = event->client;
    // your_context_t *context = event->context;
    switch (event->event_id) 
        ...
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "MQTT_EVENT_DATA");
            char *topicBuf = (char*)malloc(event->topic_len+1);
		   //void  *memcpy(void *s1,  const void *s2,  size_t  n); 函数memcpy从s2指向的对象中复制n个字符到s1指向的对象中。		   
           	memcpy(topicBuf, event->topic, event->topic_len);
			//printf("---Receive topic:\\n %s \\r\\n", topicBuf);//打印接收的消息
			temp = strcmp(topicBuf,AliyunSubscribeTopic_ota_upgrade); //判断主题是否是OTA主题,如果是才调用OTA的JSON解析函数
		    if(0 == temp)	
		    	
		    	ESP_LOGI(TAG, "OTA DATA PARSE");
		        //char  *strncpy(char *s2, const char *s1, size_t n);函数strncpy从s1指向的数组中最多复制n个字符(不复制空字符后面的字符)到s2指向的数组中。
				strncpy(local_data_buffer, event->data, event->data_len); //将指针类型的数据复制到一个数组中
				//printf("local_data_buffer:%s ", local_data_buffer); 
			    ret = user_parse_json(local_data_buffer,aliyun_ota_config);//解析数据,并使用解析出来的URL下载OTA升级包
				if (ret == ESP_OK) //解析并下载OTA升级包成功
				
			        //构造JSON格式数据,该数据用于反馈给阿里云物联网平台,作用是通知升级包接收完成
					cJSON *Wroot =	cJSON_CreateObject();
					cJSON *Pitem =	cJSON_CreateObject();					
					cJSON_AddItemToObject(Wroot, "id", cJSON_CreateString("123"));
					cJSON_AddItemToObject(Wroot, "params", Pitem);
					cJSON_AddItemToObject(Pitem, "step", cJSON_CreateString("100"));
					cJSON_AddItemToObject(Pitem, "desc", cJSON_CreateString("OTA update successfully !"));
					cJSON_AddItemToObject(Pitem, "module", cJSON_CreateString("MCU"));				
					//printf("%s\\n", cJSON_Print(Wroot)); //打印刚才构建的JSON数据,看是否正确
					char ota_inform_buf[512];
					int len = strlen(cJSON_Print(Wroot));					
					memcpy(ota_inform_buf, cJSON_Print(Wroot),len); //将JSON格式数据复制到数组中,将以数组的形式传递给发布函数
					ota_inform_buf[len] = '\\0';
					printf("%s\\n",ota_inform_buf);//打印数据是否正确
					//发布信息到阿里云物联网平台
		            msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_ota_inform, ota_inform_buf, strlen(ota_inform_buf), 0, 0);
		            ESP_LOGI(TAG, "sent publish ota inform successful, msg_id=%d", msg_id);		
					
//					cJSON_Delete(Wroot);  //执行这个函数会出错,不知道什么原因
//					cJSON_Delete(Pitem);
					esp_restart();  //ESP32设备重启,重启后将执行刚才下载的程序
				 
				else 
				
					ESP_LOGE(TAG, "Firmware upgrade failed");
			    	
		    
		    free(topicBuf);		
            break;
        ... 
        default:
            break;
    
    return ESP_OK;

 esp_err_t user_parse_json(char *json_data,user_aliyun_ota_config_t ota_config)

	cJSON *item = NULL;
	cJSON *root = NULL;
//root 开始-------------------------------------------------------------------------		
    root = cJSON_Parse(json_data);   /*json_data 阿里云OTA原始数据*/ 
    if (!root) 
    
      printf("Error before: [%s]\\n",cJSON_GetErrorPtr());	  
	  return  ESP_FAIL;
	
	printf("%s\\n\\n", cJSON_Print(root));   /*将完整的数据以JSON格式打印出来*/
	
	item = cJSON_GetObjectItem(root, "code");
	ota_config.code = cJSON_Print(item);
	
//data 开始-------------------------------------------------------------------------	    		
	cJSON *Pdata = cJSON_GetObjectItem(root, "data");	
    item = cJSON_GetObjectItem(Pdata, "size");
    ota_config.size = item->valueint;
    
//extData 开始-------------------------------------------------------------------------		
	cJSON *PextData = cJSON_GetObjectItem(Pdata, "extData");
	item = cJSON_GetObjectItem(PextData, "_package_udi");
	ota_config._package_udi = cJSON_Print(item);  
//extData 结束------------------------------------------------------------------------- 	

	item = cJSON_GetObjectItem(Pdata, "sign");
	ota_config.sign = cJSON_Print(item);    

	item = cJSON_GetObjectItem(Pdata, "version");
	ota_config.version = cJSON_Print(item);    

	item = cJSON_GetObjectItem(Pdata, "url");
	ota_config.url = cJSON_Print(item);    

	item = cJSON_GetObjectItem(Pdata, "signMethod");
	ota_config.signMethod = cJSON_Print(item);    

	item = cJSON_GetObjectItem(Pdata, "md5");
	ota_config.md5 = cJSON_Print(item);
//data 结束-------------------------------------------------------------------------

	item = cJSON_GetObjectItem(root, "id");
	ota_config.id = item->valuedouble;
    
	item = cJSON_GetObjectItem(root, "message");
	ota_config.message = cJSON_Print(item);
//root 结束-------------------------------------------------------------------------    
//   cJSON_Delete(root);   //执行这个函数会出错,不知道什么原因
//   cJSON_Delete(Pdata);
//   cJSON_Delete(PextData);
//   cJSON_Delete(item);

//  printf("code:%s\\n", ota_config.code);
//  printf("size:%d\\n", ota_config.size);	
//  printf("_package_udi:%s\\n", ota_config._package_udi);
//  printf("sign:%s\\n", ota_config.sign);	
//	printf("version:%s\\n", ota_config.version);
//  printf("url:%s\\n", ota_config.url);	
//  printf("signMethod:%s\\n", ota_config.signMethod);	
//  printf("md5:%s\\n", ota_config.md5);	
//	printf("id:%f\\n", ota_config.id);	
//	printf("message:%s\\n", ota_config.message);	

	  char url_buf[OTA_URL_SIZE];
	  int len = strlen(ota_config.url);

	  memcpy(url_buf, ota_config.url, len); //将指针型数据复制到数组中,

//解析出来的URL包含了双引号"",但数组中的URL不能包含双引号""
	  for(int i = 0;i < len;i++ )
	  
	     url_buf[i] = url_buf[i+1];   //为了去掉数组中的双引号“”,这里是前部分
	  

	  url_buf[len - 2] = '\\0';       //为了去掉数组中的双引号“”,这里是后部分

	  printf("buf:%s\\n", url_buf);  //打印检查URL是否正确


	  esp_http_client_config_t config = 	  
	  	//. url = "https://xxx"  //这里需要双引号""
	      .url = url_buf,        //这里,数组中不能包含双引号""
		  .cert_pem = (char *)server_root_crt_start, 
		  .event_handler = _http_event_handler,
	  ;
		
    esp_err_t ret = esp_https_ota(&config); //下载升级包等一系列操作

	return  ret;

3.2.6 实验分析

程序编写完成,编译下载到ESP32设备中,查看串口打印数据,如下

//数据开始
ets Jul 29 2019 12:21:46
...
I (61) boot: Partition Table: //分区表
I (64) boot: ## Label            Usage          Type ST Offset   Length
I (72) boot:  0 nvs              WiFi data        01 02 00009000 00004000
I (79) boot:  1 otadata          OTA data         01 00 0000d000 00002000
I (87) boot:  2 phy_init         RF data          01 01 0000f000 00001000
I (94) boot:  3 factory          factory app      00 00 00010000 00100000
I (102) boot:  4 ota_0            OTA app          00 10 00110000 00100000
I (109) boot:  5 ota_1            OTA app          00 11 00210000 00100000
I (117) boot: End of partition table
I (121) boot: Defaulting to factory image //刚编译并通过有线烧录,默认执行工厂镜像程序
...
I (498) boot: Loaded app from partition at offset 0x10000 //从偏移地址0x10000处加载程序
I (498) boot: Disabling RNG early entropy source...
I (498) cpu_start: Pro cpu up.
I (502) cpu_start: Application information:  //该程序信息
I (507) cpu_start: Project name:     app-MqttToAliyun
I (513) cpu_start: App version:      5204503a-dirty
I (518) cpu_start: Compile time:     Jul  3 2021 11:33:25
...
I (0) cpu_start: App cpu up.
...
I (1698) wifi station: connected to ap SSID:canbo-418-2.4G password:canbo418 //连接wifi
I (1708) MQTT_EXAMPLE: into mqtt_test_task //开始执行mqtt任务,即连接阿里云物联网平台
I (1708) MQTT_EXAMPLE: Other event id:7
I (1948) MQTT_EXAMPLE: MQTT_EVENT_CONNECTED
I (1948) MQTT_EXAMPLE: sent publish successful, msg_id=49855
I (1948) MQTT_EXAMPLE: sent subscribe successful, msg_id=15854
I (1998) MQTT_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=49855
I (2058) MQTT_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=15854
I (2068) MQTT_EXAMPLE: sent publish successful, msg_id=0
I (6228) MQTT_EXAMPLE: MQTT_EVENT_DATA
I (6238) MQTT_EXAMPLE: OTA DATA PARSE//收到阿里云物联网平台推送的OTA的JSON格式数据

        "code": "1000",
        "data": 
                "size": 822528,
                "extData":      
                        "_package_udi": "这是阿里云OTA测试程序"
                ,
                "sign": "72fd4a8b0e67fc0657b1ea707570e27a",
                "version":      "1.00",
                "url":  "https://iotx-ota.oss-cn-shanghai.aliyuncs.com/ota/6bc61a794bad89c4c1edf4ecf46a1666/ckqnivitr0003268hsmg81bac.bin?Expires=1625537056&OSSAccessKeyId=LTAI4G1TuWwSirnbAzUHfL3e&Signature=39CeoZ34CY1KucXl69yq1ajHqDw%3D",
                "signMethod":   "Md5",
                "md5":  "72fd4a8b0e67fc0657b1ea707570e27a"
        ,
        "id":   1625450656015,
        "message":      "success"


buf:https://iotx

以上是关于ESP32入门基础之空中升级(OTA)的主要内容,如果未能解决你的问题,请参考以下文章

ESP32学习笔记(25)——OTA(空中升级)接口使用(简化API)

ESP32学习笔记(24)——OTA(空中升级)接口使用(原生API)

给你的 ESP32 进行空中升级

给你的 ESP32 进行空中升级

IoT如何实现 ESP32 固件的 OTA 在线升级更新

STM32+ESP8266(ESP-12F)实现在线OTA升级(腾讯云物联网)