21static const char *
const TAG =
"esphome.ota";
22static constexpr uint16_t OTA_BLOCK_SIZE = 8192;
23static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 10000;
24static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000;
27#ifdef USE_OTA_STATE_CALLBACK
38 int err = this->
server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
43 err = this->
server_->setblocking(
false);
76 "Over-The-Air updates:\n"
80#ifdef USE_OTA_PASSWORD
82 ESP_LOGCONFIG(TAG,
" Password configured");
97static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
106 if (this->
client_ ==
nullptr) {
109 socklen_t addr_len =
sizeof(source_addr);
115 int err = this->
client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable,
sizeof(
int));
121 err = this->
client_->setblocking(
false);
134 ESP_LOGW(TAG,
"Handshake timeout");
143 if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
152 ESP_LOGW(TAG,
"Remote closed during handshake");
159 if (first_byte != 0x6C) {
160 ESP_LOGW(TAG,
"Invalid initial byte: 0x%02X", first_byte);
176 bool update_started =
false;
178 uint32_t last_progress = 0;
180 char *sbuf =
reinterpret_cast<char *
>(buf);
182 uint8_t ota_features;
183 std::unique_ptr<ota::OTABackend> backend;
185#if USE_OTA_VERSION == 2
186 size_t size_acknowledged = 0;
195 if (buf[0] != 0x26 || buf[1] != 0xF7 || buf[2] != 0x5C || buf[3] != 0x45) {
196 ESP_LOGW(TAG,
"Magic bytes mismatch! 0x6C-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3]);
203 buf[1] = USE_OTA_VERSION;
213 ota_features = buf[0];
214 ESP_LOGV(TAG,
"Features: 0x%02X", ota_features);
218 if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
224#ifdef USE_OTA_PASSWORD
234 ESP_LOGV(TAG,
"Auth: Nonce is %s", sbuf);
237 if (!this->
writeall_(
reinterpret_cast<uint8_t *
>(sbuf), 32)) {
238 ESP_LOGW(TAG,
"Auth: Writing nonce failed");
244 md5.add(this->
password_.c_str(), this->password_.length());
250 ESP_LOGW(TAG,
"Auth: Reading cnonce failed");
254 ESP_LOGV(TAG,
"Auth: CNonce is %s", sbuf);
261 ESP_LOGV(TAG,
"Auth: Result is %s", sbuf);
264 if (!this->
readall_(buf + 64, 32)) {
265 ESP_LOGW(TAG,
"Auth: Reading response failed");
268 sbuf[64 + 32] =
'\0';
269 ESP_LOGV(TAG,
"Auth: Response is %s", sbuf + 64);
272 for (uint8_t i = 0; i < 32; i++)
273 matches = matches && buf[i] == buf[64 + i];
276 ESP_LOGW(TAG,
"Auth failed! Passwords do not match");
293 for (uint8_t i = 0; i < 4; i++) {
297 ESP_LOGV(TAG,
"Size is %u bytes", ota_size);
305#ifdef USE_OTA_STATE_CALLBACK
310 error_code = backend->begin(ota_size);
313 update_started =
true;
325 ESP_LOGV(TAG,
"Update: Binary MD5 is %s", sbuf);
326 backend->set_update_md5(sbuf);
332 while (total < ota_size) {
334 size_t requested = std::min(
sizeof(buf), ota_size - total);
337 if (errno == EAGAIN || errno == EWOULDBLOCK) {
341 ESP_LOGW(TAG,
"Read error, errno %d", errno);
343 }
else if (read == 0) {
347 ESP_LOGW(TAG,
"Remote closed connection");
351 error_code = backend->write(buf, read);
353 ESP_LOGW(TAG,
"Flash write error, code: %d", error_code);
357#if USE_OTA_VERSION == 2
358 while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
361 size_acknowledged += OTA_BLOCK_SIZE;
366 if (now - last_progress > 1000) {
368 float percentage = (total * 100.0f) / ota_size;
369 ESP_LOGD(TAG,
"Progress: %0.1f%%", percentage);
370#ifdef USE_OTA_STATE_CALLBACK
382 error_code = backend->end();
384 ESP_LOGW(TAG,
"Error ending update! code: %d", error_code);
400 ESP_LOGI(TAG,
"Update complete");
402#ifdef USE_OTA_STATE_CALLBACK
409 buf[0] =
static_cast<uint8_t
>(error_code);
413 if (backend !=
nullptr && update_started) {
418#ifdef USE_OTA_STATE_CALLBACK
424 uint32_t start =
millis();
426 while (
len - at > 0) {
428 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
429 ESP_LOGW(TAG,
"Timeout reading %d bytes",
len);
435 if (errno != EAGAIN && errno != EWOULDBLOCK) {
436 ESP_LOGW(TAG,
"Error reading %d bytes, errno %d",
len, errno);
439 }
else if (read == 0) {
440 ESP_LOGW(TAG,
"Remote closed connection");
451 uint32_t start =
millis();
453 while (
len - at > 0) {
455 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
456 ESP_LOGW(TAG,
"Timeout writing %d bytes",
len);
462 if (errno != EAGAIN && errno != EWOULDBLOCK) {
463 ESP_LOGW(TAG,
"Error writing %d bytes, errno %d",
len, errno);
483 ESP_LOGD(TAG,
"Starting %s from %s", phase, this->
client_->getpeername().c_str());
void feed_wdt(uint32_t time=0)
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.
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message=nullptr)
void status_momentary_error(const std::string &name, uint32_t length=5000)
void status_clear_warning()
uint16_t get_port() const
void yield_and_feed_watchdog_()
bool writeall_(const uint8_t *buf, size_t len)
void log_start_(const char *phase)
void dump_config() override
void log_socket_error_(const char *msg)
uint32_t client_connect_time_
float get_setup_priority() const override
bool readall_(uint8_t *buf, size_t len)
void set_port(uint16_t port)
Manually set the port OTA should listen on.
std::unique_ptr< socket::Socket > server_
void cleanup_connection_()
void log_read_error_(const char *what)
std::unique_ptr< socket::Socket > client_
void init()
Initialize a new MD5 digest computation.
StateCallbackManager state_callback_
std::string get_use_address()
Get the active network hostname.
void register_ota_platform(OTAComponent *ota_caller)
std::unique_ptr< ota::OTABackend > make_ota_backend()
@ OTA_RESPONSE_UPDATE_PREPARE_OK
@ OTA_RESPONSE_SUPPORTS_COMPRESSION
@ OTA_RESPONSE_BIN_MD5_OK
@ OTA_RESPONSE_UPDATE_END_OK
@ OTA_RESPONSE_RECEIVE_OK
@ OTA_RESPONSE_ERROR_AUTH_INVALID
@ OTA_RESPONSE_ERROR_UNKNOWN
@ OTA_RESPONSE_ERROR_MAGIC
@ OTA_RESPONSE_REQUEST_AUTH
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
std::unique_ptr< Socket > socket_ip_loop_monitored(int type, int protocol)
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().
Providing packet encoding functions for exchanging data with a remote host.
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
void IRAM_ATTR HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.