目录
前言
本文是基于ESP32学习笔记(基于IDF):ESP32接入OneNET该片文章的延续,继成功接入OneNet后基于OneNet进行OTA的功能
一、相关知识
1、OTA框架

2、OTA分区

3、APP镜像

4、OTA流程

5、订阅、回复OTA升级通知消息格式
咱的ESP32需要订阅OneNet平台的升级topic,当需要升级时,平台会下发升级通知

这个是我们收到OneNet平台的升级通知后,设备进行应答回复的消息格式

6、MQTT协议和HTTP协议
HTTP :是为互联网内容(如网页、API)设计的请求-响应协议。
MQTT :是为物联网设备设计的发布-订阅协议。
| 特性 | MQTT | HTTP |
|---|---|---|
| 通信模型 | 发布-订阅 | 请求-响应 |
| 工作方式 | 客户端连接到代理,订阅感兴趣的主题。当有客户端向该主题发布消息时,代理会将消息推送给所有订阅者。 | 客户端主动向服务器发起请求,服务器处理请求后返回一个响应。通信总是由客户端发起。 |
| 连接关系 | 多对多,通过代理解耦。发布者和订阅者不需要知道彼此的存在。 | 一对一,紧密耦合。客户端必须知道服务器的确切地址。 |
二、代码编写、操作流程
1、新建这两个文件,在里面写有关OTA的代码
2、在根目录的CMakeLists.txt文件下写入该语句,用来确认当前工程的版本
bash
set(PROJECT_VER "1.0.0")

这样在编译的时候,构建系统就会把这个字符串(版本号)写入esp_app_desc_t这个结构体

3、启用APP的回滚功能,就是OTA后如果发现这个新程序不好使,则会自动返回运行OTA前的程序
在工程目录下,输入以下指令,开启工程配置
bash
idf.py menuconfig

然后进到这个Bootloader 配置

然后启用这个APP回滚功能

4、在onenet_ota.c文件下写入如下代码

c
#include "esp_ota_ops.h" //引入 ESP32 OTA相关操作库
#include <stdio.h>
#include "esp_http_client.h"
#include "onenet_token.h"
#include "onenet_mqtt.h"
#include <string.h>
#include "cJSON.h"
#include "esp_https_ota.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#define TAG "onenet_ota"
#define TOKEN_VALID_TIMESTAMP 1791515811 //时间有效期
#define ONENET_OTA_URL "http://iot-api.heclouds.com/fuse-ota"
static char target_version[32] = {0};
static int task_id = 0;
static bool ota_is_running = false;
const char* get_app_version()
{
// 静态缓冲区,用于存储版本号(仅初始化一次,避免重复分配内存)
static char app_version[32] = {0};
// 如果缓冲区未初始化(首次调用),则读取版本信息
if(app_version[0] == 0)
{
// 获取当前正在运行的固件分区(OTA 中可能有多个固件分区,这里定位当前激活的分区)
const esp_partition_t * running = esp_ota_get_running_partition();
// 存储固件描述信息的结构体(包含版本号、编译时间等)
esp_app_desc_t app_desc;
// 从当前运行的分区中读取固件描述信息(包括 version 字段)
esp_ota_get_partition_description(running,&app_desc);
// 将固件版本号(app_desc.version)复制到缓冲区中
snprintf(app_version,sizeof(app_version),"%s",app_desc.version);
}
return app_version;
}
void set_app_valid(int valid)
{
// 获取当前正在运行的固件分区
const esp_partition_t * running = esp_ota_get_running_partition();
esp_ota_img_states_t state; // 存储当前固件的状态(如:待验证、有效、无效等)
// 获取当前固件分区的状态(检查是否处于"待验证"状态)
if(esp_ota_get_state_partition(running,&state) == ESP_OK)
{
// 仅当固件处于"待验证"状态时,才执行标记操作
// (OTA 升级后,新固件首次启动时会处于此状态,等待确认)
if(state == ESP_OTA_IMG_PENDING_VERIFY)
{
if(valid)
{
// 标记当前固件为有效,取消回滚(确认升级成功)
esp_ota_mark_app_valid_cancel_rollback();
}
else
{
// 标记当前固件为无效,触发回滚到上一版本并重启(升级失败)
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
}
#define OTA_BUFF_LEN 1024
static uint8_t ota_data_buff[OTA_BUFF_LEN];
static int ota_data_size = 0;
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_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
printf("HTTP_EVENT_ON_DATA data=%.*s\r\n",evt->data_len,(char*)evt->data);
int copy_len = 0;
if(evt->data_len > OTA_BUFF_LEN - ota_data_size)
{
copy_len = OTA_BUFF_LEN - ota_data_size;
}
else
{
copy_len = evt->data_len;
}
memcpy(&ota_data_buff,evt->data,copy_len);
ota_data_size += copy_len;
break;
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
case HTTP_EVENT_REDIRECT:
ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
break;
default:break;
}
return ESP_OK;
}
static esp_err_t onenet_ota_http_connect(const char *url,esp_http_client_method_t method,const char* payload)
{
esp_http_client_config_t config = {
.url = url,
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
char *token = (char*)malloc(256);
memset(token,0,256);
dev_token_generate(token,SIG_METHOD_SHA256,TOKEN_VALID_TIMESTAMP,ONENET_PRODUCT_ID,NULL,ONENET_PRODUCT_ACCESS_KEY);
ESP_LOGI(TAG,"ota token:%s",token);
// POST
esp_http_client_set_method(client, method);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_header(client,"host","iot-api.heclouds.com");
esp_http_client_set_header(client,"Authorization",token);
if(payload)
{
ESP_LOGI(TAG,"post data:%s",payload);
esp_http_client_set_post_field(client, payload, strlen(payload));
}
memset(ota_data_buff,0,sizeof(ota_data_buff));
ota_data_size = 0;
esp_err_t err = esp_http_client_perform(client);
free(token);
esp_http_client_cleanup(client);
return err;
}
//检测升级任务
esp_err_t onenet_ota_check_task(const char* type,const char* version)
{
char url[128];
esp_err_t ret = ESP_FAIL;
snprintf(url,256,ONENET_OTA_URL"/%s/%s/check?type=%s&version=%s",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME,type,version);
if(ESP_OK == onenet_ota_http_connect(url,HTTP_METHOD_GET,NULL))
{
cJSON *root = cJSON_Parse((const char*)ota_data_buff);
if(root)
{
cJSON* code_js = cJSON_GetObjectItem(root,"code");
cJSON* data_js = cJSON_GetObjectItem(root,"data");
cJSON* target_js = cJSON_GetObjectItem(data_js,"target");
cJSON* tid_js = cJSON_GetObjectItem(data_js,"tid");
if(code_js && cJSON_GetNumberValue(code_js) == 0)
{
if(target_js && tid_js)
{
snprintf(target_version,sizeof(target_version),"%s",cJSON_GetStringValue(target_js));
task_id = cJSON_GetNumberValue(tid_js);
ret = ESP_OK;
}
}
else
{
ESP_LOGI(TAG,"check ota task invalid code");
}
cJSON_Delete(root);
}
}
return ret;
}
//上报版本号给OneNet
esp_err_t onenet_ota_upload_version(void)
{
char url[128];
char version_info[128];
esp_err_t ret = ESP_FAIL;
const char* version = get_app_version();
snprintf(version_info,sizeof(version_info),"{\"s_version\":\"%s\", \"f_version\": \"%s\"}",version,version);
snprintf(url,256,ONENET_OTA_URL"/%s/%s/version",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
if(ESP_OK == onenet_ota_http_connect(url,HTTP_METHOD_POST,version_info))
{
cJSON *root = cJSON_Parse((const char*)ota_data_buff);
if(root)
{
cJSON* code_js = cJSON_GetObjectItem(root,"code");
if(code_js && cJSON_GetNumberValue(code_js) == 0)
{
ret = ESP_OK;
}
cJSON_Delete(root);
}
}
else
{
ESP_LOGI(TAG,"Upload version fail!");
return ret;
}
return ret;
}
//上报固件下载进度
esp_err_t onenet_ota_upload_status(int tid,int step)
{
char url[128];
char payload[16];
esp_err_t ret = ESP_FAIL;
snprintf(url,sizeof(url),ONENET_OTA_URL"/%s/%s/%d/status",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME,tid);
snprintf(payload,sizeof(payload),"{\"step\":%d} ",step);
if(ESP_OK == onenet_ota_http_connect(url,HTTP_METHOD_POST,payload))
{
cJSON *root = cJSON_Parse((const char*)ota_data_buff);
if(root)
{
cJSON* code_js = cJSON_GetObjectItem(root,"code");
if(code_js && cJSON_GetNumberValue(code_js) == 0)
{
ret = ESP_OK;
}
cJSON_Delete(root);
}
}
if(ret != ESP_OK)
{
ESP_LOGI(TAG,"Upload task status fail!");
}
return ret;
}
esp_err_t onenet_ota_init_cb(esp_http_client_handle_t client)
{
static char token[256];
memset(token,0,256);
dev_token_generate(token,SIG_METHOD_SHA256,TOKEN_VALID_TIMESTAMP,ONENET_PRODUCT_ID,NULL,ONENET_PRODUCT_ACCESS_KEY);
// POST
esp_http_client_set_method(client, HTTP_METHOD_GET);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_header(client,"host","iot-api.heclouds.com");
esp_http_client_set_header(client,"Authorization",token);
return ESP_OK;
}
//下载固件
esp_err_t onenet_ota_download(int tid)
{
char url[128];
snprintf(url,sizeof(url),"http://iot-api.heclouds.com/fuse-ota/%s/%s/%d/download",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME,tid);
esp_http_client_config_t http_cfg =
{
.url = url,
};
esp_https_ota_config_t ota_cfg =
{
.http_config = &http_cfg,
.http_client_init_cb = onenet_ota_init_cb,
};
esp_err_t ota_ret = ESP_FAIL;
ota_ret = esp_https_ota(&ota_cfg);
if(ota_ret == ESP_OK)
{
ESP_LOGI(TAG,"Upgrade successful...");
}
else
{
ESP_LOGI(TAG,"Upgrade fail! Code:%d",ota_ret);
}
return ota_ret;
}
static void onenet_ota_task(void* param)
{
esp_err_t ret = ESP_FAIL;
//上报版本号
ret = onenet_ota_upload_version();
if(ret != ESP_OK)
{
ESP_LOGE(TAG,"Upload version fail!");
goto delete_ota_task;
}
//检测升级任务
ret = onenet_ota_check_task("1",get_app_version());
if(ret != ESP_OK)
{
ESP_LOGE(TAG,"Check task fail!");
goto delete_ota_task;
}
//上报任务升级状态 10%
ret = onenet_ota_upload_status(task_id,10);
if(ret != ESP_OK)
{
ESP_LOGE(TAG,"Upload status fail!");
goto delete_ota_task;
}
//进行HTTP下载
ret = onenet_ota_download(task_id);
if(ret != ESP_OK)
{
ESP_LOGE(TAG,"download fail!");
goto delete_ota_task;
}
//上报任务升级状态 100%
ret = onenet_ota_upload_status(task_id,100);
if(ret != ESP_OK)
{
ESP_LOGE(TAG,"Upload status fail!");
goto delete_ota_task;
}
//重启
esp_restart();
delete_ota_task:
ota_is_running = false;
vTaskDelete(NULL);
}
void onenet_ota_start(void)
{
if(ota_is_running)
{
return;
}
ota_is_running = true;
ESP_LOGI(TAG,"Start OTA");
xTaskCreatePinnedToCore(onenet_ota_task,"onenet_ota",8192,NULL,2,NULL,0);
}
5、在onenet_ota.h文件下,写入如下代码

c
#ifndef _ONENET_OTA_H_
#define _ONENET_OTA_H_
#include "esp_err.h"
const char* get_app_version(void);
void set_app_valid(int valid);
esp_err_t onenet_ota_upload_version(void);
void onenet_ota_start(void);
#endif
6、在onenet_mqtt.c文件下写入如下代码
c
#include "onenet_mqtt.h"
#include "onenet_token.h"
#include "mqtt_client.h"
#include "esp_log.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "cJSON.h"
#include "onenet_dm.h"
#include "onenet_ota.h"
#define TAG "onenet"
static esp_mqtt_client_handle_t mqtt_handle = NULL;
static void onenet_property_ack(const char* id,int code,const char* msg);
static void onenet_ota_ack(const char* id, int code, const char* msg);
static void onenet_subscribe(void);
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
onenet_subscribe();
cJSON* property_js = onenet_property_upload();
char* data = cJSON_PrintUnformatted(property_js);
onenet_post_property_data(data);
onenet_ota_upload_version();//上报版本号
set_app_valid(true); //设置当前程序是合法的
cJSON_free(data);
cJSON_Delete(property_js);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
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);
if(strstr(event->topic,"property/set"))
{
cJSON* property = cJSON_Parse(event->data);
cJSON* id_js = cJSON_GetObjectItem(property,"id");
onenet_property_handle(property);
onenet_property_ack(cJSON_GetStringValue(id_js),200,"success");
cJSON_Delete(property);
}
else if(strstr(event->topic,"ota/inform"))
{
cJSON* ota_js = cJSON_Parse(event->data);
cJSON* id_js = cJSON_GetObjectItem(ota_js,"id");
onenet_ota_ack(cJSON_GetStringValue(id_js),200,"success");
cJSON_Delete(ota_js);
//开始OTA升级流程
onenet_ota_start();
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
esp_err_t onenet_start(void)
{
esp_mqtt_client_config_t mqtt_config;
memset(&mqtt_config,0,sizeof(esp_mqtt_client_config_t));
mqtt_config.broker.address.uri = "mqtt://mqtts.heclouds.com";
mqtt_config.broker.address.port = 1883;
mqtt_config.credentials.client_id = ONENET_DEVICE_NAME;
mqtt_config.credentials.username = ONENET_PRODUCT_ID;
static char token[256];
dev_token_generate(token,SIG_METHOD_SHA256,2232933411,ONENET_PRODUCT_ID,NULL,ONENET_PRODUCT_ACCESS_KEY);
mqtt_config.credentials.authentication.password = token;
//将鉴权信息打印出来
ESP_LOGI(TAG,"onenet connect->clientId:%s,username:%s,password:%s",
mqtt_config.credentials.client_id,mqtt_config.credentials.username,
mqtt_config.credentials.authentication.password);
mqtt_handle = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_handle,ESP_EVENT_ANY_ID,mqtt_event_handler,NULL);
return esp_mqtt_client_start(mqtt_handle);
}
static void onenet_property_ack(const char* id, int code, const char* msg)
{
char topic[128];
snprintf(topic,128,"$sys/%s/%s/thing/property/set_reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
cJSON* reply_js = cJSON_CreateObject();
cJSON_AddStringToObject(reply_js,"id",id);
cJSON_AddNumberToObject(reply_js,"code",code);
cJSON_AddStringToObject(reply_js,"msg",msg);
char* data = cJSON_PrintUnformatted(reply_js);
esp_mqtt_client_publish(mqtt_handle,topic,data,strlen(data),1,0);
cJSON_free(data);
cJSON_Delete(reply_js);
}
static void onenet_ota_ack(const char* id, int code, const char* msg)
{
char topic[128];
snprintf(topic,128,"$sys/%s/%s/thing/ota/inform_reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
cJSON* reply_js = cJSON_CreateObject();
cJSON_AddStringToObject(reply_js,"id",id);
cJSON_AddNumberToObject(reply_js,"code",code);
cJSON_AddStringToObject(reply_js,"msg",msg);
char* data = cJSON_PrintUnformatted(reply_js);
esp_mqtt_client_publish(mqtt_handle,topic,data,strlen(data),1,0);
cJSON_free(data);
cJSON_Delete(reply_js);
}
static void onenet_subscribe(void)
{
char topic[128];
sniprintf(topic,128,"$sys/%s/%s/thing/property/post/reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);
//订阅属性设置主题
sniprintf(topic,128,"$sys/%s/%s/thing/property/set",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);
//订阅OTA升级通知主题
sniprintf(topic,128,"$sys/%s/%s/ota/inform",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);
}
esp_err_t onenet_post_property_data(const char* data)
{
char topic[128];
snprintf(topic,128,"$sys/%s/%s/thing/property/post",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);
ESP_LOGI(TAG,"Upload topic:%s,payload:%s",topic,data);
return esp_mqtt_client_publish(mqtt_handle,topic,data,strlen(data),1,0);
}

7、在onenet_mqtt.h文件下写入如下代码
c
#ifndef _ONENET_MQTT_H_
#define _ONENET_MQTT_H_
#include "esp_err.h"
//产品ID
#define ONENET_PRODUCT_ID "E3Sgq5fGjC"
//产品密钥
#define ONENET_PRODUCT_ACCESS_KEY "a82bv2/LWRePdSfT+cmRDw2Jt+8bl3NZJxOxbdN3O6M="
//设备名称
#define ONENET_DEVICE_NAME "esp32led01"
esp_err_t onenet_start(void);
esp_err_t onenet_post_property_data(const char* data);
#endif

8、在onenet.c文件下写入如下代码
c
#include <stdio.h>
#include "onenet_mqtt.h"
#include "wifi_manager.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "onenet_dm.h"
#include "esp_log.h"
static EventGroupHandle_t wifi_ev = NULL;
#define WIFI_CONNECT_BIT BIT0
#define TAG "app_main"
/**
* wifi事件回调函数
* @param ev wifi事件
* @return 无
*/
static void wifi_state_callback(WIFI_STATE state)
{
if(state == WIFI_STATE_CONNECTED)
{
xEventGroupSetBits(wifi_ev,WIFI_CONNECT_BIT);
}
}
void app_main(void)
{
esp_err_t ret;
// 1. 初始化 NVS 闪存(必须处理返回值!)
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS 分区满了或版本不兼容,尝试擦除后重新初始化
ESP_LOGE(TAG, "NVS init failed, try to erase...");
ret = nvs_flash_erase();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "NVS erase failed: %s", esp_err_to_name(ret));
return; // 无法修复,直接退出
}
ret = nvs_flash_init(); // 重新初始化
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "NVS init failed: %s", esp_err_to_name(ret));
return; // NVS 初始化失败,无法继续
}
ESP_LOGI(TAG, "NVS init success");
// 2. 创建事件组(用于同步 Wi-Fi 连接状态)
// 事件组是 FreeRTOS 提供的同步机制,这里用于标记 Wi-Fi 是否连接成功
wifi_ev = xEventGroupCreate();
// 3. 初始化 Wi-Fi 管理器,并注册状态回调函数
// wifi_manager_init:初始化 Wi-Fi 相关资源(如配置 Wi-Fi 模式、注册事件处理等)
// wifi_state_callback:自定义回调函数,用于接收 Wi-Fi 连接/断开等状态通知
onenet_dm_init();
wifi_manager_init(wifi_state_callback);
// 4. 调用 Wi-Fi 管理器连接指定的 Wi-Fi 热点
// 参数:Wi-Fi 热点名称(SSID)为 "Lymow",密码为 "1415926535"
wifi_manager_connect("Lymow","1415926535");//连接的WIFI用户名跟密码
EventBits_t ev;
while(1)
{
//等待WIFI连接成功,然后才开始连接onenet
ev = xEventGroupWaitBits(wifi_ev,WIFI_CONNECT_BIT,pdTRUE,pdFALSE,pdMS_TO_TICKS(10*1000));
if(ev & WIFI_CONNECT_BIT)
{
// Wi-Fi 已就绪,启动 OneNet 平台连接(如 MQTT 连接)
onenet_start();
}
}
}

9、输入以下指令进入配置菜单

10、在这个路径下,选择第三个选项,一个出厂默认固件+两个OTA分区

11、在这个路径下,打开HTTP之中OTA的功能,不过正式产品的开发不能用HTTP来OTA,得用HTTPS,因为HTTP是不加密的,很危险,这个只能用来在本地测试OTA流程是否跑通

12、flash这里选择4M,因为我的C3芯片模组的FLAH最大是4M

13、主频选择160M,根据自己的芯片选择最大即可

14、在CMakeLists文件下写入如下代码
bash
idf_component_register(SRCS "iot/onenet_dm.c" "iot/onenet_token.c" "iot/wifi_manager.c" "iot/onenet_mqtt.c" "iot/onenet_ota.c" "onenet.c"
INCLUDE_DIRS "." "iot")

15、在终端,使用以下指令对工程进行编译
bash
idf.py build

16、编译成功

17、使用以下指令把程序下载进板子,并打开监控
bash
idf.py flash monitor

18、可以看到程序跑起来了,上报版本号之后,OneNet平台进行了回复

19、我们把软件版本改成1.0.1,然后重新编译一个固件

20、我们在OneNet平台点击添加升级包

21、这里填一下,然后上传固件

22、点这个验证升级

23、这里填一下,然后开始验证

24、升级成功

25、可以看到平台这边也显示验证成功了
