mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[nextion] Add configurable limit for commands processed per loop (#8972)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
		| @@ -14,6 +14,7 @@ CONF_COMPONENT_NAME = "component_name" | |||||||
| CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" | CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" | ||||||
| CONF_FONT_ID = "font_id" | CONF_FONT_ID = "font_id" | ||||||
| CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" | CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" | ||||||
|  | CONF_MAX_COMMANDS_PER_LOOP = "max_commands_per_loop" | ||||||
| CONF_MAX_QUEUE_SIZE = "max_queue_size" | CONF_MAX_QUEUE_SIZE = "max_queue_size" | ||||||
| CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" | CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" | ||||||
| CONF_ON_PAGE = "on_page" | CONF_ON_PAGE = "on_page" | ||||||
|   | |||||||
| @@ -16,11 +16,12 @@ from .base_component import ( | |||||||
|     CONF_AUTO_WAKE_ON_TOUCH, |     CONF_AUTO_WAKE_ON_TOUCH, | ||||||
|     CONF_COMMAND_SPACING, |     CONF_COMMAND_SPACING, | ||||||
|     CONF_EXIT_REPARSE_ON_START, |     CONF_EXIT_REPARSE_ON_START, | ||||||
|  |     CONF_MAX_COMMANDS_PER_LOOP, | ||||||
|     CONF_MAX_QUEUE_SIZE, |     CONF_MAX_QUEUE_SIZE, | ||||||
|     CONF_ON_BUFFER_OVERFLOW, |     CONF_ON_BUFFER_OVERFLOW, | ||||||
|  |     CONF_ON_PAGE, | ||||||
|     CONF_ON_SETUP, |     CONF_ON_SETUP, | ||||||
|     CONF_ON_SLEEP, |     CONF_ON_SLEEP, | ||||||
|     CONF_ON_PAGE, |  | ||||||
|     CONF_ON_WAKE, |     CONF_ON_WAKE, | ||||||
|     CONF_SKIP_CONNECTION_HANDSHAKE, |     CONF_SKIP_CONNECTION_HANDSHAKE, | ||||||
|     CONF_START_UP_PAGE, |     CONF_START_UP_PAGE, | ||||||
| @@ -50,8 +51,27 @@ CONFIG_SCHEMA = ( | |||||||
|     display.BASIC_DISPLAY_SCHEMA.extend( |     display.BASIC_DISPLAY_SCHEMA.extend( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.declare_id(Nextion), |             cv.GenerateID(): cv.declare_id(Nextion), | ||||||
|             cv.Optional(CONF_TFT_URL): cv.url, |             cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, | ||||||
|             cv.Optional(CONF_BRIGHTNESS): cv.percentage, |             cv.Optional(CONF_BRIGHTNESS): cv.percentage, | ||||||
|  |             cv.Optional(CONF_COMMAND_SPACING): cv.All( | ||||||
|  |                 cv.positive_time_period_milliseconds, | ||||||
|  |                 cv.Range(max=TimePeriod(milliseconds=255)), | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, | ||||||
|  |             cv.Optional(CONF_MAX_COMMANDS_PER_LOOP): cv.uint16_t, | ||||||
|  |             cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int, | ||||||
|  |             cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                         BufferOverflowTrigger | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_ON_PAGE): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|             cv.Optional(CONF_ON_SETUP): automation.validate_automation( |             cv.Optional(CONF_ON_SETUP): automation.validate_automation( | ||||||
|                 { |                 { | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), | ||||||
| @@ -62,39 +82,21 @@ CONFIG_SCHEMA = ( | |||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger), |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger), | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|             cv.Optional(CONF_ON_WAKE): automation.validate_automation( |  | ||||||
|                 { |  | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), |  | ||||||
|                 } |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_ON_PAGE): automation.validate_automation( |  | ||||||
|                 { |  | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), |  | ||||||
|                 } |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_ON_TOUCH): automation.validate_automation( |             cv.Optional(CONF_ON_TOUCH): automation.validate_automation( | ||||||
|                 { |                 { | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger), |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger), | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|             cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( |             cv.Optional(CONF_ON_WAKE): automation.validate_automation( | ||||||
|                 { |                 { | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), | ||||||
|                         BufferOverflowTrigger |  | ||||||
|                     ), |  | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|  |             cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, | ||||||
|  |             cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, | ||||||
|  |             cv.Optional(CONF_TFT_URL): cv.url, | ||||||
|             cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), |             cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), | ||||||
|             cv.Optional(CONF_WAKE_UP_PAGE): cv.uint8_t, |             cv.Optional(CONF_WAKE_UP_PAGE): cv.uint8_t, | ||||||
|             cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, |  | ||||||
|             cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, |  | ||||||
|             cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, |  | ||||||
|             cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, |  | ||||||
|             cv.Optional(CONF_COMMAND_SPACING): cv.All( |  | ||||||
|                 cv.positive_time_period_milliseconds, |  | ||||||
|                 cv.Range(max=TimePeriod(milliseconds=255)), |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int, |  | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|     .extend(cv.polling_component_schema("5s")) |     .extend(cv.polling_component_schema("5s")) | ||||||
| @@ -173,6 +175,10 @@ async def to_code(config): | |||||||
|  |  | ||||||
|     cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) |     cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) | ||||||
|  |  | ||||||
|  |     if max_commands_per_loop := config.get(CONF_MAX_COMMANDS_PER_LOOP): | ||||||
|  |         cg.add_define("USE_NEXTION_MAX_COMMANDS_PER_LOOP") | ||||||
|  |         cg.add(var.set_max_commands_per_loop(max_commands_per_loop)) | ||||||
|  |  | ||||||
|     await display.register_display(var, config) |     await display.register_display(var, config) | ||||||
|  |  | ||||||
|     for conf in config.get(CONF_ON_SETUP, []): |     for conf in config.get(CONF_ON_SETUP, []): | ||||||
|   | |||||||
| @@ -162,21 +162,24 @@ void Nextion::dump_config() { | |||||||
|                 "  Wake On Touch:  %s\n" |                 "  Wake On Touch:  %s\n" | ||||||
|                 "  Exit reparse:   %s", |                 "  Exit reparse:   %s", | ||||||
|                 YESNO(this->auto_wake_on_touch_), YESNO(this->exit_reparse_on_start_)); |                 YESNO(this->auto_wake_on_touch_), YESNO(this->exit_reparse_on_start_)); | ||||||
|  | #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Max commands per loop: %u", this->max_commands_per_loop_); | ||||||
|  | #endif  // USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |  | ||||||
|   if (this->touch_sleep_timeout_ != 0) { |   if (this->touch_sleep_timeout_ != 0) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Touch Timeout:  %" PRIu32, this->touch_sleep_timeout_); |     ESP_LOGCONFIG(TAG, "  Touch Timeout:  %" PRIu32, this->touch_sleep_timeout_); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->wake_up_page_ != -1) { |   if (this->wake_up_page_ != -1) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Wake Up Page:   %" PRId16, this->wake_up_page_); |     ESP_LOGCONFIG(TAG, "  Wake Up Page:   %d", this->wake_up_page_); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->start_up_page_ != -1) { |   if (this->start_up_page_ != -1) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Start Up Page:  %" PRId16, this->start_up_page_); |     ESP_LOGCONFIG(TAG, "  Start Up Page:  %d", this->start_up_page_); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #ifdef USE_NEXTION_COMMAND_SPACING | #ifdef USE_NEXTION_COMMAND_SPACING | ||||||
|   ESP_LOGCONFIG(TAG, "  Cmd spacing:      %" PRIu8 "ms", this->command_pacer_.get_spacing()); |   ESP_LOGCONFIG(TAG, "  Cmd spacing:      %u ms", this->command_pacer_.get_spacing()); | ||||||
| #endif  // USE_NEXTION_COMMAND_SPACING | #endif  // USE_NEXTION_COMMAND_SPACING | ||||||
|  |  | ||||||
| #ifdef USE_NEXTION_MAX_QUEUE_SIZE | #ifdef USE_NEXTION_MAX_QUEUE_SIZE | ||||||
| @@ -370,6 +373,10 @@ void Nextion::process_nextion_commands_() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |   size_t commands_processed = 0; | ||||||
|  | #endif  // USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |  | ||||||
| #ifdef USE_NEXTION_COMMAND_SPACING | #ifdef USE_NEXTION_COMMAND_SPACING | ||||||
|   if (!this->command_pacer_.can_send()) { |   if (!this->command_pacer_.can_send()) { | ||||||
|     return;  // Will try again in next loop iteration |     return;  // Will try again in next loop iteration | ||||||
| @@ -384,6 +391,12 @@ void Nextion::process_nextion_commands_() { | |||||||
|   this->print_queue_members_(); |   this->print_queue_members_(); | ||||||
| #endif | #endif | ||||||
|   while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) { |   while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) { | ||||||
|  | #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |     if (++commands_processed > this->max_commands_per_loop_) { | ||||||
|  |       ESP_LOGW(TAG, "Command processing limit exceeded"); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  | #endif  // USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|     ESP_LOGN(TAG, "queue size: %zu", this->nextion_queue_.size()); |     ESP_LOGN(TAG, "queue size: %zu", this->nextion_queue_.size()); | ||||||
|     while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() && |     while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() && | ||||||
|            static_cast<uint8_t>(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) { |            static_cast<uint8_t>(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) { | ||||||
| @@ -798,8 +811,6 @@ void Nextion::process_nextion_commands_() { | |||||||
|  |  | ||||||
|     // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1); |     // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1); | ||||||
|     this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); |     this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); | ||||||
|     // App.feed_wdt(); Remove before master merge |  | ||||||
|     this->process_serial_(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint32_t ms = millis(); |   uint32_t ms = millis(); | ||||||
| @@ -840,7 +851,7 @@ void Nextion::process_nextion_commands_() { | |||||||
|   ESP_LOGN(TAG, "Loop end"); |   ESP_LOGN(TAG, "Loop end"); | ||||||
|   // App.feed_wdt(); Remove before master merge |   // App.feed_wdt(); Remove before master merge | ||||||
|   this->process_serial_(); |   this->process_serial_(); | ||||||
| }  // namespace nextion | }  // Nextion::process_nextion_commands_() | ||||||
|  |  | ||||||
| void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) { | void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) { | ||||||
|   this->set_nextion_sensor_state(static_cast<NextionQueueType>(queue_type), name, state); |   this->set_nextion_sensor_state(static_cast<NextionQueueType>(queue_type), name, state); | ||||||
|   | |||||||
| @@ -75,6 +75,22 @@ class NextionCommandPacer { | |||||||
|  |  | ||||||
| class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice { | class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice { | ||||||
|  public: |  public: | ||||||
|  | #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |   /** | ||||||
|  |    * @brief Set the maximum number of commands to process in each loop iteration | ||||||
|  |    * @param value Maximum number of commands (default: 20) | ||||||
|  |    * | ||||||
|  |    * Limiting the number of commands per loop helps prevent stack overflows | ||||||
|  |    * when a large number of commands are queued at once, especially during boot. | ||||||
|  |    */ | ||||||
|  |   inline void set_max_commands_per_loop(uint16_t value) { this->max_commands_per_loop_ = value; } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * @brief Get the current maximum number of commands allowed per loop iteration | ||||||
|  |    * @return Configured command limit per loop | ||||||
|  |    */ | ||||||
|  |   inline uint16_t get_max_commands_per_loop() const { return this->max_commands_per_loop_; } | ||||||
|  | #endif  // USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
| #ifdef USE_NEXTION_MAX_QUEUE_SIZE | #ifdef USE_NEXTION_MAX_QUEUE_SIZE | ||||||
|   /** |   /** | ||||||
|    * @brief Set the maximum allowed queue size |    * @brief Set the maximum allowed queue size | ||||||
| @@ -1287,6 +1303,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe | |||||||
|   bool is_connected() { return this->is_connected_; } |   bool is_connected() { return this->is_connected_; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|  | #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
|  |   uint16_t max_commands_per_loop_{1000}; | ||||||
|  | #endif  // USE_NEXTION_MAX_COMMANDS_PER_LOOP | ||||||
| #ifdef USE_NEXTION_MAX_QUEUE_SIZE | #ifdef USE_NEXTION_MAX_QUEUE_SIZE | ||||||
|   size_t max_queue_size_{0}; |   size_t max_queue_size_{0}; | ||||||
| #endif  // USE_NEXTION_MAX_QUEUE_SIZE | #endif  // USE_NEXTION_MAX_QUEUE_SIZE | ||||||
|   | |||||||
| @@ -281,6 +281,7 @@ display: | |||||||
|     id: main_lcd |     id: main_lcd | ||||||
|     update_interval: 5s |     update_interval: 5s | ||||||
|     command_spacing: 5ms |     command_spacing: 5ms | ||||||
|  |     max_commands_per_loop: 20 | ||||||
|     max_queue_size: 50 |     max_queue_size: 50 | ||||||
|     on_sleep: |     on_sleep: | ||||||
|       then: |       then: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user