ESPHome  2024.9.2
safe_mode.cpp
Go to the documentation of this file.
1 #include "safe_mode.h"
2 
4 #include "esphome/core/hal.h"
5 #include "esphome/core/log.h"
6 #include "esphome/core/util.h"
7 
8 #include <cerrno>
9 #include <cinttypes>
10 #include <cstdio>
11 
12 namespace esphome {
13 namespace safe_mode {
14 
15 static const char *const TAG = "safe_mode";
16 
18  ESP_LOGCONFIG(TAG, "Safe Mode:");
19  ESP_LOGCONFIG(TAG, " Boot considered successful after %" PRIu32 " seconds",
20  this->safe_mode_boot_is_good_after_ / 1000); // because milliseconds
21  ESP_LOGCONFIG(TAG, " Invoke after %u boot attempts", this->safe_mode_num_attempts_);
22  ESP_LOGCONFIG(TAG, " Remain in safe mode for %" PRIu32 " seconds",
23  this->safe_mode_enable_time_ / 1000); // because milliseconds
24 
26  auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_;
27  if (remaining_restarts) {
28  ESP_LOGW(TAG, "Last reset occurred too quickly; safe mode will be invoked in %" PRIu32 " restarts",
29  remaining_restarts);
30  } else {
31  ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
32  }
33  }
34 }
35 
37 
40  // successful boot, reset counter
41  ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter");
42  this->clean_rtc();
43  this->boot_successful_ = true;
44  }
45 }
46 
47 void SafeModeComponent::set_safe_mode_pending(const bool &pending) {
48  uint32_t current_rtc = this->read_rtc_();
49 
50  if (pending && current_rtc != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) {
51  ESP_LOGI(TAG, "Device will enter safe mode on next boot");
53  }
54 
55  if (!pending && current_rtc == SafeModeComponent::ENTER_SAFE_MODE_MAGIC) {
56  ESP_LOGI(TAG, "Safe mode pending has been cleared");
57  this->clean_rtc();
58  }
59 }
60 
63 }
64 
65 bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time,
66  uint32_t boot_is_good_after) {
67  this->safe_mode_start_time_ = millis();
68  this->safe_mode_enable_time_ = enable_time;
69  this->safe_mode_boot_is_good_after_ = boot_is_good_after;
70  this->safe_mode_num_attempts_ = num_attempts;
71  this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
72  this->safe_mode_rtc_value_ = this->read_rtc_();
73 
74  bool is_manual_safe_mode = this->safe_mode_rtc_value_ == SafeModeComponent::ENTER_SAFE_MODE_MAGIC;
75 
76  if (is_manual_safe_mode) {
77  ESP_LOGI(TAG, "Safe mode invoked manually");
78  } else {
79  ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts", this->safe_mode_rtc_value_);
80  }
81 
82  if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {
83  this->clean_rtc();
84 
85  if (!is_manual_safe_mode) {
86  ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode");
87  }
88 
89  this->status_set_error();
90  this->set_timeout(enable_time, []() {
91  ESP_LOGW(TAG, "Safe mode enable time has elapsed -- restarting");
92  App.reboot();
93  });
94 
95  // Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised
96  delay(300); // NOLINT
97  App.setup();
98 
99  ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
100 
101  this->safe_mode_callback_.call();
102 
103  return true;
104  } else {
105  // increment counter
106  this->write_rtc_(this->safe_mode_rtc_value_ + 1);
107  return false;
108  }
109 }
110 
112  this->rtc_.save(&val);
114 }
115 
117  uint32_t val;
118  if (!this->rtc_.load(&val))
119  return 0;
120  return val;
121 }
122 
124 
127  this->clean_rtc();
128 }
129 
130 } // namespace safe_mode
131 } // namespace esphome
static const uint32_t ENTER_SAFE_MODE_MAGIC
a magic number to indicate that safe mode should be entered on next boot
Definition: safe_mode.h:45
void set_safe_mode_pending(const bool &pending)
Set to true if the next startup will enter safe mode.
Definition: safe_mode.cpp:47
uint32_t safe_mode_boot_is_good_after_
The amount of time after which the boot is considered successful.
Definition: safe_mode.h:37
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition: component.cpp:26
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
CallbackManager< void()> safe_mode_callback_
Definition: safe_mode.h:43
mopeka_std_values val[4]
uint32_t safe_mode_start_time_
stores when safe mode was enabled
Definition: safe_mode.h:40
void setup()
Set up all the registered components. Call this at the end of your setup() function.
Definition: application.cpp:28
bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time, uint32_t boot_is_good_after)
Definition: safe_mode.cpp:65
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
bool save(const T *src)
Definition: preferences.h:21
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
ESPPreferences * global_preferences
Application App
Global storage of Application pointer - only one Application can exist.
float get_setup_priority() const override
Definition: safe_mode.cpp:36
bool boot_successful_
set to true after boot is considered successful
Definition: safe_mode.h:36
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t safe_mode_enable_time_
The time safe mode should remain active for.
Definition: safe_mode.h:38
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
virtual bool sync()=0
Commit pending writes to flash.
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26