21static const char *
const TAG =
"esphome.ota";
22static constexpr u_int16_t OTA_BLOCK_SIZE = 8192;
25#ifdef USE_OTA_STATE_CALLBACK
31 ESP_LOGW(TAG,
"Could not create socket");
36 int err =
server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
38 ESP_LOGW(TAG,
"Socket unable to set reuseaddr: errno %d", err);
41 err =
server_->setblocking(
false);
43 ESP_LOGW(TAG,
"Socket unable to set nonblocking mode: errno %d", err);
52 ESP_LOGW(TAG,
"Socket unable to set sockaddr: errno %d", errno);
59 ESP_LOGW(TAG,
"Socket unable to bind: errno %d", errno);
66 ESP_LOGW(TAG,
"Socket unable to listen: errno %d", errno);
74 "Over-The-Air updates:\n"
78#ifdef USE_OTA_PASSWORD
80 ESP_LOGCONFIG(TAG,
" Password configured");
87static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
91 bool update_started =
false;
93 uint32_t last_progress = 0;
95 char *sbuf =
reinterpret_cast<char *
>(buf);
98 std::unique_ptr<ota::OTABackend> backend;
100#if USE_OTA_VERSION == 2
101 size_t size_acknowledged = 0;
108 socklen_t addr_len =
sizeof(source_addr);
116 int err =
client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable,
sizeof(
int));
118 ESP_LOGW(TAG,
"Socket could not enable TCP nodelay, errno %d", errno);
124 ESP_LOGD(TAG,
"Starting update from %s", this->
client_->getpeername().c_str());
126#ifdef USE_OTA_STATE_CALLBACK
131 ESP_LOGW(TAG,
"Reading magic bytes failed");
135 if (buf[0] != 0x6C || buf[1] != 0x26 || buf[2] != 0xF7 || buf[3] != 0x5C || buf[4] != 0x45) {
136 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],
144 buf[1] = USE_OTA_VERSION;
151 ESP_LOGW(TAG,
"Reading features failed");
154 ota_features = buf[0];
155 ESP_LOGV(TAG,
"Features: 0x%02X", ota_features);
159 if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
165#ifdef USE_OTA_PASSWORD
175 ESP_LOGV(TAG,
"Auth: Nonce is %s", sbuf);
178 if (!this->
writeall_(
reinterpret_cast<uint8_t *
>(sbuf), 32)) {
179 ESP_LOGW(TAG,
"Auth: Writing nonce failed");
185 md5.add(this->
password_.c_str(), this->password_.length());
191 ESP_LOGW(TAG,
"Auth: Reading cnonce failed");
195 ESP_LOGV(TAG,
"Auth: CNonce is %s", sbuf);
202 ESP_LOGV(TAG,
"Auth: Result is %s", sbuf);
205 if (!this->
readall_(buf + 64, 32)) {
206 ESP_LOGW(TAG,
"Auth: Reading response failed");
209 sbuf[64 + 32] =
'\0';
210 ESP_LOGV(TAG,
"Auth: Response is %s", sbuf + 64);
213 for (uint8_t i = 0; i < 32; i++)
214 matches = matches && buf[i] == buf[64 + i];
217 ESP_LOGW(TAG,
"Auth failed! Passwords do not match");
230 ESP_LOGW(TAG,
"Reading size failed");
234 for (uint8_t i = 0; i < 4; i++) {
238 ESP_LOGV(TAG,
"Size is %u bytes", ota_size);
240 error_code = backend->begin(ota_size);
243 update_started =
true;
251 ESP_LOGW(TAG,
"Reading binary MD5 checksum failed");
255 ESP_LOGV(TAG,
"Update: Binary MD5 is %s", sbuf);
256 backend->set_update_md5(sbuf);
262 while (total < ota_size) {
264 size_t requested = std::min(
sizeof(buf), ota_size - total);
267 if (errno == EAGAIN || errno == EWOULDBLOCK) {
272 ESP_LOGW(TAG,
"Error receiving data for update, errno %d", errno);
274 }
else if (read == 0) {
278 ESP_LOGW(TAG,
"Remote end closed connection");
282 error_code = backend->write(buf, read);
284 ESP_LOGW(TAG,
"Error writing binary data to flash!, error_code: %d", error_code);
288#if USE_OTA_VERSION == 2
289 while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
292 size_acknowledged += OTA_BLOCK_SIZE;
297 if (now - last_progress > 1000) {
299 float percentage = (total * 100.0f) / ota_size;
300 ESP_LOGD(TAG,
"Progress: %0.1f%%", percentage);
301#ifdef USE_OTA_STATE_CALLBACK
314 error_code = backend->end();
316 ESP_LOGW(TAG,
"Error ending update! error_code: %d", error_code);
326 ESP_LOGW(TAG,
"Reading back acknowledgement failed");
333 ESP_LOGI(TAG,
"Update complete");
335#ifdef USE_OTA_STATE_CALLBACK
342 buf[0] =
static_cast<uint8_t
>(error_code);
347 if (backend !=
nullptr && update_started) {
352#ifdef USE_OTA_STATE_CALLBACK
358 uint32_t start =
millis();
360 while (
len - at > 0) {
362 if (now - start > 1000) {
363 ESP_LOGW(TAG,
"Timed out reading %d bytes of data",
len);
369 if (errno == EAGAIN || errno == EWOULDBLOCK) {
374 ESP_LOGW(TAG,
"Failed to read %d bytes of data, errno %d",
len, errno);
376 }
else if (read == 0) {
377 ESP_LOGW(TAG,
"Remote closed connection");
389 uint32_t start =
millis();
391 while (
len - at > 0) {
393 if (now - start > 1000) {
394 ESP_LOGW(TAG,
"Timed out writing %d bytes of data",
len);
400 if (errno == EAGAIN || errno == EWOULDBLOCK) {
405 ESP_LOGW(TAG,
"Failed to write %d bytes of data, errno %d",
len, errno);
void feed_wdt(uint32_t time=0)
virtual void mark_failed()
Mark this component as failed.
void status_momentary_error(const std::string &name, uint32_t length=5000)
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
uint16_t get_port() const
bool writeall_(const uint8_t *buf, size_t len)
void dump_config() override
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_
std::unique_ptr< socket::Socket > client_
void init()
Initialize a new MD5 digest computation.
CallbackManager< void(ota::OTAState, float, uint8_t)> 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.
void IRAM_ATTR HOT yield()
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.