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