IDF 附带了一个非常简单的 OTA 示例。正如您对问题的评论中所建议的那样,该示例下载固件更新,如果更新版本存在导致重置的错误,它将自动恢复到以前的图像。这是示例的操作部分:
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
esp_err_t _ota_http_event_handler(esp_http_client_event_t* evt);
#define FIRMWARE_UPGRADE_URL "https://yourserver.com/somefolder/somefirmware.bin"
void simple_ota_task(void* pvParameter)
{
esp_http_client_config_t config = {
.url = FIRMWARE_UPGRADE_URL,
.cert_pem = (char*)server_cert_pem_start,
.event_handler = _ota_http_event_handler,
};
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK)
ESP_LOGW("ota", "Upgrade success");
else
ESP_LOGE(TAG, "Upgrade failure");
esp_restart();
}
void foo()
{
// after initializing WiFi, create a task to execute OTA:
//
xTaskCreate(&simple_ota_task, "ota_task", 8192, NULL, 5, NULL);
}
// event handler callback referenced above, it has no functional purpose
// just dumps info log output
esp_err_t _ota_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;
}
要设置嵌入证书,请通过检查 bin 文件所在站点的 SSL 证书来获取公钥,使用 Web 浏览器保存它,然后将其转换为 Base64,即 PEM 格式。(我使用了一些网页。)在我的例子中,我创建了一个与 /main 目录相同级别的目录,称为 /server_certs,并将 Base64 转换保存在该目录中作为 ca_cert.pem。(这也太迂腐了吧?)
然后将这些行添加到 /main 目录中的 CMakeFiles.txt 中:
# Embed the server root certificate into the final binary
set(COMPONENT_EMBED_TXTFILES ${IDF_PROJECT_PATH}/server_certs/ca_cert.pem)
register_component()
我不清楚的是如何确定是否有更新的版本可用,如果有一个我无法辨别的内置方式,并且不想无缘无故下载更新,永远。所以我推出了自己的版本,在固件中嵌入了一个版本字符串,在我的服务器上创建了一个返回当前版本的 WebAPI 入口点(两者都是手工维护的,直到我能想到更好的方法......我想实现)和当字符串不匹配时调用 OTA 功能。它看起来很像这样:
// The WebAPI returns data in JSON format,
// so if a method returns a string it is quoted.
#define FW_VERSION "\"00.09.015\""
// the HTTP request has just completed, payload stored in recv_buf
//
// tack a null terminator using an index maintained by the HTTP transfer
recv_buf[pos + 1] = 0;
// test for success
if (strncmp(recv_buf, "HTTP/1.1 200 OK", 15) == 0)
{
// find the end of the headers
char *p = strstr(recv_buf, "\r\n\r\n");
if (p)
{
ESP_LOGI("***", "version: %s content %s", FW_VERSION, (p + 4));
if (strcmp((p + 4), FW_VERSION) > 0)
{
// execute OTA task
foo();
}
else
{
// Assumes the new version has run far enough to be
// considered working, commits the update. It doesn't
// hurt anything to call this any number of times.
esp_ota_mark_app_valid_cancel_rollback();
}
}
}
如果您不使用 CMake/Ninja 构建工具,请考虑检查一下,它比基于 MingW32 的工具集快得多。