22 static const char *
const TAG =
"ota";
24 static const uint8_t OTA_VERSION_1_0 = 1;
31 return make_unique<ArduinoESP8266OTABackend>();
34 return make_unique<ArduinoESP32OTABackend>();
38 return make_unique<IDFOTABackend>();
41 return make_unique<ArduinoRP2040OTABackend>();
44 return make_unique<ArduinoLibreTinyOTABackend>();
53 ESP_LOGW(TAG,
"Could not create socket.");
58 int err =
server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
60 ESP_LOGW(TAG,
"Socket unable to set reuseaddr: errno %d", err);
63 err =
server_->setblocking(
false);
65 ESP_LOGW(TAG,
"Socket unable to set nonblocking mode: errno %d", err);
74 ESP_LOGW(TAG,
"Socket unable to set sockaddr: errno %d", errno);
81 ESP_LOGW(TAG,
"Socket unable to bind: errno %d", errno);
88 ESP_LOGW(TAG,
"Socket unable to listen: errno %d", errno);
97 ESP_LOGCONFIG(TAG,
"Over-The-Air Updates:");
99 #ifdef USE_OTA_PASSWORD 101 ESP_LOGCONFIG(TAG,
" Using Password.");
106 ESP_LOGW(TAG,
"Last Boot was an unhandled reset, will proceed to safe mode in %" PRIu32
" restarts",
117 ESP_LOGI(TAG,
"Boot seems successful, resetting boot loop counter.");
122 static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
126 bool update_started =
false;
128 uint32_t last_progress = 0;
130 char *sbuf =
reinterpret_cast<char *
>(buf);
132 uint8_t ota_features;
133 std::unique_ptr<OTABackend> backend;
138 socklen_t addr_len =
sizeof(source_addr);
145 int err =
client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable,
sizeof(
int));
147 ESP_LOGW(TAG,
"Socket could not enable tcp nodelay, errno: %d", errno);
151 ESP_LOGD(TAG,
"Starting OTA Update from %s...", this->
client_->getpeername().c_str());
153 #ifdef USE_OTA_STATE_CALLBACK 158 ESP_LOGW(TAG,
"Reading magic bytes failed!");
162 if (buf[0] != 0x6C || buf[1] != 0x26 || buf[2] != 0xF7 || buf[3] != 0x5C || buf[4] != 0x45) {
163 ESP_LOGW(TAG,
"Magic bytes do not match! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3],
171 buf[1] = OTA_VERSION_1_0;
178 ESP_LOGW(TAG,
"Reading features failed!");
181 ota_features = buf[0];
182 ESP_LOGV(TAG,
"OTA features is 0x%02X", ota_features);
186 if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
192 #ifdef USE_OTA_PASSWORD 202 ESP_LOGV(TAG,
"Auth: Nonce is %s", sbuf);
205 if (!this->
writeall_(reinterpret_cast<uint8_t *>(sbuf), 32)) {
206 ESP_LOGW(TAG,
"Auth: Writing nonce failed!");
218 ESP_LOGW(TAG,
"Auth: Reading cnonce failed!");
222 ESP_LOGV(TAG,
"Auth: CNonce is %s", sbuf);
229 ESP_LOGV(TAG,
"Auth: Result is %s", sbuf);
232 if (!this->
readall_(buf + 64, 32)) {
233 ESP_LOGW(TAG,
"Auth: Reading response failed!");
236 sbuf[64 + 32] =
'\0';
237 ESP_LOGV(TAG,
"Auth: Response is %s", sbuf + 64);
240 for (uint8_t i = 0; i < 32; i++)
241 matches = matches && buf[i] == buf[64 + i];
244 ESP_LOGW(TAG,
"Auth failed! Passwords do not match!");
249 #endif // USE_OTA_PASSWORD 257 ESP_LOGW(TAG,
"Reading size failed!");
261 for (uint8_t i = 0; i < 4; i++) {
265 ESP_LOGV(TAG,
"OTA size is %u bytes", ota_size);
267 error_code = backend->begin(ota_size);
270 update_started =
true;
278 ESP_LOGW(TAG,
"Reading binary MD5 checksum failed!");
282 ESP_LOGV(TAG,
"Update: Binary MD5 is %s", sbuf);
283 backend->set_update_md5(sbuf);
289 while (total < ota_size) {
291 size_t requested = std::min(
sizeof(buf), ota_size - total);
292 ssize_t read = this->
client_->read(buf, requested);
294 if (errno == EAGAIN || errno == EWOULDBLOCK) {
299 ESP_LOGW(TAG,
"Error receiving data for update, errno: %d", errno);
301 }
else if (read == 0) {
305 ESP_LOGW(TAG,
"Remote end closed connection");
309 error_code = backend->write(buf, read);
311 ESP_LOGW(TAG,
"Error writing binary data to flash!, error_code: %d", error_code);
317 if (now - last_progress > 1000) {
319 float percentage = (total * 100.0f) / ota_size;
320 ESP_LOGD(TAG,
"OTA in progress: %0.1f%%", percentage);
321 #ifdef USE_OTA_STATE_CALLBACK 334 error_code = backend->end();
336 ESP_LOGW(TAG,
"Error ending OTA!, error_code: %d", error_code);
346 ESP_LOGW(TAG,
"Reading back acknowledgement failed!");
353 ESP_LOGI(TAG,
"OTA update finished!");
355 #ifdef USE_OTA_STATE_CALLBACK 362 buf[0] =
static_cast<uint8_t
>(error_code);
367 if (backend !=
nullptr && update_started) {
372 #ifdef USE_OTA_STATE_CALLBACK 378 uint32_t start =
millis();
380 while (len - at > 0) {
382 if (now - start > 1000) {
383 ESP_LOGW(TAG,
"Timed out reading %d bytes of data", len);
387 ssize_t read = this->
client_->read(buf + at, len - at);
389 if (errno == EAGAIN || errno == EWOULDBLOCK) {
394 ESP_LOGW(TAG,
"Failed to read %d bytes of data, errno: %d", len, errno);
396 }
else if (read == 0) {
397 ESP_LOGW(TAG,
"Remote closed connection");
409 uint32_t start =
millis();
411 while (len - at > 0) {
413 if (now - start > 1000) {
414 ESP_LOGW(TAG,
"Timed out writing %d bytes of data", len);
418 ssize_t written = this->
client_->write(buf + at, len - at);
420 if (errno == EAGAIN || errno == EWOULDBLOCK) {
425 ESP_LOGW(TAG,
"Failed to write %d bytes of data, errno: %d", len, errno);
444 uint32_t current_rtc = this->
read_rtc_();
447 ESP_LOGI(TAG,
"Device will enter safe mode on next boot.");
452 ESP_LOGI(TAG,
"Safe mode pending has been cleared");
470 if (is_manual_safe_mode) {
471 ESP_LOGI(TAG,
"Safe mode has been entered manually");
473 ESP_LOGCONFIG(TAG,
"There have been %" PRIu32
" suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_);
476 if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {
479 if (!is_manual_safe_mode) {
480 ESP_LOGE(TAG,
"Boot loop detected. Proceeding to safe mode.");
485 ESP_LOGE(TAG,
"No OTA attempt made, restarting.");
493 ESP_LOGI(TAG,
"Waiting for OTA attempt.");
498 this->
write_rtc_(this->safe_mode_rtc_value_ + 1);
518 #ifdef USE_OTA_STATE_CALLBACK void dump_config() override
void init()
Initialize a new MD5 digest computation.
std::unique_ptr< Socket > socket_ip(int type, int protocol)
Create a socket in the newest available IP domain (IPv6 or IPv4) of the given type and protocol...
uint16_t get_port() const
uint32_t safe_mode_enable_time_
The time safe mode should be on for.
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
std::string get_use_address()
Get the active network hostname.
bool get_safe_mode_pending()
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void add_on_state_callback(std::function< void(OTAState, float, uint8_t)> &&callback)
void setup()
Set up all the registered components. Call this at the end of your setup() function.
uint32_t safe_mode_start_time_
stores when safe mode was enabled.
uint32_t IRAM_ATTR HOT millis()
void status_momentary_error(const std::string &name, uint32_t length=5000)
bool readall_(uint8_t *buf, size_t len)
static const uint32_t ENTER_SAFE_MODE_MAGIC
a magic number to indicate that safe mode should be entered on next boot
void set_port(uint16_t port)
Manually set the port OTA should listen on.
ESPPreferences * global_preferences
void status_clear_warning()
std::unique_ptr< socket::Socket > client_
float get_setup_priority() const override
bool has_safe_mode_
stores whether safe mode can be enabled.
std::unique_ptr< OTABackend > make_ota_backend()
void write_rtc_(uint32_t val)
Application App
Global storage of Application pointer - only one Application can exist.
void on_safe_shutdown() override
void status_set_warning()
OTAComponent * global_ota_component
bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time)
uint8_t safe_mode_num_attempts_
void IRAM_ATTR HOT yield()
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
virtual void mark_failed()
Mark this component as failed.
uint32_t safe_mode_rtc_value_
Implementation of SPI Controller mode.
virtual bool sync()=0
Commit pending writes to flash.
void set_safe_mode_pending(const bool &pending)
Set to true if the next startup will enter safe mode.
bool writeall_(const uint8_t *buf, size_t len)
std::unique_ptr< socket::Socket > server_
CallbackManager< void(OTAState, float, uint8_t)> state_callback_
OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA...
void IRAM_ATTR HOT delay(uint32_t ms)