ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
esp32_hosted_update.cpp
Go to the documentation of this file.
1#if defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4)
6#include "esphome/core/log.h"
7#include <esp_image_format.h>
8#include <esp_app_desc.h>
9#include <esp_hosted.h>
10
11extern "C" {
12#include <esp_hosted_ota.h>
13}
14
16
17static const char *const TAG = "esp32_hosted.update";
18
19// older coprocessor firmware versions have a 1500-byte limit per RPC call
20constexpr size_t CHUNK_SIZE = 1500;
21
23 this->update_info_.title = "ESP32 Hosted Coprocessor";
24
25 // if wifi is not present, connect to the coprocessor
26#ifndef USE_WIFI
27 esp_hosted_connect_to_slave(); // NOLINT
28#endif
29
30 // get coprocessor version
31 esp_hosted_coprocessor_fwver_t ver_info;
32 if (esp_hosted_get_coprocessor_fwversion(&ver_info) == ESP_OK) {
33 this->update_info_.current_version = str_sprintf("%d.%d.%d", ver_info.major1, ver_info.minor1, ver_info.patch1);
34 } else {
35 this->update_info_.current_version = "unknown";
36 }
37 ESP_LOGD(TAG, "Coprocessor version: %s", this->update_info_.current_version.c_str());
38
39 // get image version
40 const int app_desc_offset = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t);
41 if (this->firmware_size_ >= app_desc_offset + sizeof(esp_app_desc_t)) {
42 esp_app_desc_t *app_desc = (esp_app_desc_t *) (this->firmware_data_ + app_desc_offset);
43 if (app_desc->magic_word == ESP_APP_DESC_MAGIC_WORD) {
44 ESP_LOGD(TAG, "Firmware version: %s", app_desc->version);
45 ESP_LOGD(TAG, "Project name: %s", app_desc->project_name);
46 ESP_LOGD(TAG, "Build date: %s", app_desc->date);
47 ESP_LOGD(TAG, "Build time: %s", app_desc->time);
48 ESP_LOGD(TAG, "IDF version: %s", app_desc->idf_ver);
49 this->update_info_.latest_version = app_desc->version;
50 if (this->update_info_.latest_version != this->update_info_.current_version) {
52 } else {
54 }
55 } else {
56 ESP_LOGW(TAG, "Invalid app description magic word: 0x%08x (expected 0x%08x)", app_desc->magic_word,
57 ESP_APP_DESC_MAGIC_WORD);
59 }
60 } else {
61 ESP_LOGW(TAG, "Firmware too small to contain app description");
63 }
64
65 // publish state
66 this->status_clear_error();
67 this->publish_state();
68}
69
71 ESP_LOGCONFIG(TAG,
72 "ESP32 Hosted Update:\n"
73 " Current Version: %s\n"
74 " Latest Version: %s\n"
75 " Latest Size: %zu bytes",
76 this->update_info_.current_version.c_str(), this->update_info_.latest_version.c_str(),
77 this->firmware_size_);
78}
79
81 if (this->state_ != update::UPDATE_STATE_AVAILABLE && !force) {
82 ESP_LOGW(TAG, "Update not available");
83 return;
84 }
85
86 if (this->firmware_data_ == nullptr || this->firmware_size_ == 0) {
87 ESP_LOGE(TAG, "No firmware data available");
88 return;
89 }
90
91 sha256::SHA256 hasher;
92 hasher.init();
93 hasher.add(this->firmware_data_, this->firmware_size_);
94 hasher.calculate();
95 if (!hasher.equals_bytes(this->firmware_sha256_.data())) {
96 this->status_set_error(LOG_STR("SHA256 verification failed"));
97 this->publish_state();
98 return;
99 }
100
101 ESP_LOGI(TAG, "Starting OTA update (%zu bytes)", this->firmware_size_);
102
103 watchdog::WatchdogManager watchdog(20000);
104 update::UpdateState prev_state = this->state_;
106 this->update_info_.has_progress = false;
107 this->publish_state();
108
109 esp_err_t err = esp_hosted_slave_ota_begin(); // NOLINT
110 if (err != ESP_OK) {
111 ESP_LOGE(TAG, "Failed to begin OTA: %s", esp_err_to_name(err));
112 this->state_ = prev_state;
113 this->status_set_error(LOG_STR("Failed to begin OTA"));
114 this->publish_state();
115 return;
116 }
117
118 uint8_t chunk[CHUNK_SIZE];
119 const uint8_t *data_ptr = this->firmware_data_;
120 size_t remaining = this->firmware_size_;
121 while (remaining > 0) {
122 size_t chunk_size = std::min(remaining, static_cast<size_t>(CHUNK_SIZE));
123 memcpy(chunk, data_ptr, chunk_size);
124 err = esp_hosted_slave_ota_write(chunk, chunk_size); // NOLINT
125 if (err != ESP_OK) {
126 ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err));
127 esp_hosted_slave_ota_end(); // NOLINT
128 this->state_ = prev_state;
129 this->status_set_error(LOG_STR("Failed to write OTA data"));
130 this->publish_state();
131 return;
132 }
133 data_ptr += chunk_size;
134 remaining -= chunk_size;
135 App.feed_wdt();
136 }
137
138 err = esp_hosted_slave_ota_end(); // NOLINT
139 if (err != ESP_OK) {
140 ESP_LOGE(TAG, "Failed to end OTA: %s", esp_err_to_name(err));
141 this->state_ = prev_state;
142 this->status_set_error(LOG_STR("Failed to end OTA"));
143 this->publish_state();
144 return;
145 }
146
147 // activate new firmware
148 err = esp_hosted_slave_ota_activate(); // NOLINT
149 if (err != ESP_OK) {
150 ESP_LOGE(TAG, "Failed to activate OTA: %s", esp_err_to_name(err));
151 this->state_ = prev_state;
152 this->status_set_error(LOG_STR("Failed to activate OTA"));
153 this->publish_state();
154 return;
155 }
156
157 // update state
158 ESP_LOGI(TAG, "OTA update successful");
160 this->status_clear_error();
161 this->publish_state();
162
163 // schedule a restart to ensure everything is in sync
164 ESP_LOGI(TAG, "Restarting in 1 second");
165 this->set_timeout(1000, []() { App.safe_reboot(); });
166}
167
168} // namespace esphome::esp32_hosted
169#endif
void feed_wdt(uint32_t time=0)
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
bool equals_bytes(const uint8_t *expected)
Compare the hash against a provided byte-encoded hash.
Definition hash_base.h:32
void calculate() override
Definition sha256.cpp:55
void add(const uint8_t *data, size_t len) override
Definition sha256.cpp:53
void init() override
Definition sha256.cpp:48
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:220
Application App
Global storage of Application pointer - only one Application can exist.