3#ifdef USE_NEXTION_TFT_UPLOAD
16static const char *
const TAG =
"nextion.upload.arduino";
17static constexpr size_t NEXTION_MAX_RESPONSE_LOG_BYTES = 16;
24static constexpr uint32_t NEXTION_UPLOAD_ACK_TIMEOUT_MS = 5000;
31 ESP_LOGV(TAG,
"Heap: %" PRIu32, EspClass::getFreeHeap());
33 ESP_LOGD(TAG,
"Range start: %" PRIu32, range_start);
34 if (range_size <= 0 || range_end <= range_start) {
35 ESP_LOGE(TAG,
"Invalid range end: %" PRIu32
", size: %" PRIu32, range_end, range_size);
39 char range_header[32];
40 buf_append_printf(range_header,
sizeof(range_header), 0,
"bytes=%" PRIu32
"-%" PRIu32, range_start, range_end);
41 ESP_LOGV(TAG,
"Range: %s", range_header);
42 http_client.addHeader(
"Range", range_header);
43 int code = http_client.GET();
44 if (code != HTTP_CODE_OK && code != HTTP_CODE_PARTIAL_CONTENT) {
45 ESP_LOGW(TAG,
"HTTP failed: %s", HTTPClient::errorToString(code).c_str());
51 uint8_t *buffer = allocator.
allocate(4096);
53 ESP_LOGE(TAG,
"Buffer alloc failed");
57 std::string recv_string;
60 const uint16_t buffer_size =
62 ESP_LOGV(TAG,
"Fetch %" PRIu16
" bytes", buffer_size);
63 uint16_t read_len = 0;
64 int partial_read_len = 0;
67 if (http_client.getStreamPtr()->available() > 0) {
69 http_client.getStreamPtr()->readBytes(
reinterpret_cast<char *
>(buffer) + read_len, buffer_size - read_len);
70 read_len += partial_read_len;
71 if (partial_read_len > 0) {
77 if (read_len != buffer_size) {
79 ESP_LOGE(TAG,
"Read failed: %" PRIu16
"/%" PRIu16
" bytes", read_len, buffer_size);
85 ESP_LOGV(TAG,
"Fetched %d bytes", read_len);
97 if (!recv_string.empty() && recv_string[0] == 0x08 && recv_string.size() < 5) {
98 const uint32_t deadline =
millis() + NEXTION_UPLOAD_ACK_TIMEOUT_MS;
99 while (recv_string.size() < 5 &&
millis() < deadline) {
103 recv_string.push_back(
static_cast<char>(b));
110 if (recv_string.size() < 5) {
111 ESP_LOGE(TAG,
"Truncated 0x08 response: got %zu bytes within %" PRIu32
"ms", recv_string.size(),
112 NEXTION_UPLOAD_ACK_TIMEOUT_MS);
120 ESP_LOGD(TAG,
"Upload: %0.2f%% (%" PRIu32
" left, heap: %" PRIu32
")", upload_percentage, this->
content_length_,
121 EspClass::getFreeHeap());
123 if (recv_string.empty()) {
124 ESP_LOGW(TAG,
"No response from display after %" PRIu32
"ms", NEXTION_UPLOAD_ACK_TIMEOUT_MS);
129 if (recv_string[0] == 0x08 && recv_string.size() == 5) {
133 format_hex_pretty_to(hex_buf,
reinterpret_cast<const uint8_t *
>(recv_string.data()), recv_string.size()));
135 for (
int j = 0; j < 4; ++j) {
136 result +=
static_cast<uint8_t
>(recv_string[j + 1]) << (8 * j);
139 ESP_LOGI(TAG,
"New range: %" PRIu32, result);
141 range_start = result;
143 range_start = range_end + 1;
148 return range_end + 1;
149 }
else if (recv_string[0] != 0x05 && recv_string[0] != 0x08) {
152 TAG,
"Invalid response: [%s]",
153 format_hex_pretty_to(hex_buf,
reinterpret_cast<const uint8_t *
>(recv_string.data()), recv_string.size()));
161 }
else if (read_len == 0) {
162 ESP_LOGV(TAG,
"HTTP end");
165 ESP_LOGE(TAG,
"HTTP read failed: %d", read_len);
169 range_start = range_end + 1;
173 return range_end + 1;
177 ESP_LOGD(TAG,
"TFT upload requested, exit reparse: %s, URL: %s", YESNO(exit_reparse), this->
tft_url_.c_str());
180 ESP_LOGW(TAG,
"Upload in progress");
185 ESP_LOGE(TAG,
"No network");
192 ESP_LOGD(TAG,
"Exit reparse mode");
194 ESP_LOGW(TAG,
"Exit reparse failed");
201 if (baud_rate <= 0) {
204 ESP_LOGD(TAG,
"Baud rate: %" PRIu32, baud_rate);
207 ESP_LOGV(TAG,
"Init HTTP client, heap: %" PRIu32, EspClass::getFreeHeap());
208 HTTPClient http_client;
211 bool begin_status =
false;
212#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
213 http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
214#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
215 http_client.setFollowRedirects(
true);
217#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
218 http_client.setRedirectLimit(3);
223 ESP_LOGD(TAG,
"Connection failed");
226 ESP_LOGD(TAG,
"Connected");
228 http_client.addHeader(
"Range",
"bytes=0-255");
229 const char *header_names[] = {
"Content-Range"};
230 http_client.collectHeaders(header_names, 1);
231 ESP_LOGD(TAG,
"URL: %s", this->
tft_url_.c_str());
232 http_client.setReuse(
true);
235 int code = http_client.GET();
240 ESP_LOGW(TAG,
"HTTP fail: URL: %s; Error: %s, retry %d/%u", this->
tft_url_.c_str(),
241 HTTPClient::errorToString(code).c_str(), tries, this->tft_upload_http_retries_);
245 code = http_client.GET();
249 if (code != 200 && code != 206) {
250 ESP_LOGE(TAG,
"HTTP request failed with status %d", code);
254 String content_range_string = http_client.header(
"Content-Range");
255 content_range_string.remove(0, 12);
256 this->
tft_size_ = content_range_string.toInt();
258 ESP_LOGD(TAG,
"TFT size: %zu bytes", this->
tft_size_);
260 ESP_LOGE(TAG,
"Size check failed");
261 ESP_LOGD(TAG,
"Close HTTP");
263 ESP_LOGV(TAG,
"Connection closed");
266 ESP_LOGV(TAG,
"Size check OK");
270 ESP_LOGD(TAG,
"Uploading");
273 ESP_LOGV(TAG,
"Wake-up");
278 ESP_LOGV(TAG,
"Heap: %" PRIu32, EspClass::getFreeHeap());
285 snprintf(command,
sizeof(command),
"whmi-wris %" PRIu32
",%" PRIu32
",1", this->
content_length_, baud_rate);
288 ESP_LOGV(TAG,
"Clear RX buffer");
292 ESP_LOGV(TAG,
"Heap: %" PRIu32
", upload cmd: %s", EspClass::getFreeHeap(), command);
303 std::string response;
304 ESP_LOGV(TAG,
"Wait upload resp");
309 ESP_LOGD(TAG,
"Upload resp: [%s] %zu B",
310 format_hex_pretty_to(hex_buf,
reinterpret_cast<const uint8_t *
>(response.data()), response.size()),
312 ESP_LOGV(TAG,
"Heap: %" PRIu32, EspClass::getFreeHeap());
314 if (response.find(0x05) != std::string::npos) {
315 ESP_LOGV(TAG,
"Upload prep done");
317 ESP_LOGE(TAG,
"Prep failed %d '%s'", response[0], response.c_str());
318 ESP_LOGD(TAG,
"Close HTTP");
320 ESP_LOGV(TAG,
"Connection closed");
329 this->
tft_url_.c_str(), this->content_length_, EspClass::getFreeHeap());
333 ESP_LOGV(TAG,
"Start chunk transfer");
338 if (upload_result < 0) {
339 ESP_LOGE(TAG,
"Upload error");
340 ESP_LOGD(TAG,
"Close HTTP");
342 ESP_LOGV(TAG,
"Connection closed");
346 ESP_LOGV(TAG,
"Heap: %" PRIu32
" left: %" PRIu32, EspClass::getFreeHeap(), this->
content_length_);
349 ESP_LOGD(TAG,
"Upload complete");
351 ESP_LOGV(TAG,
"Close HTTP");
353 ESP_LOGV(TAG,
"Connection closed");
358 if (this->
tft_url_.starts_with(
"https:")) {
void feed_wdt()
Feed the task watchdog.
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.
An STL allocator that uses SPI or internal RAM.
void deallocate(T *p, size_t n)
int upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start)
will request 4096 bytes chunks from the web server and send each to Nextion
bool send_command_(const std::string &command)
Manually send a raw command to the display and don't wait for an acknowledgement packet.
uint8_t tft_upload_http_retries_
HTTP retry count (default: 5)
WiFiClient * wifi_client_
bool upload_tft(uint32_t baud_rate=0, bool exit_reparse=true)
Uploads the TFT file to the Nextion display.
uint16_t tft_upload_http_timeout_
HTTP timeout in ms (default: 4.5s)
bool set_protocol_reparse_mode(bool active_mode)
Sets the Nextion display's protocol reparse mode.
bool upload_end_(bool successful)
Ends the upload process, restart Nextion and, if successful, restarts ESP.
BearSSL::WiFiClientSecure * wifi_client_secure_
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag)
struct esphome::nextion::Nextion::@147 connection_state_
Status flags for Nextion display state management.
WiFiClient * get_wifi_client_()
void reset_(bool reset_nextion=true)
uint32_t original_baud_rate_
bool upload_first_chunk_sent_
void set_baud_rate(uint32_t baud_rate)
virtual void load_settings(bool dump_config)=0
Load the UART settings.
uint32_t get_baud_rate() const
bool read_byte(uint8_t *data)
void write_array(const uint8_t *data, size_t len)
ESPHOME_ALWAYS_INLINE bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.