ESPHome  2023.11.6
nextion.cpp
Go to the documentation of this file.
1 #include "nextion.h"
2 #include "esphome/core/util.h"
3 #include "esphome/core/log.h"
5 
6 namespace esphome {
7 namespace nextion {
8 
9 static const char *const TAG = "nextion";
10 
12  this->is_setup_ = false;
13  this->ignore_is_setup_ = true;
14 
15  // Wake up the nextion
16  this->send_command_("bkcmd=0");
17  this->send_command_("sleep=0");
18 
19  this->send_command_("bkcmd=0");
20  this->send_command_("sleep=0");
21 
22  // Reboot it
23  this->send_command_("rest");
24 
25  this->ignore_is_setup_ = false;
26 }
27 
28 bool Nextion::send_command_(const std::string &command) {
29  if (!this->ignore_is_setup_ && !this->is_setup()) {
30  return false;
31  }
32 
33  ESP_LOGN(TAG, "send_command %s", command.c_str());
34 
35  this->write_str(command.c_str());
36  const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
37  this->write_array(to_send, sizeof(to_send));
38  return true;
39 }
40 
42  if (this->get_is_connected_())
43  return true;
44 
45  if (this->comok_sent_ == 0) {
46  this->reset_(false);
47 
48  this->ignore_is_setup_ = true;
49  this->send_command_("boguscommand=0"); // bogus command. needed sometimes after updating
50  this->send_command_("connect");
51 
52  this->comok_sent_ = millis();
53  this->ignore_is_setup_ = false;
54 
55  return false;
56  }
57 
58  if (millis() - this->comok_sent_ <= 500) // Wait 500 ms
59  return false;
60 
61  std::string response;
62 
63  this->recv_ret_string_(response, 0, false);
64  if (!response.empty() && response[0] == 0x1A) {
65  // Swallow invalid variable name responses that may be caused by the above commands
66  ESP_LOGD(TAG, "0x1A error ignored during setup");
67  return false;
68  }
69  if (response.empty() || response.find("comok") == std::string::npos) {
70 #ifdef NEXTION_PROTOCOL_LOG
71  ESP_LOGN(TAG, "Bad connect request %s", response.c_str());
72  for (size_t i = 0; i < response.length(); i++) {
73  ESP_LOGN(TAG, "response %s %d %d %c", response.c_str(), i, response[i], response[i]);
74  }
75 #endif
76 
77  ESP_LOGW(TAG, "Nextion is not connected! ");
78  comok_sent_ = 0;
79  return false;
80  }
81 
82  this->ignore_is_setup_ = true;
83  ESP_LOGI(TAG, "Nextion is connected");
84  this->is_connected_ = true;
85 
86  ESP_LOGN(TAG, "connect request %s", response.c_str());
87 
88  size_t start;
89  size_t end = 0;
90  std::vector<std::string> connect_info;
91  while ((start = response.find_first_not_of(',', end)) != std::string::npos) {
92  end = response.find(',', start);
93  connect_info.push_back(response.substr(start, end - start));
94  }
95 
96  if (connect_info.size() == 7) {
97  ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size());
98 
99  this->device_model_ = connect_info[2];
100  this->firmware_version_ = connect_info[3];
101  this->serial_number_ = connect_info[5];
102  this->flash_size_ = connect_info[6];
103  } else {
104  ESP_LOGE(TAG, "Nextion returned bad connect value \"%s\"", response.c_str());
105  }
106 
107  this->ignore_is_setup_ = false;
108  this->dump_config();
109  return true;
110 }
111 
112 void Nextion::reset_(bool reset_nextion) {
113  uint8_t d;
114 
115  while (this->available()) { // Clear receive buffer
116  this->read_byte(&d);
117  };
118  this->nextion_queue_.clear();
119  this->waveform_queue_.clear();
120 }
121 
123  ESP_LOGCONFIG(TAG, "Nextion:");
124  ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str());
125  ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str());
126  ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str());
127  ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str());
128  ESP_LOGCONFIG(TAG, " Wake On Touch: %s", this->auto_wake_on_touch_ ? "True" : "False");
129 
130  if (this->touch_sleep_timeout_ != 0) {
131  ESP_LOGCONFIG(TAG, " Touch Timeout: %d", this->touch_sleep_timeout_);
132  }
133 
134  if (this->wake_up_page_ != -1) {
135  ESP_LOGCONFIG(TAG, " Wake Up Page : %d", this->wake_up_page_);
136  }
137 
138  if (this->start_up_page_ != -1) {
139  ESP_LOGCONFIG(TAG, " Start Up Page : %d", this->start_up_page_);
140  }
141 }
142 
145  if (!this->is_setup()) {
146  return;
147  }
148  if (this->writer_.has_value()) {
149  (*this->writer_)(*this);
150  }
151 }
152 
153 void Nextion::add_sleep_state_callback(std::function<void()> &&callback) {
154  this->sleep_callback_.add(std::move(callback));
155 }
156 
157 void Nextion::add_wake_state_callback(std::function<void()> &&callback) {
158  this->wake_callback_.add(std::move(callback));
159 }
160 
161 void Nextion::add_setup_state_callback(std::function<void()> &&callback) {
162  this->setup_callback_.add(std::move(callback));
163 }
164 
165 void Nextion::add_new_page_callback(std::function<void(uint8_t)> &&callback) {
166  this->page_callback_.add(std::move(callback));
167 }
168 
170  if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
171  return;
172 
173  for (auto *binarysensortype : this->binarysensortype_) {
174  binarysensortype->update_component();
175  }
176  for (auto *sensortype : this->sensortype_) {
177  sensortype->update_component();
178  }
179  for (auto *switchtype : this->switchtype_) {
180  switchtype->update_component();
181  }
182  for (auto *textsensortype : this->textsensortype_) {
183  textsensortype->update_component();
184  }
185 }
186 
187 bool Nextion::send_command_printf(const char *format, ...) {
188  if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
189  return false;
190 
191  char buffer[256];
192  va_list arg;
193  va_start(arg, format);
194  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
195  va_end(arg);
196  if (ret <= 0) {
197  ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
198  return false;
199  }
200 
201  if (this->send_command_(buffer)) {
202  this->add_no_result_to_queue_("send_command_printf");
203  return true;
204  }
205  return false;
206 }
207 
208 #ifdef NEXTION_PROTOCOL_LOG
210  ESP_LOGN(TAG, "print_queue_members_ (top 10) size %zu", this->nextion_queue_.size());
211  ESP_LOGN(TAG, "*******************************************");
212  int count = 0;
213  for (auto *i : this->nextion_queue_) {
214  if (count++ == 10)
215  break;
216 
217  if (i == nullptr) {
218  ESP_LOGN(TAG, "Nextion queue is null");
219  } else {
220  ESP_LOGN(TAG, "Nextion queue type: %d:%s , name: %s", i->component->get_queue_type(),
221  i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str());
222  }
223  }
224  ESP_LOGN(TAG, "*******************************************");
225 }
226 #endif
227 
229  if (!this->check_connect_() || this->is_updating_)
230  return;
231 
232  if (this->nextion_reports_is_setup_ && !this->sent_setup_commands_) {
233  this->ignore_is_setup_ = true;
234  this->sent_setup_commands_ = true;
235  this->send_command_("bkcmd=3"); // Always, returns 0x00 to 0x23 result of serial command.
236 
238 
239  // Check if a startup page has been set and send the command
240  if (this->start_up_page_ != -1) {
241  this->goto_page(this->start_up_page_);
242  }
243 
245 
246  if (this->touch_sleep_timeout_ != 0) {
248  }
249 
250  if (this->wake_up_page_ != -1) {
251  this->set_wake_up_page(this->wake_up_page_);
252  }
253 
254  this->ignore_is_setup_ = false;
255  }
256 
257  this->process_serial_(); // Receive serial data
258  this->process_nextion_commands_(); // Process nextion return commands
259 
260  if (!this->nextion_reports_is_setup_) {
261  if (this->started_ms_ == 0)
262  this->started_ms_ = millis();
263 
264  if (this->started_ms_ + this->startup_override_ms_ < millis()) {
265  ESP_LOGD(TAG, "Manually set nextion report ready");
266  this->nextion_reports_is_setup_ = true;
267  }
268  }
269 }
270 
271 bool Nextion::remove_from_q_(bool report_empty) {
272  if (this->nextion_queue_.empty()) {
273  if (report_empty) {
274  ESP_LOGE(TAG, "Nextion queue is empty!");
275  }
276  return false;
277  }
278 
279  NextionQueue *nb = this->nextion_queue_.front();
280  NextionComponentBase *component = nb->component;
281 
282  ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str());
283 
284  if (component->get_queue_type() == NextionQueueType::NO_RESULT) {
285  if (component->get_variable_name() == "sleep_wake") {
286  this->is_sleeping_ = false;
287  }
288  delete component; // NOLINT(cppcoreguidelines-owning-memory)
289  }
290  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
291  this->nextion_queue_.pop_front();
292  return true;
293 }
294 
296  uint8_t d;
297 
298  while (this->available()) {
299  read_byte(&d);
300  this->command_data_ += d;
301  }
302 }
303 // nextion.tech/instruction-set/
305  if (this->command_data_.length() == 0) {
306  return;
307  }
308 
309  size_t to_process_length = 0;
310  std::string to_process;
311 
312  ESP_LOGN(TAG, "this->command_data_ %s length %d", this->command_data_.c_str(), this->command_data_.length());
313 #ifdef NEXTION_PROTOCOL_LOG
314  this->print_queue_members_();
315 #endif
316  while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) {
317  ESP_LOGN(TAG, "print_queue_members_ size %zu", this->nextion_queue_.size());
318  while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() &&
319  static_cast<uint8_t>(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) {
320  ++to_process_length;
321  ESP_LOGN(TAG, "Add extra 0xFF to process");
322  }
323 
324  this->nextion_event_ = this->command_data_[0];
325 
326  to_process_length -= 1;
327  to_process = this->command_data_.substr(1, to_process_length);
328 
329  switch (this->nextion_event_) {
330  case 0x00: // instruction sent by user has failed
331  ESP_LOGW(TAG, "Nextion reported invalid instruction!");
332  this->remove_from_q_();
333 
334  break;
335  case 0x01: // instruction sent by user was successful
336 
337  ESP_LOGVV(TAG, "instruction sent by user was successful");
338  ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False");
339 
340  this->remove_from_q_();
341  if (!this->is_setup_) {
342  if (this->nextion_queue_.empty()) {
343  ESP_LOGD(TAG, "Nextion is setup");
344  this->is_setup_ = true;
345  this->setup_callback_.call();
346  }
347  }
348 
349  break;
350  case 0x02: // invalid Component ID or name was used
351  ESP_LOGW(TAG, "Nextion reported component ID or name invalid!");
352  this->remove_from_q_();
353  break;
354  case 0x03: // invalid Page ID or name was used
355  ESP_LOGW(TAG, "Nextion reported page ID invalid!");
356  this->remove_from_q_();
357  break;
358  case 0x04: // invalid Picture ID was used
359  ESP_LOGW(TAG, "Nextion reported picture ID invalid!");
360  this->remove_from_q_();
361  break;
362  case 0x05: // invalid Font ID was used
363  ESP_LOGW(TAG, "Nextion reported font ID invalid!");
364  this->remove_from_q_();
365  break;
366  case 0x06: // File operation fails
367  ESP_LOGW(TAG, "Nextion File operation fail!");
368  break;
369  case 0x09: // Instructions with CRC validation fails their CRC check
370  ESP_LOGW(TAG, "Nextion Instructions with CRC validation fails their CRC check!");
371  break;
372  case 0x11: // invalid Baud rate was used
373  ESP_LOGW(TAG, "Nextion reported baud rate invalid!");
374  break;
375  case 0x12: // invalid Waveform ID or Channel # was used
376  if (this->waveform_queue_.empty()) {
377  ESP_LOGW(TAG,
378  "Nextion reported invalid Waveform ID or Channel # was used but no waveform sensor in queue found!");
379  } else {
380  auto &nb = this->waveform_queue_.front();
381  NextionComponentBase *component = nb->component;
382 
383  ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!",
384  component->get_component_id(), component->get_wave_channel_id());
385 
386  ESP_LOGN(TAG, "Removing waveform from queue with component id %d and waveform id %d",
387  component->get_component_id(), component->get_wave_channel_id());
388 
389  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
390  this->waveform_queue_.pop_front();
391  }
392  break;
393  case 0x1A: // variable name invalid
394  ESP_LOGW(TAG, "Nextion reported variable name invalid!");
395  this->remove_from_q_();
396  break;
397  case 0x1B: // variable operation invalid
398  ESP_LOGW(TAG, "Nextion reported variable operation invalid!");
399  this->remove_from_q_();
400  break;
401  case 0x1C: // failed to assign
402  ESP_LOGW(TAG, "Nextion reported failed to assign variable!");
403  this->remove_from_q_();
404  break;
405  case 0x1D: // operate EEPROM failed
406  ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!");
407  break;
408  case 0x1E: // parameter quantity invalid
409  ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!");
410  this->remove_from_q_();
411  break;
412  case 0x1F: // IO operation failed
413  ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!");
414  break;
415  case 0x20: // undefined escape characters
416  ESP_LOGW(TAG, "Nextion reported undefined escape characters!");
417  this->remove_from_q_();
418  break;
419  case 0x23: // too long variable name
420  ESP_LOGW(TAG, "Nextion reported too long variable name!");
421  this->remove_from_q_();
422  break;
423  case 0x24: // Serial Buffer overflow occurs
424  ESP_LOGW(TAG, "Nextion reported Serial Buffer overflow!");
425  break;
426  case 0x65: { // touch event return data
427  if (to_process_length != 3) {
428  ESP_LOGW(TAG, "Touch event data is expecting 3, received %zu", to_process_length);
429  break;
430  }
431 
432  uint8_t page_id = to_process[0];
433  uint8_t component_id = to_process[1];
434  uint8_t touch_event = to_process[2]; // 0 -> release, 1 -> press
435  ESP_LOGD(TAG, "Got touch page=%u component=%u type=%s", page_id, component_id,
436  touch_event ? "PRESS" : "RELEASE");
437  for (auto *touch : this->touch_) {
438  touch->process_touch(page_id, component_id, touch_event != 0);
439  }
440  break;
441  }
442  case 0x66: { // Nextion initiated new page event return data.
443  // Also is used for sendme command which we never explicitly initiate
444  if (to_process_length != 1) {
445  ESP_LOGW(TAG, "New page event data is expecting 1, received %zu", to_process_length);
446  break;
447  }
448 
449  uint8_t page_id = to_process[0];
450  ESP_LOGD(TAG, "Got new page=%u", page_id);
451  this->page_callback_.call(page_id);
452  break;
453  }
454  case 0x67: { // Touch Coordinate (awake)
455  break;
456  }
457  case 0x68: { // touch coordinate data (sleep)
458 
459  if (to_process_length != 5) {
460  ESP_LOGW(TAG, "Touch coordinate data is expecting 5, received %zu", to_process_length);
461  ESP_LOGW(TAG, "%s", to_process.c_str());
462  break;
463  }
464 
465  uint16_t x = (uint16_t(to_process[0]) << 8) | to_process[1];
466  uint16_t y = (uint16_t(to_process[2]) << 8) | to_process[3];
467  uint8_t touch_event = to_process[4]; // 0 -> release, 1 -> press
468  ESP_LOGD(TAG, "Got touch at x=%u y=%u type=%s", x, y, touch_event ? "PRESS" : "RELEASE");
469  break;
470  }
471 
472  // 0x70 0x61 0x62 0x31 0x32 0x33 0xFF 0xFF 0xFF
473  // Returned when using get command for a string.
474  // Each byte is converted to char.
475  // data: ab123
476  case 0x70: // string variable data return
477  {
478  if (this->nextion_queue_.empty()) {
479  ESP_LOGW(TAG, "ERROR: Received string return but the queue is empty");
480  break;
481  }
482 
483  NextionQueue *nb = this->nextion_queue_.front();
484  NextionComponentBase *component = nb->component;
485 
486  if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) {
487  ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor",
488  component->get_variable_name().c_str());
489  } else {
490  ESP_LOGN(TAG, "Received get_string response: \"%s\" for component id: %s, type: %s", to_process.c_str(),
491  component->get_variable_name().c_str(), component->get_queue_type_string().c_str());
492  component->set_state_from_string(to_process, true, false);
493  }
494 
495  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
496  this->nextion_queue_.pop_front();
497 
498  break;
499  }
500  // 0x71 0x01 0x02 0x03 0x04 0xFF 0xFF 0xFF
501  // Returned when get command to return a number
502  // 4 byte 32-bit value in little endian order.
503  // (0x01+0x02*256+0x03*65536+0x04*16777216)
504  // data: 67305985
505  case 0x71: // numeric variable data return
506  {
507  if (this->nextion_queue_.empty()) {
508  ESP_LOGE(TAG, "ERROR: Received numeric return but the queue is empty");
509  break;
510  }
511 
512  if (to_process_length == 0) {
513  ESP_LOGE(TAG, "ERROR: Received numeric return but no data!");
514  break;
515  }
516 
517  int dataindex = 0;
518 
519  int value = 0;
520 
521  for (int i = 0; i < 4; ++i) {
522  value += to_process[i] << (8 * i);
523  ++dataindex;
524  }
525 
526  NextionQueue *nb = this->nextion_queue_.front();
527  NextionComponentBase *component = nb->component;
528 
529  if (component->get_queue_type() != NextionQueueType::SENSOR &&
531  component->get_queue_type() != NextionQueueType::SWITCH) {
532  ESP_LOGE(TAG, "ERROR: Received numeric return but next in queue \"%s\" is not a valid sensor type %d",
533  component->get_variable_name().c_str(), component->get_queue_type());
534  } else {
535  ESP_LOGN(TAG, "Received numeric return for variable %s, queue type %d:%s, value %d",
536  component->get_variable_name().c_str(), component->get_queue_type(),
537  component->get_queue_type_string().c_str(), value);
538  component->set_state_from_int(value, true, false);
539  }
540 
541  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
542  this->nextion_queue_.pop_front();
543 
544  break;
545  }
546 
547  case 0x86: { // device automatically enters into sleep mode
548  ESP_LOGVV(TAG, "Received Nextion entering sleep automatically");
549  this->is_sleeping_ = true;
550  this->sleep_callback_.call();
551  break;
552  }
553  case 0x87: // device automatically wakes up
554  {
555  ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically");
556  this->is_sleeping_ = false;
557  this->wake_callback_.call();
558  this->all_components_send_state_(false);
559  break;
560  }
561  case 0x88: // system successful start up
562  {
563  ESP_LOGD(TAG, "system successful start up %zu", to_process_length);
564  this->nextion_reports_is_setup_ = true;
565  break;
566  }
567  case 0x89: { // start SD card upgrade
568  break;
569  }
570  // Data from nextion is
571  // 0x90 - Start
572  // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
573  // 00 - NULL
574  // 00/01 - Single byte for on/off
575  // FF FF FF - End
576  case 0x90: { // Switched component
577  std::string variable_name;
578 
579  // Get variable name
580  auto index = to_process.find('\0');
581  if (index == std::string::npos || (to_process_length - index - 1) < 1) {
582  ESP_LOGE(TAG, "Bad switch component data received for 0x90 event!");
583  ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
584  break;
585  }
586 
587  variable_name = to_process.substr(0, index);
588  ++index;
589 
590  ESP_LOGN(TAG, "Got Switch variable_name=%s value=%d", variable_name.c_str(), to_process[0] != 0);
591 
592  for (auto *switchtype : this->switchtype_) {
593  switchtype->process_bool(variable_name, to_process[index] != 0);
594  }
595  break;
596  }
597  // Data from nextion is
598  // 0x91 - Start
599  // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
600  // 00 - NULL
601  // variable length of 0x71 return data: prints temp1.val,0
602  // FF FF FF - End
603  case 0x91: { // Sensor component
604  std::string variable_name;
605 
606  auto index = to_process.find('\0');
607  if (index == std::string::npos || (to_process_length - index - 1) != 4) {
608  ESP_LOGE(TAG, "Bad sensor component data received for 0x91 event!");
609  ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
610  break;
611  }
612 
613  index = to_process.find('\0');
614  variable_name = to_process.substr(0, index);
615  // // Get variable name
616  int value = 0;
617  for (int i = 0; i < 4; ++i) {
618  value += to_process[i + index + 1] << (8 * i);
619  }
620 
621  ESP_LOGN(TAG, "Got sensor variable_name=%s value=%d", variable_name.c_str(), value);
622 
623  for (auto *sensor : this->sensortype_) {
624  sensor->process_sensor(variable_name, value);
625  }
626  break;
627  }
628 
629  // Data from nextion is
630  // 0x92 - Start
631  // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
632  // 00 - NULL
633  // variable length of 0x70 return formatted data (bytes) that contain the text prints temp1.txt,0
634  // 00 - NULL
635  // FF FF FF - End
636  case 0x92: { // Text Sensor Component
637  std::string variable_name;
638  std::string text_value;
639 
640  // Get variable name
641  auto index = to_process.find('\0');
642  if (index == std::string::npos || (to_process_length - index - 1) < 1) {
643  ESP_LOGE(TAG, "Bad text sensor component data received for 0x92 event!");
644  ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
645  break;
646  }
647 
648  variable_name = to_process.substr(0, index);
649  ++index;
650 
651  text_value = to_process.substr(index);
652 
653  ESP_LOGN(TAG, "Got Text Sensor variable_name=%s value=%s", variable_name.c_str(), text_value.c_str());
654 
655  // NextionTextSensorResponseQueue *nq = new NextionTextSensorResponseQueue;
656  // nq->variable_name = variable_name;
657  // nq->state = text_value;
658  // this->textsensorq_.push_back(nq);
659  for (auto *textsensortype : this->textsensortype_) {
660  textsensortype->process_text(variable_name, text_value);
661  }
662  break;
663  }
664  // Data from nextion is
665  // 0x93 - Start
666  // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
667  // 00 - NULL
668  // 00/01 - Single byte for on/off
669  // FF FF FF - End
670  case 0x93: { // Binary Sensor component
671  std::string variable_name;
672 
673  // Get variable name
674  auto index = to_process.find('\0');
675  if (index == std::string::npos || (to_process_length - index - 1) < 1) {
676  ESP_LOGE(TAG, "Bad binary sensor component data received for 0x92 event!");
677  ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
678  break;
679  }
680 
681  variable_name = to_process.substr(0, index);
682  ++index;
683 
684  ESP_LOGN(TAG, "Got Binary Sensor variable_name=%s value=%d", variable_name.c_str(), to_process[index] != 0);
685 
686  for (auto *binarysensortype : this->binarysensortype_) {
687  binarysensortype->process_bool(&variable_name[0], to_process[index] != 0);
688  }
689  break;
690  }
691  case 0xFD: { // data transparent transmit finished
692  ESP_LOGVV(TAG, "Nextion reported data transmit finished!");
693  this->check_pending_waveform_();
694  break;
695  }
696  case 0xFE: { // data transparent transmit ready
697  ESP_LOGVV(TAG, "Nextion reported ready for transmit!");
698  if (this->waveform_queue_.empty()) {
699  ESP_LOGE(TAG, "No waveforms in queue to send data!");
700  break;
701  }
702 
703  auto &nb = this->waveform_queue_.front();
704  auto *component = nb->component;
705  size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
706  : 255; // ADDT command can only send 255
707 
708  this->write_array(component->get_wave_buffer().data(), static_cast<int>(buffer_to_send));
709 
710  ESP_LOGN(TAG, "Nextion sending waveform data for component id %d and waveform id %d, size %zu",
711  component->get_component_id(), component->get_wave_channel_id(), buffer_to_send);
712 
713  component->clear_wave_buffer(buffer_to_send);
714  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
715  this->waveform_queue_.pop_front();
716  break;
717  }
718  default:
719  ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", this->nextion_event_);
720  break;
721  }
722 
723  // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1);
724  this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1);
725  // App.feed_wdt(); Remove before master merge
726  this->process_serial_();
727  }
728 
729  uint32_t ms = millis();
730 
731  if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) {
732  for (size_t i = 0; i < this->nextion_queue_.size(); i++) {
733  NextionComponentBase *component = this->nextion_queue_[i]->component;
734  if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) {
735  if (this->nextion_queue_[i]->queue_time == 0) {
736  ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0",
737  component->get_queue_type_string().c_str(), component->get_variable_name().c_str());
738  }
739 
740  if (component->get_variable_name() == "sleep_wake") {
741  this->is_sleeping_ = false;
742  }
743 
744  ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\"", component->get_queue_type_string().c_str(),
745  component->get_variable_name().c_str());
746 
747  if (component->get_queue_type() == NextionQueueType::NO_RESULT) {
748  if (component->get_variable_name() == "sleep_wake") {
749  this->is_sleeping_ = false;
750  }
751  delete component; // NOLINT(cppcoreguidelines-owning-memory)
752  }
753 
754  delete this->nextion_queue_[i]; // NOLINT(cppcoreguidelines-owning-memory)
755 
756  this->nextion_queue_.erase(this->nextion_queue_.begin() + i);
757  i--;
758 
759  } else {
760  break;
761  }
762  }
763  }
764  ESP_LOGN(TAG, "Loop End");
765  // App.feed_wdt(); Remove before master merge
766  this->process_serial_();
767 } // namespace nextion
768 
769 void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) {
770  this->set_nextion_sensor_state(static_cast<NextionQueueType>(queue_type), name, state);
771 }
772 
773 void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state) {
774  ESP_LOGN(TAG, "Received state for variable %s, state %lf for queue type %d", name.c_str(), state, queue_type);
775 
776  switch (queue_type) {
778  for (auto *sensor : this->sensortype_) {
779  if (name == sensor->get_variable_name()) {
780  sensor->set_state(state, true, true);
781  break;
782  }
783  }
784  break;
785  }
787  for (auto *sensor : this->binarysensortype_) {
788  if (name == sensor->get_variable_name()) {
789  sensor->set_state(state != 0, true, true);
790  break;
791  }
792  }
793  break;
794  }
796  for (auto *sensor : this->switchtype_) {
797  if (name == sensor->get_variable_name()) {
798  sensor->set_state(state != 0, true, true);
799  break;
800  }
801  }
802  break;
803  }
804  default: {
805  ESP_LOGW(TAG, "set_nextion_sensor_state does not support a queue type %d", queue_type);
806  }
807  }
808 }
809 
810 void Nextion::set_nextion_text_state(const std::string &name, const std::string &state) {
811  ESP_LOGD(TAG, "Received state for variable %s, state %s", name.c_str(), state.c_str());
812 
813  for (auto *sensor : this->textsensortype_) {
814  if (name == sensor->get_variable_name()) {
815  sensor->set_state(state, true, true);
816  break;
817  }
818  }
819 }
820 
821 void Nextion::all_components_send_state_(bool force_update) {
822  ESP_LOGD(TAG, "all_components_send_state_ ");
823  for (auto *binarysensortype : this->binarysensortype_) {
824  if (force_update || binarysensortype->get_needs_to_send_update())
825  binarysensortype->send_state_to_nextion();
826  }
827  for (auto *sensortype : this->sensortype_) {
828  if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_chan_id() == 0)
829  sensortype->send_state_to_nextion();
830  }
831  for (auto *switchtype : this->switchtype_) {
832  if (force_update || switchtype->get_needs_to_send_update())
833  switchtype->send_state_to_nextion();
834  }
835  for (auto *textsensortype : this->textsensortype_) {
836  if (force_update || textsensortype->get_needs_to_send_update())
837  textsensortype->send_state_to_nextion();
838  }
839 }
840 
841 void Nextion::update_components_by_prefix(const std::string &prefix) {
842  for (auto *binarysensortype : this->binarysensortype_) {
843  if (binarysensortype->get_variable_name().find(prefix, 0) != std::string::npos)
844  binarysensortype->update_component_settings(true);
845  }
846  for (auto *sensortype : this->sensortype_) {
847  if (sensortype->get_variable_name().find(prefix, 0) != std::string::npos)
848  sensortype->update_component_settings(true);
849  }
850  for (auto *switchtype : this->switchtype_) {
851  if (switchtype->get_variable_name().find(prefix, 0) != std::string::npos)
852  switchtype->update_component_settings(true);
853  }
854  for (auto *textsensortype : this->textsensortype_) {
855  if (textsensortype->get_variable_name().find(prefix, 0) != std::string::npos)
856  textsensortype->update_component_settings(true);
857  }
858 }
859 
860 uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag) {
861  uint16_t ret = 0;
862  uint8_t c = 0;
863  uint8_t nr_of_ff_bytes = 0;
864  uint64_t start;
865  bool exit_flag = false;
866  bool ff_flag = false;
867 
868  start = millis();
869 
870  while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
871  this->read_byte(&c);
872  if (c == 0xFF) {
873  nr_of_ff_bytes++;
874  } else {
875  nr_of_ff_bytes = 0;
876  ff_flag = false;
877  }
878 
879  if (nr_of_ff_bytes >= 3)
880  ff_flag = true;
881 
882  response += (char) c;
883  if (recv_flag) {
884  if (response.find(0x05) != std::string::npos) {
885  exit_flag = true;
886  }
887  }
888  App.feed_wdt();
889  delay(1);
890 
891  if (exit_flag || ff_flag) {
892  break;
893  }
894  }
895 
896  if (ff_flag)
897  response = response.substr(0, response.length() - 3); // Remove last 3 0xFF
898 
899  ret = response.length();
900  return ret;
901 }
902 
908 void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
909  // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
910  nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
911 
912  // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
913  nextion_queue->component = new nextion::NextionComponentBase;
914  nextion_queue->component->set_variable_name(variable_name);
915 
916  nextion_queue->queue_time = millis();
917 
918  this->nextion_queue_.push_back(nextion_queue);
919 
920  ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str());
921 }
922 
929 void Nextion::add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command) {
930  if ((!this->is_setup() && !this->ignore_is_setup_) || command.empty())
931  return;
932 
933  if (this->send_command_(command)) {
934  this->add_no_result_to_queue_(variable_name);
935  }
936 }
937 
938 bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format,
939  ...) {
940  if ((!this->is_setup() && !this->ignore_is_setup_))
941  return false;
942 
943  char buffer[256];
944  va_list arg;
945  va_start(arg, format);
946  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
947  va_end(arg);
948  if (ret <= 0) {
949  ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
950  return false;
951  }
952 
953  this->add_no_result_to_queue_with_command_(variable_name, buffer);
954  return true;
955 }
956 
964 bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) {
965  if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
966  return false;
967 
968  char buffer[256];
969  va_list arg;
970  va_start(arg, format);
971  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
972  va_end(arg);
973  if (ret <= 0) {
974  ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
975  return false;
976  }
977 
978  this->add_no_result_to_queue_with_command_(variable_name, buffer);
979  return true;
980 }
981 
993  state_value);
994 }
995 
996 void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name,
997  const std::string &variable_name_to_send, int state_value) {
998  this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value);
999 }
1000 
1001 void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
1002  const std::string &variable_name_to_send, int state_value,
1003  bool is_sleep_safe) {
1004  if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping()))
1005  return;
1006 
1007  this->add_no_result_to_queue_with_ignore_sleep_printf_(variable_name, "%s=%d", variable_name_to_send.c_str(),
1008  state_value);
1009 }
1010 
1019 void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) {
1021  state_value);
1022 }
1023 void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name,
1024  const std::string &variable_name_to_send,
1025  const std::string &state_value) {
1026  this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value);
1027 }
1028 
1029 void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
1030  const std::string &variable_name_to_send,
1031  const std::string &state_value, bool is_sleep_safe) {
1032  if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping()))
1033  return;
1034 
1035  this->add_no_result_to_queue_with_printf_(variable_name, "%s=\"%s\"", variable_name_to_send.c_str(),
1036  state_value.c_str());
1037 }
1038 
1040  if ((!this->is_setup() && !this->ignore_is_setup_))
1041  return;
1042 
1043  // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
1044  nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
1045 
1046  nextion_queue->component = component;
1047  nextion_queue->queue_time = millis();
1048 
1049  ESP_LOGN(TAG, "Add to queue type: %s component %s", component->get_queue_type_string().c_str(),
1050  component->get_variable_name().c_str());
1051 
1052  std::string command = "get " + component->get_variable_name_to_send();
1053 
1054  if (this->send_command_(command)) {
1055  this->nextion_queue_.push_back(nextion_queue);
1056  }
1057 }
1058 
1068  if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
1069  return;
1070 
1071  // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
1072  nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
1073 
1074  nextion_queue->component = component;
1075  nextion_queue->queue_time = millis();
1076 
1077  this->waveform_queue_.push_back(nextion_queue);
1078  if (this->waveform_queue_.size() == 1)
1079  this->check_pending_waveform_();
1080 }
1081 
1083  if (this->waveform_queue_.empty())
1084  return;
1085 
1086  auto *nb = this->waveform_queue_.front();
1087  auto *component = nb->component;
1088  size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
1089  : 255; // ADDT command can only send 255
1090 
1091  std::string command = "addt " + to_string(component->get_component_id()) + "," +
1092  to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send);
1093  if (!this->send_command_(command)) {
1094  delete nb; // NOLINT(cppcoreguidelines-owning-memory)
1095  this->waveform_queue_.pop_front();
1096  }
1097 }
1098 
1099 void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
1100 
1101 ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20")
1102 void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is deprecated"); }
1103 
1104 } // namespace nextion
1105 } // namespace esphome
void goto_page(const char *page)
Show the page with a given name.
bool ignore_is_setup_
Sends commands ignoring of the Nextion has been setup.
Definition: nextion.h:779
void write_str(const char *str)
Definition: uart.h:27
void all_components_send_state_(bool force_update=false)
Definition: nextion.cpp:821
CallbackManager< void(uint8_t)> page_callback_
Definition: nextion.h:858
CallbackManager< void()> sleep_callback_
Definition: nextion.h:856
const char * name
Definition: stm32flash.h:78
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
void add_new_page_callback(std::function< void(uint8_t)> &&callback)
Add a callback to be notified when the nextion changes pages.
Definition: nextion.cpp:165
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
void add_wake_state_callback(std::function< void()> &&callback)
Add a callback to be notified of wake state changes.
Definition: nextion.cpp:157
bool send_command_printf(const char *format,...) __attribute__((format(printf
Manually send a raw formatted command to the display.
Definition: nextion.cpp:187
void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override
Definition: nextion.cpp:991
void add_addt_command_to_queue(NextionComponentBase *component) override
Add addt command to the queue.
Definition: nextion.cpp:1067
uint32_t startup_override_ms_
Definition: nextion.h:884
void add_to_get_queue(NextionComponentBase *component) override
Definition: nextion.cpp:1039
std::vector< NextionComponentBase * > touch_
Definition: nextion.h:850
optional< nextion_writer_t > writer_
Definition: nextion.h:860
uint16_t x
Definition: tt21100.cpp:17
void add_setup_state_callback(std::function< void()> &&callback)
Add a callback to be notified when the nextion completes its initialize setup.
Definition: nextion.cpp:161
bool send_command_(const std::string &command)
Manually send a raw command to the display and don&#39;t wait for an acknowledgement packet.
Definition: nextion.cpp:28
float get_setup_priority() const override
Definition: nextion.cpp:143
void setup() override
Definition: nextion.cpp:11
std::string serial_number_
Definition: nextion.h:865
bool has_value() const
Definition: optional.h:87
bool void add_no_result_to_queue_with_set_internal_(const std::string &variable_name, const std::string &variable_name_to_send, int state_value, bool is_sleep_safe=false)
Definition: nextion.cpp:1001
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint16_t y
Definition: tt21100.cpp:18
bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format,...) __attribute__((format(printf
Sends a formatted command to the nextion.
Definition: nextion.cpp:964
void add_sleep_state_callback(std::function< void()> &&callback)
Add a callback to be notified of sleep state changes.
Definition: nextion.cpp:153
virtual void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion)
std::string flash_size_
Definition: nextion.h:866
void set_nextion_sensor_state(int queue_type, const std::string &name, float state)
Set the nextion sensor state object.
Definition: nextion.cpp:769
CallbackManager< void()> setup_callback_
Definition: nextion.h:855
bool read_byte(uint8_t *data)
Definition: uart.h:29
void set_nextion_text_state(const std::string &name, const std::string &state)
Definition: nextion.cpp:810
void set_wait_for_ack(bool wait_for_ack)
void loop() override
Definition: nextion.cpp:228
std::deque< NextionQueue * > nextion_queue_
Definition: nextion.h:769
CallbackManager< void()> wake_callback_
Definition: nextion.h:857
Application App
Global storage of Application pointer - only one Application can exist.
void add_no_result_to_queue_(const std::string &variable_name)
Definition: nextion.cpp:908
std::deque< NextionQueue * > waveform_queue_
Definition: nextion.h:770
std::string command_data_
Definition: nextion.h:882
bool remove_from_q_(bool report_empty=true)
Definition: nextion.cpp:271
void set_backlight_brightness(float brightness)
Set the brightness of the backlight.
std::string device_model_
Definition: nextion.h:863
bool add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format,...) __attribute__((format(printf
Definition: nextion.cpp:938
void set_touch_sleep_timeout(uint16_t timeout)
Set the touch sleep timeout of the display.
std::vector< NextionComponentBase * > textsensortype_
Definition: nextion.h:853
ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20") void Nextion
Definition: nextion.cpp:1101
void set_wake_up_page(uint8_t page_id=255)
Sets which page Nextion loads when exiting sleep mode.
std::vector< NextionComponentBase * > sensortype_
Definition: nextion.h:852
void dump_config() override
Definition: nextion.cpp:122
std::string to_string(int value)
Definition: helpers.cpp:74
std::vector< NextionComponentBase * > switchtype_
Definition: nextion.h:851
void set_auto_wake_on_touch(bool auto_wake)
Sets if Nextion should auto-wake from sleep when touch press occurs.
virtual void set_state_from_int(int state_value, bool publish, bool send_to_nextion)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command)
Definition: nextion.cpp:929
void reset_(bool reset_nextion=true)
Definition: nextion.cpp:112
void update_components_by_prefix(const std::string &prefix)
Definition: nextion.cpp:841
void update() override
Definition: nextion.cpp:144
std::string firmware_version_
Definition: nextion.h:864
void set_variable_name(const std::string &variable_name, const std::string &variable_name_to_send="")
uint32_t touch_sleep_timeout_
Definition: nextion.h:786
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag)
Definition: nextion.cpp:860
std::function< void(Nextion &)> nextion_writer_t
Definition: nextion.h:30
std::vector< NextionComponentBase * > binarysensortype_
Definition: nextion.h:854
bool state
Definition: fan.h:34
void set_writer(const nextion_writer_t &writer)
Definition: nextion.cpp:1099
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26