12#ifdef USE_OTA_ROLLBACK
14#include <zephyr/dfu/mcuboot.h>
15#elif defined(USE_ESP32)
16#include <esp_ota_ops.h>
17#include <esp_system.h>
18#include <esp_image_format.h>
26#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK) && !defined(USE_OTA_PARTITIONS)
29static const esp_partition_t *find_alternate_app_partition(
bool verify) {
30 const esp_partition_t *running = esp_ota_get_running_partition();
31 const esp_partition_t *result =
nullptr;
32 esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY,
nullptr);
33 while (it !=
nullptr) {
34 const esp_partition_t *p = esp_partition_get(it);
35 if (p->address != running->address) {
40 esp_image_metadata_t data = {};
41 const esp_partition_pos_t part_pos = {
45 if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &part_pos, &data) == ESP_OK) {
50 it = esp_partition_next(it);
52 esp_partition_iterator_release(it);
60 " Successful after: %" PRIu32
"s\n"
61 " Invoke after: %u attempts\n"
62 " Duration: %" PRIu32
"s",
66#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
67 const char *state_str =
"support unknown";
68 if (this->
ota_state_ == ESP_OTA_IMG_PENDING_VERIFY) {
69 state_str =
"supported";
70#ifndef USE_OTA_PARTITIONS
71 }
else if (this->
ota_state_ == ESP_OTA_IMG_NEW) {
72 state_str =
"not supported";
75 ESP_LOGCONFIG(TAG,
" Bootloader rollback: %s", state_str);
80 if (remaining_restarts) {
81 ESP_LOGW(TAG,
"Last reset too quick; invoke in %" PRIu32
" restarts", remaining_restarts);
83 ESP_LOGW(TAG,
"SAFE MODE IS ACTIVE");
87#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
88 const esp_partition_t *last_invalid = esp_ota_get_last_invalid_partition();
89 if (last_invalid !=
nullptr) {
91 "OTA rollback detected! Rolled back from partition '%s'\n"
92 " The device reset before the boot was marked successful",
94 if (esp_reset_reason() == ESP_RST_BROWNOUT) {
95 ESP_LOGW(TAG,
"Last reset was due to brownout - check your power supply!\n"
96 " See https://esphome.io/guides/faq.html#brownout-detector-was-triggered");
100 ESP_LOGW(TAG,
"OTA updates are impossible.");
101#ifdef USE_OTA_PARTITIONS
102 ESP_LOGW(TAG,
" OTA partition table update or serial flashing is required.");
104 if (find_alternate_app_partition(
false) !=
nullptr) {
105 ESP_LOGW(TAG,
" Activate safe mode to reboot to the recovery partition.");
107 ESP_LOGE(TAG,
" No recovery partition available; serial flashing is required.");
116#ifdef USE_OTA_ROLLBACK
120#if defined(USE_ZEPHYR)
121 if (!boot_is_img_confirmed()) {
122 boot_write_img_confirmed();
124#elif defined(USE_ESP32)
129 if (esp_ota_get_running_partition() == esp_ota_get_boot_partition()) {
130 esp_ota_mark_app_valid_cancel_rollback();
139#ifdef USE_OTA_ROLLBACK
150 ESP_LOGI(TAG,
"Boot seems successful; resetting boot loop counter");
159 ESP_LOGI(TAG,
"Device will enter on next boot");
164 ESP_LOGI(TAG,
"Safe mode pending has been cleared");
174 ESP_LOGI(TAG,
"Restarting in safe mode");
189#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
191 const esp_partition_t *running_part = esp_ota_get_running_partition();
192 esp_ota_get_state_partition(running_part, &this->
ota_state_);
193 const esp_partition_t *next_part = esp_ota_get_next_update_partition(
nullptr);
203 ESP_LOGI(TAG,
"Manual mode");
205 ESP_LOGCONFIG(TAG,
"Unsuccessful boot attempts: %" PRIu32, rtc_val);
208 if (rtc_val < num_attempts && !is_manual) {
217 ESP_LOGE(TAG,
"Boot loop detected");
220#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK) && !defined(USE_OTA_PARTITIONS)
229 const esp_partition_t *rollback_part = find_alternate_app_partition(
true);
230 if (rollback_part !=
nullptr) {
231 esp_err_t err = esp_ota_set_boot_partition(rollback_part);
233 ESP_LOGW(TAG,
"OTA updates are impossible. Rebooting to recovery app.");
236 ESP_LOGE(TAG,
"Failed to set recovery boot partition: %s", esp_err_to_name(err));
244 ESP_LOGW(TAG,
"Timeout, restarting");
252 ESP_LOGW(TAG,
"SAFE MODE IS ACTIVE");
254#ifdef USE_SAFE_MODE_CALLBACK
263 ESP_LOGE(TAG,
"Failed to set rtc value (%" PRIu32
")",
val);
267 ESP_LOGE(TAG,
"Failed to persist rtc value (%" PRIu32
")",
val);
285 ESP_LOGE(TAG,
"Failed to clear boot loop counter");
292#if defined(USE_OTA_ROLLBACK) && defined(USE_SAFE_MODE_BOOT_IS_GOOD_ON_SHUTDOWN)
void setup()
Reserve space for components to avoid memory fragmentation.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
void disable_loop()
Disable this component's loop.
uint32_t safe_mode_enable_time_
The time safe mode should remain active for.
void on_safe_shutdown() override
bool boot_successful_
set to true after boot is considered successful
uint32_t safe_mode_start_time_
stores when safe mode was enabled
uint32_t safe_mode_boot_is_good_after_
The amount of time after which the boot is considered successful.
float get_setup_priority() const override
StaticCallbackManager< ESPHOME_SAFE_MODE_CALLBACK_COUNT, void()> safe_mode_callback_
esp_ota_img_states_t ota_state_
void restart_in_safe_mode()
Set safe mode pending and reboot; run by the safe mode button.
uint32_t safe_mode_rtc_value_
bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time, uint32_t boot_is_good_after, bool in_flash)
void dump_config() override
void set_safe_mode_pending(const bool &pending)
Set to true if the next startup will enter safe mode.
void write_rtc_(uint32_t val)
bool get_safe_mode_pending()
static const uint32_t ENTER_SAFE_MODE_MAGIC
a magic number to indicate that safe mode should be entered on next boot
void confirm_app_image_()
uint8_t safe_mode_num_attempts_
constexpr uint32_t RTC_KEY
RTC key for storing boot loop counter - used by safe_mode and preferences backends.
ESPHOME_LOG_TAG(TAG, "safe_mode")
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
ESPPreferences * global_preferences
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
ESPPreferenceObject make_preference(size_t, uint32_t, bool)
bool sync()
Commit pending writes to flash.