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