跳到主要内容

如何实现由设备和手机应用主动检查 OTA 升级的功能?

本文说明 ESP RainMaker 中用户确认 OTA 的工作流程与实现。与管理面板中的 "User Approval" 选项不同,该方案让节点自行决定是立即应用升级,还是等待用户明确确认,从而对升级过程提供更好的控制和安全性。

组件​

节点端​

  • OTA 服务(esp.service.ota),包含两个参数:
    • Operation(esp.param.ota_operation):用于接收指令
    • Info(esp.param.ota_info):用于共享升级信息

客户端应用​

  • 监控 Info 参数以获知可用升级
  • 提供用户确认升级的界面
  • 通过 Operation 参数发送指令

工作流程​

  1. 发起升级检查 — 可通过 CONFIG_ESP_RMAKER_OTA_AUTOFETCH 自动触发,也可通过 check 操作指令手动触发。
  2. 发现可用升级 — 节点用升级详情更新 Info 参数,并等待用户确认。上报状态为 delayed,描述为 Waiting for Approval。
  3. 确认升级 — 用户在应用中查看升级信息。用户确认后,应用发送 start 指令,并缓存任务 ID,以便后续通过 nodes/ota_status REST API 查询。
  4. 升级过程 — 节点下载固件、校验、应用升级,成功后重启。
  5. 升级监控 — 节点作为常规 OTA 流程的一部分上报升级状态。用户可使用节点 ID 和任务 ID,通过 nodes/ota_status REST API 进行监控。

JSON 结构​

节点配置 JSON​

{
"node_id": "<node-id>",
"config_version": "2020-03-20",
"devices": [],
"services": [
{
"name": "OTA",
"type": "esp.service.ota",
"params": [
{
"name": "Operation",
"type": "esp.param.ota_operation",
"data_type": "string",
"properties": ["write"],
"valid_strs": ["check", "start"]
},
{
"name": "Info",
"type": "esp.param.ota_info",
"data_type": "object",
"properties": ["read"]
}
]
}
]
}

节点参数 JSON​

初始状态​

{
"OTA": {
"Operation": "",
"Info": "{}"
}
}

发现可用升级时​

{
"OTA": {
"Operation": "",
"Info": {
"job_id": "job_12345",
"fw_version": "2.0.0"
}
}
}

参数更新消息​

从应用到节点(Operation)​

{ "OTA": { "Operation": "check" } }

或

{ "OTA": { "Operation": "start" } }

从节点到应用(Info)​

{
"OTA": {
"Info": {
"job_id": "job_12345",
"fw_version": "2.0.0"
}
}
}

实现示例​

节点端实现​

/* 创建 OTA 服务 */
esp_rmaker_device_t *ota_service = esp_rmaker_service_create("OTA", "esp.service.ota", NULL);

/* 添加带 write 属性和合法取值的 "Operation" 字符串参数 */
esp_rmaker_param_t *operation_param = esp_rmaker_param_create("Operation",
"esp.param.ota_operation",
esp_rmaker_str(""),
PROP_FLAG_WRITE);
const char *valid_strs[] = {"check", "start"};
esp_rmaker_param_add_valid_str_list(operation_param, valid_strs, 2);
esp_rmaker_device_add_param(ota_service, operation_param);

/* 添加只读的 "Info" 对象参数 */
esp_rmaker_param_t *info_param = esp_rmaker_param_create("Info",
"esp.param.ota_info",
esp_rmaker_obj("{}"),
PROP_FLAG_READ);
esp_rmaker_device_add_param(ota_service, info_param);

/* 为 OTA 服务注册写入回调 */
esp_rmaker_device_add_cb(ota_service, ota_service_write_cb, NULL);

/* 将 OTA 服务添加到节点 */
esp_rmaker_node_add_device(node, ota_service);

/* 注册自定义 OTA 回调 */
esp_rmaker_ota_config_t ota_config = {
.ota_cb = custom_ota_callback,
};
esp_rmaker_ota_enable(&ota_config, OTA_USING_TOPICS);

写入回调实现​

static esp_err_t ota_service_write_cb(const esp_rmaker_device_t *device,
const esp_rmaker_param_t *param,
const esp_rmaker_param_val_t val,
void *priv_data,
esp_rmaker_write_ctx_t *ctx)
{
if (strcmp(esp_rmaker_param_get_name(param), "Operation") == 0) {
if (strcmp(val.val.s, "start") == 0) {
ota_approved = true;
ESP_LOGI(TAG, "OTA approved.");
esp_rmaker_ota_fetch_with_delay(1);
} else if (strcmp(val.val.s, "check") == 0) {
ESP_LOGI(TAG, "Checking for OTA updates.");
esp_rmaker_ota_fetch_with_delay(1);
}
}
return ESP_OK;
}

自定义 OTA 回调​

static esp_err_t custom_ota_callback(esp_rmaker_ota_handle_t handle,
esp_rmaker_ota_data_t *ota_data)
{
esp_rmaker_param_t *info_param = esp_rmaker_device_get_param_by_name(ota_service, "Info");

/* 如果 OTA 已获确认,则继续升级 */
if (ota_approved) {
ota_approved = false; /* 确认仅对一次 fetch 有效 */
esp_err_t err = esp_rmaker_ota_default_cb(handle, ota_data);
/* OTA 升级成功后清空 Info 参数 */
esp_rmaker_param_update_and_report(info_param, esp_rmaker_obj("{}"));
return err;
}

/* 用任务 ID 和固件版本更新 Info 参数 */
if (info_param && (ota_data->ota_job_id || ota_data->fw_version)) {
json_gen_str_t jstr;
char json_str[128];
json_gen_str_start(&jstr, json_str, sizeof(json_str), NULL, NULL);
json_gen_start_object(&jstr);
if (ota_data->ota_job_id) {
json_gen_obj_set_string(&jstr, "job_id", ota_data->ota_job_id);
}
if (ota_data->fw_version) {
json_gen_obj_set_string(&jstr, "fw_version", ota_data->fw_version);
}
json_gen_end_object(&jstr);
json_gen_str_end(&jstr);
esp_rmaker_param_update_and_report(info_param, esp_rmaker_obj(json_str));
}

esp_rmaker_ota_report_status(handle, OTA_STATUS_DELAYED, "Waiting for Approval");
return ESP_OK;
}

配置选项​

以下选项位于 menuconfig 的 ESP RainMaker Config → ESP RainMaker OTA Config 下:

配置项默认值说明
CONFIG_ESP_RMAKER_OTA_AUTOFETCHy启动时自动获取 OTA 升级
CONFIG_ESP_RMAKER_OTA_AUTOFETCH_PERIOD0(仅启动时一次)周期性检查间隔,单位为小时(0–168)

控制台命令(用于测试)​

使用 esp_rmaker_console_init() 初始化控制台后,可以注册 ota 命令:

static int ota_cmd_handler(int argc, char **argv)
{
if (argc < 2) {
ESP_LOGE(TAG, "Invalid arguments. Use 'ota start' or 'ota check'.");
return ESP_ERR_INVALID_ARG;
}

if (strcmp(argv[1], "start") == 0) {
ota_approved = true;
ESP_LOGI(TAG, "OTA approved via console command.");
esp_rmaker_ota_fetch_with_delay(1);
} else if (strcmp(argv[1], "check") == 0) {
ESP_LOGI(TAG, "Checking for OTA updates via console command.");
esp_rmaker_ota_fetch_with_delay(1);
} else {
ESP_LOGE(TAG, "Unknown argument: %s. Use 'start' or 'check'.", argv[1]);
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}

void register_ota_command()
{
const esp_console_cmd_t ota_cmd = {
.command = "ota",
.help = "Manage OTA updates. Usage: ota <start|check>",
.hint = NULL,
.func = &ota_cmd_handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&ota_cmd));
}

用法:

ota check    # 检查升级
ota start # 开始已确认的升级

On this page