mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
[nextion] Use persistent http connection for TFT upload (Arduino) (#6582)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
6fe328ef2b
commit
c299dff124
2 changed files with 213 additions and 224 deletions
|
@ -976,6 +976,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* @return Whether the send was successful.
|
* @return Whether the send was successful.
|
||||||
*/
|
*/
|
||||||
bool send_command(const char *command);
|
bool send_command(const char *command);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually send a raw formatted command to the display.
|
* Manually send a raw formatted command to the display.
|
||||||
* @param format The printf-style command format, like "vis %s,0"
|
* @param format The printf-style command format, like "vis %s,0"
|
||||||
|
@ -989,7 +990,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* Set the tft file URL. https seems problematic with arduino..
|
* Set the tft file URL. https seems problematic with arduino..
|
||||||
*/
|
*/
|
||||||
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
|
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Uploads the TFT file to the Nextion display.
|
* @brief Uploads the TFT file to the Nextion display.
|
||||||
|
@ -1013,6 +1013,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
*/
|
*/
|
||||||
bool upload_tft(uint32_t baud_rate = 0, bool exit_reparse = true);
|
bool upload_tft(uint32_t baud_rate = 0, bool exit_reparse = true);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1134,6 +1136,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
void all_components_send_state_(bool force_update = false);
|
void all_components_send_state_(bool force_update = false);
|
||||||
uint64_t comok_sent_ = 0;
|
uint64_t comok_sent_ = 0;
|
||||||
bool remove_from_q_(bool report_empty = true);
|
bool remove_from_q_(bool report_empty = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
* Sends commands ignoring of the Nextion has been setup.
|
* Sends commands ignoring of the Nextion has been setup.
|
||||||
|
@ -1175,20 +1178,16 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
void check_pending_waveform_();
|
void check_pending_waveform_();
|
||||||
|
|
||||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||||
uint32_t content_length_ = 0;
|
|
||||||
int tft_size_ = 0;
|
|
||||||
uint32_t original_baud_rate_ = 0;
|
|
||||||
bool upload_first_chunk_sent_ = false;
|
|
||||||
|
|
||||||
std::string tft_url_;
|
|
||||||
uint8_t *transfer_buffer_{nullptr};
|
|
||||||
size_t transfer_buffer_size_;
|
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
WiFiClient *wifi_client_{nullptr};
|
WiFiClient *wifi_client_{nullptr};
|
||||||
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
|
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
|
||||||
WiFiClient *get_wifi_client_();
|
WiFiClient *get_wifi_client_();
|
||||||
#endif
|
#endif
|
||||||
|
std::string tft_url_;
|
||||||
|
uint32_t content_length_ = 0;
|
||||||
|
int tft_size_ = 0;
|
||||||
|
uint32_t original_baud_rate_ = 0;
|
||||||
|
bool upload_first_chunk_sent_ = false;
|
||||||
|
|
||||||
#ifdef ARDUINO
|
#ifdef ARDUINO
|
||||||
/**
|
/**
|
||||||
|
@ -1198,18 +1197,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* @param int range_start Position of next byte to transfer.
|
* @param int range_start Position of next byte to transfer.
|
||||||
* @return position of last byte transferred, -1 for failure.
|
* @return position of last byte transferred, -1 for failure.
|
||||||
*/
|
*/
|
||||||
int upload_by_chunks_(HTTPClient *http, int range_start);
|
int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start);
|
||||||
|
|
||||||
bool upload_with_range_(uint32_t range_start, uint32_t range_end);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* start update tft file to nextion.
|
|
||||||
*
|
|
||||||
* @param const uint8_t *file_buf
|
|
||||||
* @param size_t buf_size
|
|
||||||
* @return true if success, false for failure.
|
|
||||||
*/
|
|
||||||
bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size);
|
|
||||||
/**
|
/**
|
||||||
* Ends the upload process, restart Nextion and, if successful,
|
* Ends the upload process, restart Nextion and, if successful,
|
||||||
* restarts ESP
|
* restarts ESP
|
||||||
|
@ -1217,6 +1206,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* @return bool True: Transfer completed successfuly, False: Transfer failed.
|
* @return bool True: Transfer completed successfuly, False: Transfer failed.
|
||||||
*/
|
*/
|
||||||
bool upload_end_(bool successful);
|
bool upload_end_(bool successful);
|
||||||
|
|
||||||
#elif defined(USE_ESP_IDF)
|
#elif defined(USE_ESP_IDF)
|
||||||
/**
|
/**
|
||||||
* will request 4096 bytes chunks from the web server
|
* will request 4096 bytes chunks from the web server
|
||||||
|
@ -1226,6 +1216,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* @return position of last byte transferred, -1 for failure.
|
* @return position of last byte transferred, -1 for failure.
|
||||||
*/
|
*/
|
||||||
int upload_range(const std::string &url, int range_start);
|
int upload_range(const std::string &url, int range_start);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends the upload process, restart Nextion and, if successful,
|
* Ends the upload process, restart Nextion and, if successful,
|
||||||
* restarts ESP
|
* restarts ESP
|
||||||
|
@ -1233,7 +1224,12 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
* @return bool True: Transfer completed successfuly, False: Transfer failed.
|
* @return bool True: Transfer completed successfuly, False: Transfer failed.
|
||||||
*/
|
*/
|
||||||
bool upload_end(bool successful);
|
bool upload_end(bool successful);
|
||||||
#endif // ARDUINO vs ESP-IDF
|
#endif // ARDUINO vs USE_ESP_IDF
|
||||||
|
/**
|
||||||
|
* Returns the ESP Free Heap memory. This is framework independent.
|
||||||
|
* @return Free Heap in bytes.
|
||||||
|
*/
|
||||||
|
uint32_t get_free_heap_();
|
||||||
|
|
||||||
#endif // USE_NEXTION_TFT_UPLOAD
|
#endif // USE_NEXTION_TFT_UPLOAD
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
#include "nextion.h"
|
#include "nextion.h"
|
||||||
|
|
||||||
#ifdef ARDUINO
|
|
||||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||||
|
#ifdef ARDUINO
|
||||||
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/components/network/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <esp_heap_caps.h>
|
#include <esp_heap_caps.h>
|
||||||
|
@ -20,124 +21,132 @@ static const char *const TAG = "nextion.upload.arduino";
|
||||||
// Followed guide
|
// Followed guide
|
||||||
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
|
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
|
||||||
|
|
||||||
int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) {
|
inline uint32_t Nextion::get_free_heap_() {
|
||||||
int range_end = 0;
|
#if defined(USE_ESP32)
|
||||||
|
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
return EspClass::getFreeHeap();
|
||||||
|
#endif // USE_ESP32 vs USE_ESP8266
|
||||||
|
}
|
||||||
|
|
||||||
if (range_start == 0 && this->transfer_buffer_size_ > 16384) { // Start small at the first run in case of a big skip
|
int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||||
range_end = 16384 - 1;
|
uint32_t range_size = this->tft_size_ - range_start;
|
||||||
} else {
|
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
range_end = range_start + this->transfer_buffer_size_ - 1;
|
uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
|
||||||
|
ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
|
||||||
|
if (range_size <= 0 or range_end <= range_start) {
|
||||||
|
ESP_LOGD(TAG, "Range end: %" PRIu32, range_end);
|
||||||
|
ESP_LOGD(TAG, "Range size: %" PRIu32, range_size);
|
||||||
|
ESP_LOGE(TAG, "Invalid range");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (range_end > this->tft_size_)
|
char range_header[32];
|
||||||
range_end = this->tft_size_;
|
sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
|
||||||
|
ESP_LOGV(TAG, "Requesting range: %s", range_header);
|
||||||
#ifdef USE_ESP8266
|
http_client.addHeader("Range", range_header);
|
||||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
|
int code = http_client.GET();
|
||||||
http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) {
|
||||||
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
ESP_LOGW(TAG, "HTTP Request failed; Error: %s", HTTPClient::errorToString(code).c_str());
|
||||||
http->setFollowRedirects(true);
|
return -1;
|
||||||
#endif
|
|
||||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
|
||||||
http->setRedirectLimit(3);
|
|
||||||
#endif
|
|
||||||
#endif // USE_ESP8266
|
|
||||||
|
|
||||||
char range_header[64];
|
|
||||||
sprintf(range_header, "bytes=%d-%d", range_start, range_end);
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Requesting range: %s", range_header);
|
|
||||||
|
|
||||||
int tries = 1;
|
|
||||||
int code = 0;
|
|
||||||
bool begin_status = false;
|
|
||||||
while (tries <= 5) {
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
begin_status = http->begin(this->tft_url_.c_str());
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
++tries;
|
|
||||||
if (!begin_status) {
|
|
||||||
ESP_LOGD(TAG, "upload_by_chunks_: connection failed");
|
|
||||||
delay(500); // NOLINT
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
http->addHeader("Range", range_header);
|
|
||||||
|
|
||||||
code = http->GET();
|
|
||||||
if (code == 200 || code == 206) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retries(%d/5)", this->tft_url_.c_str(),
|
|
||||||
HTTPClient::errorToString(code).c_str(), tries);
|
|
||||||
http->end();
|
|
||||||
App.feed_wdt();
|
|
||||||
delay(500); // NOLINT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tries > 5) {
|
// Allocate the buffer dynamically
|
||||||
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
|
uint8_t *buffer = allocator.allocate(4096);
|
||||||
|
if (!buffer) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate upload buffer");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string recv_string;
|
std::string recv_string;
|
||||||
size_t size = 0;
|
while (true) {
|
||||||
int fetched = 0;
|
|
||||||
int range = range_end - range_start;
|
|
||||||
|
|
||||||
while (fetched < range) {
|
|
||||||
size = http->getStreamPtr()->available();
|
|
||||||
if (!size) {
|
|
||||||
App.feed_wdt();
|
|
||||||
delay(0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int c = http->getStreamPtr()->readBytes(
|
|
||||||
&this->transfer_buffer_[fetched], ((size > this->transfer_buffer_size_) ? this->transfer_buffer_size_ : size));
|
|
||||||
fetched += c;
|
|
||||||
}
|
|
||||||
http->end();
|
|
||||||
ESP_LOGN(TAG, "Fetched %d of %d bytes", fetched, this->content_length_);
|
|
||||||
|
|
||||||
// upload fetched segments to the display in 4KB chunks
|
|
||||||
int write_len;
|
|
||||||
for (int i = 0; i < range; i += 4096) {
|
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
write_len = this->content_length_ < 4096 ? this->content_length_ : 4096;
|
const uint16_t buffer_size =
|
||||||
this->write_array(&this->transfer_buffer_[i], write_len);
|
this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data
|
||||||
this->content_length_ -= write_len;
|
ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size);
|
||||||
ESP_LOGD(TAG, "Uploaded %0.2f %%; %d bytes remaining",
|
uint16_t read_len = 0;
|
||||||
100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_);
|
int partial_read_len = 0;
|
||||||
|
const uint32_t start_time = millis();
|
||||||
if (!this->upload_first_chunk_sent_) {
|
while (read_len < buffer_size && millis() - start_time < 5000) {
|
||||||
this->upload_first_chunk_sent_ = true;
|
if (http_client.getStreamPtr()->available() > 0) {
|
||||||
delay(500); // NOLINT
|
partial_read_len =
|
||||||
}
|
http_client.getStreamPtr()->readBytes(reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len);
|
||||||
|
read_len += partial_read_len;
|
||||||
this->recv_ret_string_(recv_string, 4096, true);
|
if (partial_read_len > 0) {
|
||||||
if (recv_string[0] != 0x05) { // 0x05 == "ok"
|
App.feed_wdt();
|
||||||
ESP_LOGD(TAG, "recv_string [%s]",
|
delay(2);
|
||||||
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// handle partial upload request
|
|
||||||
if (recv_string[0] == 0x08 && recv_string.size() == 5) {
|
|
||||||
uint32_t result = 0;
|
|
||||||
for (int j = 0; j < 4; ++j) {
|
|
||||||
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
|
|
||||||
}
|
|
||||||
if (result > 0) {
|
|
||||||
ESP_LOGD(TAG, "Nextion reported new range %d", result);
|
|
||||||
this->content_length_ = this->tft_size_ - result;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recv_string.clear();
|
if (read_len != buffer_size) {
|
||||||
|
// Did not receive the full package within the timeout period
|
||||||
|
ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len,
|
||||||
|
buffer_size);
|
||||||
|
// Deallocate buffer
|
||||||
|
allocator.deallocate(buffer, 4096);
|
||||||
|
buffer = nullptr;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len);
|
||||||
|
if (read_len > 0) {
|
||||||
|
recv_string.clear();
|
||||||
|
this->write_array(buffer, buffer_size);
|
||||||
|
App.feed_wdt();
|
||||||
|
this->recv_ret_string_(recv_string, upload_first_chunk_sent_ ? 500 : 5000, true);
|
||||||
|
this->content_length_ -= read_len;
|
||||||
|
const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_;
|
||||||
|
#if defined(USE_ESP32) && defined(USE_PSRAM)
|
||||||
|
ESP_LOGD(
|
||||||
|
TAG,
|
||||||
|
"Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes",
|
||||||
|
upload_percentage, this->content_length_, static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
|
||||||
|
static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)));
|
||||||
|
#else
|
||||||
|
ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage,
|
||||||
|
this->content_length_, this->get_free_heap_());
|
||||||
|
#endif
|
||||||
|
upload_first_chunk_sent_ = true;
|
||||||
|
if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request
|
||||||
|
ESP_LOGD(TAG, "recv_string [%s]",
|
||||||
|
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
|
||||||
|
uint32_t result = 0;
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
|
||||||
|
}
|
||||||
|
if (result > 0) {
|
||||||
|
ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
|
||||||
|
this->content_length_ = this->tft_size_ - result;
|
||||||
|
range_start = result;
|
||||||
|
} else {
|
||||||
|
range_start = range_end + 1;
|
||||||
|
}
|
||||||
|
// Deallocate buffer
|
||||||
|
allocator.deallocate(buffer, 4096);
|
||||||
|
buffer = nullptr;
|
||||||
|
return range_end + 1;
|
||||||
|
} else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok"
|
||||||
|
ESP_LOGE(TAG, "Invalid response from Nextion: [%s]",
|
||||||
|
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
|
||||||
|
// Deallocate buffer
|
||||||
|
allocator.deallocate(buffer, 4096);
|
||||||
|
buffer = nullptr;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
recv_string.clear();
|
||||||
|
} else if (read_len == 0) {
|
||||||
|
ESP_LOGV(TAG, "End of HTTP response reached");
|
||||||
|
break; // Exit the loop if there is no more data to read
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len);
|
||||||
|
break; // Exit the loop on error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
range_start = range_end + 1;
|
||||||
|
// Deallocate buffer
|
||||||
|
allocator.deallocate(buffer, 4096);
|
||||||
|
buffer = nullptr;
|
||||||
return range_end + 1;
|
return range_end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,12 +156,12 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||||
ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
|
ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
|
||||||
|
|
||||||
if (this->is_updating_) {
|
if (this->is_updating_) {
|
||||||
ESP_LOGD(TAG, "Currently updating");
|
ESP_LOGW(TAG, "Currently uploading");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!network::is_connected()) {
|
if (!network::is_connected()) {
|
||||||
ESP_LOGD(TAG, "network is not connected");
|
ESP_LOGE(TAG, "Network is not connected");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,43 +184,42 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
|
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
|
||||||
|
|
||||||
HTTPClient http;
|
// Define the configuration for the HTTP client
|
||||||
http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along
|
ESP_LOGV(TAG, "Initializing HTTP client");
|
||||||
|
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
|
HTTPClient http_client;
|
||||||
|
http_client.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along
|
||||||
|
|
||||||
bool begin_status = false;
|
bool begin_status = false;
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
begin_status = http.begin(this->tft_url_.c_str());
|
begin_status = http_client.begin(this->tft_url_.c_str());
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
|
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
|
||||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
||||||
http.setFollowRedirects(true);
|
http_client.setFollowRedirects(true);
|
||||||
#endif
|
#endif
|
||||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
||||||
http.setRedirectLimit(3);
|
http_client.setRedirectLimit(3);
|
||||||
#endif
|
#endif
|
||||||
begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
begin_status = http_client.begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
||||||
#endif
|
#endif // USE_ESP8266
|
||||||
|
|
||||||
if (!begin_status) {
|
if (!begin_status) {
|
||||||
this->is_updating_ = false;
|
this->is_updating_ = false;
|
||||||
ESP_LOGD(TAG, "Connection failed");
|
ESP_LOGD(TAG, "Connection failed");
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
|
||||||
allocator.deallocate(this->transfer_buffer_, this->transfer_buffer_size_);
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Connected");
|
ESP_LOGD(TAG, "Connected");
|
||||||
}
|
}
|
||||||
|
http_client.addHeader("Range", "bytes=0-255");
|
||||||
http.addHeader("Range", "bytes=0-255");
|
|
||||||
const char *header_names[] = {"Content-Range"};
|
const char *header_names[] = {"Content-Range"};
|
||||||
http.collectHeaders(header_names, 1);
|
http_client.collectHeaders(header_names, 1);
|
||||||
ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str());
|
ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str());
|
||||||
|
http_client.setReuse(true);
|
||||||
http.setReuse(true);
|
|
||||||
// try up to 5 times. DNS sometimes needs a second try or so
|
// try up to 5 times. DNS sometimes needs a second try or so
|
||||||
int tries = 1;
|
int tries = 1;
|
||||||
int code = http.GET();
|
int code = http_client.GET();
|
||||||
delay(100); // NOLINT
|
delay(100); // NOLINT
|
||||||
|
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
|
@ -221,34 +229,41 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||||
|
|
||||||
delay(250); // NOLINT
|
delay(250); // NOLINT
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
code = http.GET();
|
code = http_client.GET();
|
||||||
++tries;
|
++tries;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((code != 200 && code != 206) || tries > 5) {
|
if (code != 200 and code != 206) {
|
||||||
return this->upload_end_(false);
|
return this->upload_end_(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
String content_range_string = http.header("Content-Range");
|
String content_range_string = http_client.header("Content-Range");
|
||||||
content_range_string.remove(0, 12);
|
content_range_string.remove(0, 12);
|
||||||
this->content_length_ = content_range_string.toInt();
|
this->tft_size_ = content_range_string.toInt();
|
||||||
this->tft_size_ = content_length_;
|
|
||||||
http.end();
|
|
||||||
|
|
||||||
if (this->content_length_ < 4096) {
|
ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_);
|
||||||
ESP_LOGE(TAG, "Failed to get file size");
|
if (this->tft_size_ < 4096) {
|
||||||
|
ESP_LOGE(TAG, "File size check failed.");
|
||||||
|
ESP_LOGD(TAG, "Close HTTP connection");
|
||||||
|
http_client.end();
|
||||||
|
ESP_LOGV(TAG, "Connection closed");
|
||||||
return this->upload_end_(false);
|
return this->upload_end_(false);
|
||||||
|
} else {
|
||||||
|
ESP_LOGV(TAG, "File size check passed. Proceeding...");
|
||||||
}
|
}
|
||||||
|
this->content_length_ = this->tft_size_;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str());
|
ESP_LOGD(TAG, "Uploading Nextion");
|
||||||
// The Nextion will ignore the update command if it is sleeping
|
|
||||||
|
|
||||||
|
// The Nextion will ignore the upload command if it is sleeping
|
||||||
|
ESP_LOGV(TAG, "Wake-up Nextion");
|
||||||
|
this->ignore_is_setup_ = true;
|
||||||
this->send_command_("sleep=0");
|
this->send_command_("sleep=0");
|
||||||
this->set_backlight_brightness(1.0);
|
this->send_command_("dim=100");
|
||||||
delay(250); // NOLINT
|
delay(250); // NOLINT
|
||||||
|
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
|
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
|
|
||||||
char command[128];
|
char command[128];
|
||||||
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
|
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
|
||||||
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
|
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
|
||||||
|
@ -256,11 +271,12 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||||
sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate);
|
sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate);
|
||||||
|
|
||||||
// Clear serial receive buffer
|
// Clear serial receive buffer
|
||||||
uint8_t d;
|
ESP_LOGV(TAG, "Clear serial receive buffer");
|
||||||
while (this->available()) {
|
this->reset_(false);
|
||||||
this->read_byte(&d);
|
delay(250); // NOLINT
|
||||||
};
|
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Send upload instruction: %s", command);
|
||||||
this->send_command_(command);
|
this->send_command_(command);
|
||||||
|
|
||||||
if (baud_rate != this->original_baud_rate_) {
|
if (baud_rate != this->original_baud_rate_) {
|
||||||
|
@ -272,84 +288,60 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
|
|
||||||
std::string response;
|
std::string response;
|
||||||
ESP_LOGD(TAG, "Waiting for upgrade response");
|
ESP_LOGV(TAG, "Waiting for upgrade response");
|
||||||
this->recv_ret_string_(response, 2000, true); // This can take some time to return
|
this->recv_ret_string_(response, 5000, true); // This can take some time to return
|
||||||
|
|
||||||
// The Nextion display will, if it's ready to accept data, send a 0x05 byte.
|
// The Nextion display will, if it's ready to accept data, send a 0x05 byte.
|
||||||
ESP_LOGD(TAG, "Upgrade response is [%s] - %zu bytes",
|
ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)",
|
||||||
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
|
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
|
||||||
response.length());
|
response.length());
|
||||||
|
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
for (size_t i = 0; i < response.length(); i++) {
|
|
||||||
ESP_LOGD(TAG, "Available %d : 0x%02X", i, response[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.find(0x05) != std::string::npos) {
|
if (response.find(0x05) != std::string::npos) {
|
||||||
ESP_LOGD(TAG, "preparation for tft update done");
|
ESP_LOGV(TAG, "Preparation for TFT upload done");
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str());
|
ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str());
|
||||||
|
ESP_LOGD(TAG, "Close HTTP connection");
|
||||||
|
http_client.end();
|
||||||
|
ESP_LOGV(TAG, "Connection closed");
|
||||||
return this->upload_end_(false);
|
return this->upload_end_(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096
|
ESP_LOGD(TAG, "Uploading TFT to Nextion:");
|
||||||
#ifdef USE_ESP32
|
ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str());
|
||||||
uint32_t chunk_size = 8192;
|
ESP_LOGD(TAG, " File size: %d bytes", this->content_length_);
|
||||||
if (heap_caps_get_free_size(MALLOC_CAP_SPIRAM) > 0) {
|
ESP_LOGD(TAG, " Free heap: %" PRIu32, this->get_free_heap_());
|
||||||
chunk_size = this->content_length_;
|
|
||||||
} else {
|
|
||||||
if (ESP.getFreeHeap() > 81920) { // Ensure some FreeHeap to other things and limit chunk size
|
|
||||||
chunk_size = ESP.getFreeHeap() - 65536;
|
|
||||||
chunk_size = int(chunk_size / 4096) * 4096;
|
|
||||||
chunk_size = chunk_size > 65536 ? 65536 : chunk_size;
|
|
||||||
} else if (ESP.getFreeHeap() < 32768) {
|
|
||||||
chunk_size = 4096;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
|
||||||
uint32_t chunk_size = ESP.getFreeHeap() < 16384 ? 4096 : 8192;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (this->transfer_buffer_ == nullptr) {
|
// Proceed with the content download as before
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
|
||||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
|
||||||
ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap());
|
|
||||||
this->transfer_buffer_ = allocator.allocate(chunk_size);
|
|
||||||
if (this->transfer_buffer_ == nullptr) { // Try a smaller size
|
|
||||||
ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size);
|
|
||||||
chunk_size = 4096;
|
|
||||||
ESP_LOGD(TAG, "Allocating %d buffer", chunk_size);
|
|
||||||
this->transfer_buffer_ = allocator.allocate(chunk_size);
|
|
||||||
|
|
||||||
if (!this->transfer_buffer_)
|
ESP_LOGV(TAG, "Starting transfer by chunks loop");
|
||||||
return this->upload_end_(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->transfer_buffer_size_ = chunk_size;
|
uint32_t position = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
|
||||||
ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d using %zu chunksize, Heap Size %d",
|
|
||||||
this->tft_url_.c_str(), this->content_length_, this->transfer_buffer_size_, ESP.getFreeHeap());
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
while (this->content_length_ > 0) {
|
while (this->content_length_ > 0) {
|
||||||
result = this->upload_by_chunks_(&http, result);
|
int upload_result = upload_by_chunks_(http_client, position);
|
||||||
if (result < 0) {
|
if (upload_result < 0) {
|
||||||
ESP_LOGD(TAG, "Error updating Nextion!");
|
ESP_LOGE(TAG, "Error uploading TFT to Nextion!");
|
||||||
|
ESP_LOGD(TAG, "Close HTTP connection");
|
||||||
|
http_client.end();
|
||||||
|
ESP_LOGV(TAG, "Connection closed");
|
||||||
return this->upload_end_(false);
|
return this->upload_end_(false);
|
||||||
}
|
}
|
||||||
App.feed_wdt();
|
App.feed_wdt();
|
||||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, this->get_free_heap_(), this->content_length_);
|
||||||
ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_);
|
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Successfully updated Nextion!");
|
|
||||||
|
|
||||||
return this->upload_end_(true);
|
ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!");
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Close HTTP connection");
|
||||||
|
http_client.end();
|
||||||
|
ESP_LOGV(TAG, "Connection closed");
|
||||||
|
return upload_end_(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Nextion::upload_end_(bool successful) {
|
bool Nextion::upload_end_(bool successful) {
|
||||||
|
ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful));
|
||||||
this->is_updating_ = false;
|
this->is_updating_ = false;
|
||||||
|
this->ignore_is_setup_ = false;
|
||||||
|
|
||||||
uint32_t baud_rate = this->parent_->get_baud_rate();
|
uint32_t baud_rate = this->parent_->get_baud_rate();
|
||||||
if (baud_rate != this->original_baud_rate_) {
|
if (baud_rate != this->original_baud_rate_) {
|
||||||
|
@ -358,12 +350,12 @@ bool Nextion::upload_end_(bool successful) {
|
||||||
this->parent_->load_settings();
|
this->parent_->load_settings();
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Restarting Nextion");
|
|
||||||
this->soft_reset();
|
|
||||||
if (successful) {
|
if (successful) {
|
||||||
|
ESP_LOGD(TAG, "Restarting ESPHome");
|
||||||
delay(1500); // NOLINT
|
delay(1500); // NOLINT
|
||||||
ESP_LOGD(TAG, "Restarting esphome");
|
arch_restart();
|
||||||
ESP.restart(); // NOLINT(readability-static-accessed-through-instance)
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Nextion TFT upload failed");
|
||||||
}
|
}
|
||||||
return successful;
|
return successful;
|
||||||
}
|
}
|
||||||
|
@ -386,9 +378,10 @@ WiFiClient *Nextion::get_wifi_client_() {
|
||||||
}
|
}
|
||||||
return this->wifi_client_;
|
return this->wifi_client_;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // USE_ESP8266
|
||||||
|
|
||||||
} // namespace nextion
|
} // namespace nextion
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // USE_NEXTION_TFT_UPLOAD
|
|
||||||
#endif // ARDUINO
|
#endif // ARDUINO
|
||||||
|
#endif // USE_NEXTION_TFT_UPLOAD
|
||||||
|
|
Loading…
Reference in a new issue