mqtt - 适用于来自 AWS 的深度睡眠设备的 FreeRTOS OTA
问题描述
背景
我有一个在 freeRTOS 上运行的小型电池供电系统。根据任何适当的互联网连接设备,我需要定期运行 OTA 更新。问题在于,在电池供电的情况下,该设备 99.9% 的寿命都处于深度睡眠状态。
当设备唤醒时,可以通过发布到设备的 OTA/更新主题从 AWS 发布 OTA 更新。从控制台,您只能使用 QOS = 0。但从内部,比如 lambda,我相信可以使用 QOS = 1。
{
"state": {
"desired": {
"ota_url":"https://s3-ap-southeast-2.amazonaws.com/my.awesome.bucket/signed_binary_v21.bin"
}
}
}
问题
- 如何修改此方法以成功更新一次睡眠 15 分钟并唤醒可能 10 秒的设备。在唤醒期间,它会发送一条消息。是否有某种方法可以将所需的 OTA/更新隐藏在 AWS 的响应中以某种方式包含在内。我还没有弄清楚阴影是如何真正起作用的。或者你可以指定一个重试周期和时间来继续尝试吗?
- 从安全角度来看,这种方法是否与最新的最佳实践基本一致:签名二进制、加密闪存和安全启动等。
非常感谢。
解决方案
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 的工具集快得多。
推荐阅读
- android - Android - 在使用带有导航组件的 CollapsingToolbar 时自定义处理导航
- iis - IIS 与任务管理器的内存消耗
- excel - 在 Excel 中修剪空格而不丢失格式
- aws-cdk - 如何将自定义 Lambda 层包含到管道堆栈中?(AWS-CDK)
- r - 了解 R 中 agrep 模糊匹配中的约束
- python - Tensorflow 给出“ValueError:检查输入时出错”
- javascript - React-Native 如何将我的 json 数据返回到我的道具?
- redis - Redis RPUSH - 特定的返回值语义
- r - 我无法取回被排除在插补模型之外的变量 (impute.transcan)
- c# - 在 WrapPanel 中将 DataTables 绑定到 DataGrids