3 #ifdef USE_NEXTION_TFT_UPLOAD 14 #include <esp_heap_caps.h> 19 static const char *
const TAG =
"nextion.upload.arduino";
25 #if defined(USE_ESP32) 26 return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
27 #elif defined(USE_ESP8266) 28 return EspClass::getFreeHeap();
29 #endif // USE_ESP32 vs USE_ESP8266 33 uint32_t range_size = this->
tft_size_ - range_start;
36 ESP_LOGD(TAG,
"Range start: %" PRIu32, range_start);
37 if (range_size <= 0 or range_end <= range_start) {
38 ESP_LOGD(TAG,
"Range end: %" PRIu32, range_end);
39 ESP_LOGD(TAG,
"Range size: %" PRIu32, range_size);
40 ESP_LOGE(TAG,
"Invalid range");
44 char range_header[32];
45 sprintf(range_header,
"bytes=%" PRIu32
"-%" PRIu32, range_start, range_end);
46 ESP_LOGV(TAG,
"Requesting range: %s", range_header);
47 http_client.addHeader(
"Range", range_header);
48 int code = http_client.GET();
49 if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) {
50 ESP_LOGW(TAG,
"HTTP Request failed; Error: %s", HTTPClient::errorToString(code).c_str());
56 uint8_t *buffer = allocator.
allocate(4096);
58 ESP_LOGE(TAG,
"Failed to allocate upload buffer");
62 std::string recv_string;
65 const uint16_t buffer_size =
67 ESP_LOGV(TAG,
"Fetching %" PRIu16
" bytes from HTTP", buffer_size);
68 uint16_t read_len = 0;
69 int partial_read_len = 0;
70 const uint32_t start_time =
millis();
71 while (read_len < buffer_size &&
millis() - start_time < 5000) {
72 if (http_client.getStreamPtr()->available() > 0) {
74 http_client.getStreamPtr()->readBytes(reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len);
75 read_len += partial_read_len;
76 if (partial_read_len > 0) {
82 if (read_len != buffer_size) {
84 ESP_LOGE(TAG,
"Failed to read full package, received only %" PRIu16
" of %" PRIu16
" bytes", read_len,
91 ESP_LOGV(TAG,
"%d bytes fetched, writing it to UART", read_len);
99 #if defined(USE_ESP32) && defined(USE_PSRAM) 102 "Uploaded %0.2f%%, remaining %" PRIu32
" bytes, free heap: %" PRIu32
" (DRAM) + %" PRIu32
" (PSRAM) bytes",
103 upload_percentage, this->
content_length_, static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
104 static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)));
106 ESP_LOGD(TAG,
"Uploaded %0.2f%%, remaining %" PRIu32
" bytes, free heap: %" PRIu32
" bytes", upload_percentage,
110 if (recv_string[0] == 0x08 && recv_string.size() == 5) {
111 ESP_LOGD(TAG,
"recv_string [%s]",
112 format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
114 for (
int j = 0; j < 4; ++j) {
115 result +=
static_cast<uint8_t
>(recv_string[j + 1]) << (8 * j);
118 ESP_LOGI(TAG,
"Nextion reported new range %" PRIu32, result);
120 range_start = result;
122 range_start = range_end + 1;
127 return range_end + 1;
128 }
else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) {
129 ESP_LOGE(TAG,
"Invalid response from Nextion: [%s]",
130 format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
138 }
else if (read_len == 0) {
139 ESP_LOGV(TAG,
"End of HTTP response reached");
142 ESP_LOGE(TAG,
"Failed to read from HTTP client, error code: %d", read_len);
146 range_start = range_end + 1;
150 return range_end + 1;
154 ESP_LOGD(TAG,
"Nextion TFT upload requested");
155 ESP_LOGD(TAG,
"Exit reparse: %s", YESNO(exit_reparse));
156 ESP_LOGD(TAG,
"URL: %s", this->
tft_url_.c_str());
159 ESP_LOGW(TAG,
"Currently uploading");
164 ESP_LOGE(TAG,
"Network is not connected");
171 ESP_LOGD(TAG,
"Exiting Nextion reparse mode");
173 ESP_LOGW(TAG,
"Failed to request Nextion to exit reparse mode");
180 static const std::vector<uint32_t> SUPPORTED_BAUD_RATES = {2400, 4800, 9600, 19200, 31250, 38400, 57600,
181 115200, 230400, 250000, 256000, 512000, 921600};
182 if (std::find(SUPPORTED_BAUD_RATES.begin(), SUPPORTED_BAUD_RATES.end(), baud_rate) == SUPPORTED_BAUD_RATES.end()) {
185 ESP_LOGD(TAG,
"Baud rate: %" PRIu32, baud_rate);
188 ESP_LOGV(TAG,
"Initializing HTTP client");
190 HTTPClient http_client;
191 http_client.setTimeout(15000);
193 bool begin_status =
false;
195 begin_status = http_client.begin(this->
tft_url_.c_str());
198 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) 199 http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
200 #elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) 201 http_client.setFollowRedirects(
true);
203 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) 204 http_client.setRedirectLimit(3);
207 #endif // USE_ESP8266 210 ESP_LOGD(TAG,
"Connection failed");
213 ESP_LOGD(TAG,
"Connected");
215 http_client.addHeader(
"Range",
"bytes=0-255");
216 const char *header_names[] = {
"Content-Range"};
217 http_client.collectHeaders(header_names, 1);
218 ESP_LOGD(TAG,
"Requesting URL: %s", this->
tft_url_.c_str());
219 http_client.setReuse(
true);
222 int code = http_client.GET();
226 while (code != 200 && code != 206 && tries <= 5) {
227 ESP_LOGW(TAG,
"HTTP Request failed; URL: %s; Error: %s, retrying (%d/5)", this->
tft_url_.c_str(),
228 HTTPClient::errorToString(code).c_str(), tries);
232 code = http_client.GET();
236 if (code != 200 and code != 206) {
240 String content_range_string = http_client.header(
"Content-Range");
241 content_range_string.remove(0, 12);
242 this->
tft_size_ = content_range_string.toInt();
244 ESP_LOGD(TAG,
"TFT file size: %zu bytes", this->
tft_size_);
246 ESP_LOGE(TAG,
"File size check failed.");
247 ESP_LOGD(TAG,
"Close HTTP connection");
249 ESP_LOGV(TAG,
"Connection closed");
252 ESP_LOGV(TAG,
"File size check passed. Proceeding...");
256 ESP_LOGD(TAG,
"Uploading Nextion");
259 ESP_LOGV(TAG,
"Wake-up Nextion");
271 sprintf(command,
"whmi-wris %d,%d,1", this->
content_length_, baud_rate);
274 ESP_LOGV(TAG,
"Clear serial receive buffer");
279 ESP_LOGV(TAG,
"Send upload instruction: %s", command);
283 ESP_LOGD(TAG,
"Changing baud rate from %" PRIu32
" to %" PRIu32
" bps", this->
original_baud_rate_, baud_rate);
290 std::string response;
291 ESP_LOGV(TAG,
"Waiting for upgrade response");
295 ESP_LOGD(TAG,
"Upgrade response is [%s] - %zu byte(s)",
296 format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
300 if (response.find(0x05) != std::string::npos) {
301 ESP_LOGV(TAG,
"Preparation for TFT upload done");
303 ESP_LOGE(TAG,
"Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str());
304 ESP_LOGD(TAG,
"Close HTTP connection");
306 ESP_LOGV(TAG,
"Connection closed");
310 ESP_LOGD(TAG,
"Uploading TFT to Nextion:");
311 ESP_LOGD(TAG,
" URL: %s", this->
tft_url_.c_str());
317 ESP_LOGV(TAG,
"Starting transfer by chunks loop");
322 if (upload_result < 0) {
323 ESP_LOGE(TAG,
"Error uploading TFT to Nextion!");
324 ESP_LOGD(TAG,
"Close HTTP connection");
326 ESP_LOGV(TAG,
"Connection closed");
333 ESP_LOGD(TAG,
"Successfully uploaded TFT to Nextion!");
335 ESP_LOGD(TAG,
"Close HTTP connection");
337 ESP_LOGV(TAG,
"Connection closed");
342 ESP_LOGD(TAG,
"Nextion TFT upload finished: %s", YESNO(successful));
348 ESP_LOGD(TAG,
"Changing baud rate back from %" PRIu32
" to %" PRIu32
" bps", baud_rate, this->
original_baud_rate_);
354 ESP_LOGD(TAG,
"Restarting ESPHome");
358 ESP_LOGE(TAG,
"Nextion TFT upload failed");
365 if (this->
tft_url_.compare(0, 6,
"https:") == 0) {
381 #endif // USE_ESP8266 386 #endif // USE_ARDUINO 387 #endif // USE_NEXTION_TFT_UPLOAD bool ignore_is_setup_
Sends commands ignoring of the Nextion has been setup.
uint32_t get_baud_rate() const
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
void write_array(const uint8_t *data, size_t len)
WiFiClient * get_wifi_client_()
bool upload_tft(uint32_t baud_rate=0, bool exit_reparse=true)
Uploads the TFT file to the Nextion display.
uint32_t get_free_heap_()
Returns the ESP Free Heap memory.
bool send_command_(const std::string &command)
Manually send a raw command to the display and don't wait for an acknowledgement packet.
BearSSL::WiFiClientSecure * wifi_client_secure_
bool upload_end_(bool successful)
Ends the upload process, restart Nextion and, if successful, restarts ESP.
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
uint32_t IRAM_ATTR HOT millis()
virtual void load_settings(bool dump_config)
Load the UART settings.
int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start)
will request chunk_size chunks from the web server and send each to the nextion
bool set_protocol_reparse_mode(bool active_mode)
Sets the Nextion display's protocol reparse mode.
uint32_t original_baud_rate_
Application App
Global storage of Application pointer - only one Application can exist.
void set_baud_rate(uint32_t baud_rate)
void deallocate(T *p, size_t n)
bool upload_first_chunk_sent_
WiFiClient * wifi_client_
Implementation of SPI Controller mode.
void reset_(bool reset_nextion=true)
An STL allocator that uses SPI or internal RAM.
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag)
void IRAM_ATTR HOT delay(uint32_t ms)