ESPHome 2026.3.0
Loading...
Searching...
No Matches
ota_zephyr_mcumgr.cpp
Go to the documentation of this file.
1#ifdef USE_ZEPHYR
2#include "ota_zephyr_mcumgr.h"
3#include "esphome/core/log.h"
4#include "esphome/core/hal.h"
5#include <zephyr/sys/math_extras.h>
6#include <zephyr/usb/usb_device.h>
7#include <zephyr/dfu/mcuboot.h>
8
9// It should be from below header but there is problem with internal includes.
10// #include <zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h>
11// NOLINTBEGIN(readability-identifier-naming,google-runtime-int)
12struct img_mgmt_upload_action {
14 unsigned long long size;
15};
16
17struct img_mgmt_upload_req {
18 uint32_t image; /* 0 by default */
19 size_t off; /* SIZE_MAX if unspecified */
20};
21// NOLINTEND(readability-identifier-naming,google-runtime-int)
22
24
25static_assert(sizeof(struct img_mgmt_upload_action) == 8, "ABI mismatch");
26static_assert(sizeof(struct img_mgmt_upload_req) == 8, "ABI mismatch");
27static_assert(offsetof(struct img_mgmt_upload_req, image) == 0, "ABI mismatch");
28static_assert(offsetof(struct img_mgmt_upload_req, off) == 4, "ABI mismatch");
29
30static const char *const TAG = "zephyr_mcumgr";
31static OTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
32
33static enum mgmt_cb_return mcumgr_img_mgmt_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc,
34 uint16_t *group, bool *abort_more, void *data, size_t data_size) {
35 if (MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK == event) {
36 const img_mgmt_upload_check &upload = *static_cast<img_mgmt_upload_check *>(data);
37 global_ota_component->update_chunk(upload);
38 } else if (MGMT_EVT_OP_IMG_MGMT_DFU_STARTED == event) {
39 global_ota_component->update_started();
40 } else if (MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE == event) {
41 global_ota_component->update_chunk_wrote();
42 } else if (MGMT_EVT_OP_IMG_MGMT_DFU_PENDING == event) {
43 global_ota_component->update_pending();
44 } else if (MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED == event) {
45 global_ota_component->update_stopped();
46 } else {
47 ESP_LOGD(TAG, "MCUmgr Image Management Event with the %d ID", u32_count_trailing_zeros(MGMT_EVT_GET_ID(event)));
48 }
49 return MGMT_CB_OK;
50}
51
52OTAComponent::OTAComponent() { global_ota_component = this; }
53
55 this->img_mgmt_callback_.callback = mcumgr_img_mgmt_cb;
56 this->img_mgmt_callback_.event_id = MGMT_EVT_OP_IMG_MGMT_ALL;
57 mgmt_callback_register(&this->img_mgmt_callback_);
58#ifdef CONFIG_USB_DEVICE_STACK
59 usb_enable(nullptr);
60#endif
61// Handle OTA rollback: mark partition valid immediately unless USE_OTA_ROLLBACK is enabled,
62// in which case safe_mode will mark it valid after confirming successful boot.
63#ifndef USE_OTA_ROLLBACK
64 if (!boot_is_img_confirmed()) {
65 boot_write_img_confirmed();
66 }
67#endif
68}
69
70#ifdef ESPHOME_LOG_HAS_CONFIG
71static const char *swap_type_str(uint8_t type) {
72 switch (type) {
73 case BOOT_SWAP_TYPE_NONE:
74 return "none";
75 case BOOT_SWAP_TYPE_TEST:
76 return "test";
77 case BOOT_SWAP_TYPE_PERM:
78 return "perm";
79 case BOOT_SWAP_TYPE_REVERT:
80 return "revert";
81 case BOOT_SWAP_TYPE_FAIL:
82 return "fail";
83 }
84
85 return "unknown";
86}
87#endif
88
90 ESP_LOGCONFIG(TAG,
91 "Over-The-Air Updates:\n"
92 " swap type after reboot: %s\n"
93 " image confirmed: %s",
94 swap_type_str(mcuboot_swap_type()), YESNO(boot_is_img_confirmed()));
95}
96
97void OTAComponent::update_chunk(const img_mgmt_upload_check &upload) {
98 float percentage = (upload.req->off * 100.0f) / upload.action->size;
99 this->defer([this, percentage]() { this->percentage_ = percentage; });
100}
101
103 this->defer([this]() {
104 ESP_LOGD(TAG, "Starting update");
105#ifdef USE_OTA_STATE_LISTENER
106 this->notify_state_(ota::OTA_STARTED, 0.0f, 0);
107#endif
108 });
109}
110
112 uint32_t now = millis();
113 if (now - this->last_progress_ > 1000) {
114 this->last_progress_ = now;
115 this->defer([this]() {
116 ESP_LOGD(TAG, "OTA in progress: %0.1f%%", this->percentage_);
117#ifdef USE_OTA_STATE_LISTENER
119#endif
120 });
121 }
122}
123
125 this->defer([this]() {
126 ESP_LOGD(TAG, "OTA pending");
127#ifdef USE_OTA_STATE_LISTENER
128 this->notify_state_(ota::OTA_COMPLETED, 100.0f, 0);
129#endif
130 });
131}
132
134 this->defer([this]() {
135 ESP_LOGD(TAG, "OTA stopped");
136#ifdef USE_OTA_STATE_LISTENER
137 this->notify_state_(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(ota::OTA_RESPONSE_ERROR_UNKNOWN));
138#endif
139 });
140}
141
142} // namespace esphome::zephyr_mcumgr
143#endif
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition component.h:501
void notify_state_(OTAState state, float progress, uint8_t error)
void update_chunk(const img_mgmt_upload_check &upload)
uint16_t type
@ OTA_RESPONSE_ERROR_UNKNOWN
Definition ota_backend.h:41
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
static void uint32_t