[nextion] Gate waveform code behind USE_NEXTION_WAVEFORM, use StaticRingBuffer (#15273)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Edward Firmo
2026-04-02 20:17:20 +00:00
committed by GitHub
co-authored by pre-commit-ci-lite[bot] J. Nick Koston J. Nick Koston
parent 63710a4cb7
commit 1e72f0ee5a
9 changed files with 125 additions and 74 deletions
+32 -21
View File
@@ -150,10 +150,12 @@ void Nextion::reset_(bool reset_nextion) {
delete entry; // NOLINT(cppcoreguidelines-owning-memory)
}
this->nextion_queue_.clear();
#ifdef USE_NEXTION_WAVEFORM
for (auto *entry : this->waveform_queue_) {
delete entry; // NOLINT(cppcoreguidelines-owning-memory)
}
this->waveform_queue_.clear();
#endif // USE_NEXTION_WAVEFORM
}
void Nextion::dump_config() {
@@ -496,20 +498,21 @@ void Nextion::process_nextion_commands_() {
ESP_LOGW(TAG, "Invalid baud rate");
break;
case 0x12: // invalid Waveform ID or Channel # was used
#ifdef USE_NEXTION_WAVEFORM
if (this->waveform_queue_.empty()) {
ESP_LOGW(TAG, "Waveform ID/ch used but no sensor queued");
} else {
auto &nb = this->waveform_queue_.front();
NextionComponentBase *component = nb->component;
ESP_LOGW(TAG, "Invalid waveform ID %d/ch %d", component->get_component_id(),
component->get_wave_channel_id());
ESP_LOGN(TAG, "Remove waveform ID %d/ch %d", component->get_component_id(), component->get_wave_channel_id());
delete nb; // NOLINT(cppcoreguidelines-owning-memory)
this->waveform_queue_.pop_front();
this->waveform_queue_.pop();
}
#else // USE_NEXTION_WAVEFORM
ESP_LOGW(TAG, "Waveform ID/ch error but waveform not enabled");
#endif // USE_NEXTION_WAVEFORM
break;
case 0x1A: // variable name invalid
ESP_LOGW(TAG, "Invalid variable name");
@@ -812,29 +815,30 @@ void Nextion::process_nextion_commands_() {
}
case 0xFD: { // data transparent transmit finished
ESP_LOGVV(TAG, "Data transmit done");
#ifdef USE_NEXTION_WAVEFORM
this->check_pending_waveform_();
#endif // USE_NEXTION_WAVEFORM
break;
}
case 0xFE: { // data transparent transmit ready
ESP_LOGVV(TAG, "Ready for transmit");
#ifdef USE_NEXTION_WAVEFORM
if (this->waveform_queue_.empty()) {
ESP_LOGE(TAG, "No waveforms queued");
break;
}
auto &nb = this->waveform_queue_.front();
auto *component = nb->component;
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
: 255; // ADDT command can only send 255
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size() : 255;
this->write_array(component->get_wave_buffer().data(), static_cast<int>(buffer_to_send));
ESP_LOGN(TAG, "Send waveform: component id %d, waveform id %d, size %zu", component->get_component_id(),
component->get_wave_channel_id(), buffer_to_send);
component->clear_wave_buffer(buffer_to_send);
delete nb; // NOLINT(cppcoreguidelines-owning-memory)
this->waveform_queue_.pop_front();
this->waveform_queue_.pop();
#else // USE_NEXTION_WAVEFORM
ESP_LOGW(TAG, "Waveform transmit ready but waveform not enabled");
#endif // USE_NEXTION_WAVEFORM
break;
}
default:
@@ -934,8 +938,13 @@ void Nextion::all_components_send_state_(bool force_update) {
binarysensortype->send_state_to_nextion();
}
for (auto *sensortype : this->sensortype_) {
if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_channel_id() == 0)
#ifdef USE_NEXTION_WAVEFORM
if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_channel_id() == UINT8_MAX) {
#else // USE_NEXTION_WAVEFORM
if (force_update || sensortype->get_needs_to_send_update()) {
#endif // USE_NEXTION_WAVEFORM
sensortype->send_state_to_nextion();
}
}
for (auto *switchtype : this->switchtype_) {
if (force_update || switchtype->get_needs_to_send_update())
@@ -1239,13 +1248,11 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) {
}
}
#ifdef USE_NEXTION_WAVEFORM
/**
* @brief Add addt command to the queue
* @brief Add addt command to the waveform queue.
*
* @param component_id The waveform component id
* @param wave_chan_id The waveform channel to send it to
* @param buffer_to_send The buffer size
* @param buffer_size The buffer data
* @param component Pointer to the Nextion component with waveform data to send.
*/
void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
if ((!this->is_setup() && !this->connection_state_.ignore_is_setup_) || this->is_sleeping())
@@ -1262,7 +1269,11 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
nextion_queue->component = component;
nextion_queue->queue_time = App.get_loop_component_start_time();
this->waveform_queue_.push_back(nextion_queue);
if (!this->waveform_queue_.push(nextion_queue)) {
ESP_LOGW(TAG, "Waveform queue full, drop");
delete nextion_queue; // NOLINT(cppcoreguidelines-owning-memory)
return;
}
if (this->waveform_queue_.size() == 1)
this->check_pending_waveform_();
}
@@ -1273,17 +1284,17 @@ void Nextion::check_pending_waveform_() {
auto *nb = this->waveform_queue_.front();
auto *component = nb->component;
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
: 255; // ADDT command can only send 255
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size() : 255;
char command[24]; // "addt " + uint8 + "," + uint8 + "," + uint8 + null = max 17 chars
buf_append_printf(command, sizeof(command), 0, "addt %u,%u,%zu", component->get_component_id(),
component->get_wave_channel_id(), buffer_to_send);
if (!this->send_command_(command)) {
delete nb; // NOLINT(cppcoreguidelines-owning-memory)
this->waveform_queue_.pop_front();
this->waveform_queue_.pop();
}
}
#endif // USE_NEXTION_WAVEFORM
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
+15 -1
View File
@@ -9,6 +9,10 @@
#include "esphome/core/defines.h"
#include "esphome/core/time.h"
#ifdef USE_NEXTION_WAVEFORM
#include "esphome/core/helpers.h"
#endif // USE_NEXTION_WAVEFORM
#include "nextion_base.h"
#include "nextion_component.h"
@@ -602,6 +606,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
*/
void disable_component_touch(const char *component);
#ifdef USE_NEXTION_WAVEFORM
/**
* Add waveform data to a waveform component
* @param component_id The integer component id.
@@ -611,6 +616,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
void add_waveform_data(uint8_t component_id, uint8_t channel_number, uint8_t value);
void open_waveform_channel(uint8_t component_id, uint8_t channel_number, uint8_t value);
#endif // USE_NEXTION_WAVEFORM
/**
* Display a picture at coordinates.
@@ -1205,7 +1211,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
void add_to_get_queue(NextionComponentBase *component) override;
#ifdef USE_NEXTION_WAVEFORM
void add_addt_command_to_queue(NextionComponentBase *component) override;
#endif // USE_NEXTION_WAVEFORM
void update_components_by_prefix(const std::string &prefix);
@@ -1391,7 +1399,11 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
#endif // USE_NEXTION_COMMAND_SPACING
std::list<NextionQueue *> nextion_queue_;
std::list<NextionQueue *> waveform_queue_;
#ifdef USE_NEXTION_WAVEFORM
/// Fixed-size ring buffer for waveform queue. Nextion supports at most 4 waveform
/// channels (IDs 0-3), so 4 entries is both the correct maximum and a safe default.
StaticRingBuffer<NextionQueue *, 4> waveform_queue_;
#endif // USE_NEXTION_WAVEFORM
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
void all_components_send_state_(bool force_update = false);
uint32_t comok_sent_ = 0;
@@ -1460,7 +1472,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
const std::string &variable_name_to_send,
const std::string &state_value, bool is_sleep_safe = false);
#ifdef USE_NEXTION_WAVEFORM
void check_pending_waveform_();
#endif // USE_NEXTION_WAVEFORM
#ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ESP8266
@@ -33,7 +33,9 @@ class NextionBase {
const std::string &variable_name_to_send,
const std::string &state_value) = 0;
#ifdef USE_NEXTION_WAVEFORM
virtual void add_addt_command_to_queue(NextionComponentBase *component) = 0;
#endif // USE_NEXTION_WAVEFORM
virtual void add_to_get_queue(NextionComponentBase *component) = 0;
@@ -217,6 +217,7 @@ void Nextion::set_component_value(const char *component, int32_t value) {
this->add_no_result_to_queue_with_printf_(".val", "%s.val=%" PRId32, component, value);
}
#ifdef USE_NEXTION_WAVEFORM
void Nextion::add_waveform_data(uint8_t component_id, uint8_t channel_number, uint8_t value) {
this->add_no_result_to_queue_with_printf_("add", "add %" PRIu8 ",%" PRIu8 ",%" PRIu8, component_id, channel_number,
value);
@@ -226,6 +227,7 @@ void Nextion::open_waveform_channel(uint8_t component_id, uint8_t channel_number
this->add_no_result_to_queue_with_printf_("addt", "addt %" PRIu8 ",%" PRIu8 ",%" PRIu8, component_id, channel_number,
value);
}
#endif // USE_NEXTION_WAVEFORM
void Nextion::set_component_coordinates(const char *component, uint16_t x, uint16_t y) {
this->add_no_result_to_queue_with_printf_(".xcen", "%s.xcen=%" PRIu16, component, x);
@@ -64,6 +64,7 @@ class NextionComponentBase {
uint8_t get_component_id() const { return this->component_id_; }
void set_component_id(uint8_t component_id) { this->component_id_ = component_id; }
#ifdef USE_NEXTION_WAVEFORM
uint8_t get_wave_channel_id() const { return this->wave_chan_id_; }
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
@@ -76,6 +77,7 @@ class NextionComponentBase {
this->wave_buffer_.erase(this->wave_buffer_.begin(), this->wave_buffer_.begin() + buffer_sent);
}
}
#endif // USE_NEXTION_WAVEFORM
const std::string &get_variable_name() const { return this->variable_name_; }
const std::string &get_variable_name_to_send() const { return this->variable_name_to_send_; }
@@ -85,19 +87,23 @@ class NextionComponentBase {
virtual void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion){};
virtual void send_state_to_nextion(){};
bool get_needs_to_send_update() const { return this->needs_to_send_update_; }
#ifdef USE_NEXTION_WAVEFORM
// Remove before 2026.10.0
ESPDEPRECATED("Use get_wave_channel_id() instead. Will be removed in 2026.10.0", "2026.4.0")
uint8_t get_wave_chan_id() const { return this->get_wave_channel_id(); }
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
#endif // USE_NEXTION_WAVEFORM
protected:
std::string variable_name_;
std::string variable_name_to_send_;
uint8_t component_id_ = 0;
#ifdef USE_NEXTION_WAVEFORM
uint8_t wave_chan_id_ = UINT8_MAX;
std::vector<uint8_t> wave_buffer_;
int wave_max_length_ = 255;
#endif // USE_NEXTION_WAVEFORM
bool needs_to_send_update_;
};
@@ -85,16 +85,16 @@ async def to_code(config):
cg.add(var.set_component_id(config[CONF_COMPONENT_ID]))
if CONF_WAVE_CHANNEL_ID in config:
cg.add_define("USE_NEXTION_WAVEFORM")
cg.add(var.set_wave_channel_id(config[CONF_WAVE_CHANNEL_ID]))
if CONF_WAVEFORM_SEND_LAST_VALUE in config:
cg.add(var.set_waveform_send_last_value(config[CONF_WAVEFORM_SEND_LAST_VALUE]))
if CONF_WAVE_MAX_VALUE in config:
cg.add(var.set_wave_max_value(config[CONF_WAVE_MAX_VALUE]))
if CONF_WAVE_MAX_LENGTH in config:
cg.add(var.set_wave_max_length(config[CONF_WAVE_MAX_LENGTH]))
if CONF_WAVEFORM_SEND_LAST_VALUE in config:
cg.add(
var.set_waveform_send_last_value(config[CONF_WAVEFORM_SEND_LAST_VALUE])
)
if CONF_WAVE_MAX_VALUE in config:
cg.add(var.set_wave_max_value(config[CONF_WAVE_MAX_VALUE]))
if CONF_WAVE_MAX_LENGTH in config:
cg.add(var.set_wave_max_length(config[CONF_WAVE_MAX_LENGTH]))
@automation.register_action(
@@ -10,37 +10,44 @@ void NextionSensor::process_sensor(const std::string &variable_name, int state)
if (!this->nextion_->is_setup())
return;
if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name) {
#ifdef USE_NEXTION_WAVEFORM
if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name)
#else // USE_NEXTION_WAVEFORM
if (this->variable_name_ == variable_name)
#endif // USE_NEXTION_WAVEFORM
{
this->publish_state(state);
ESP_LOGD(TAG, "Sensor: %s=%d", variable_name.c_str(), state);
}
}
#ifdef USE_NEXTION_WAVEFORM
void NextionSensor::add_to_wave_buffer(float state) {
this->needs_to_send_update_ = true;
int wave_state = (int) ((state / (float) this->wave_maxvalue_) * 100);
wave_buffer_.push_back(wave_state);
this->wave_buffer_.push_back(wave_state);
if (this->wave_buffer_.size() > (size_t) this->wave_max_length_) {
this->wave_buffer_.erase(this->wave_buffer_.begin());
}
}
#endif // USE_NEXTION_WAVEFORM
void NextionSensor::update() {
if (!this->nextion_->is_setup() || this->nextion_->is_updating())
return;
#ifdef USE_NEXTION_WAVEFORM
if (this->wave_chan_id_ == UINT8_MAX) {
this->nextion_->add_to_get_queue(this);
} else {
if (this->send_last_value_) {
this->add_to_wave_buffer(this->last_value_);
}
this->wave_update_();
}
#else // USE_NEXTION_WAVEFORM
this->nextion_->add_to_get_queue(this);
#endif // USE_NEXTION_WAVEFORM
}
void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
@@ -50,61 +57,60 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
if (std::isnan(state))
return;
if (this->wave_chan_id_ == UINT8_MAX) {
if (send_to_nextion) {
if (this->nextion_->is_sleeping() || !this->component_flags_.visible) {
this->needs_to_send_update_ = true;
} else {
this->needs_to_send_update_ = false;
if (this->precision_ > 0) {
double to_multiply = pow(10, this->precision_);
int state_value = (int) (state * to_multiply);
this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value);
} else {
this->nextion_->add_no_result_to_queue_with_set(this, (int) state);
}
}
}
} else {
#ifdef USE_NEXTION_WAVEFORM
if (this->wave_chan_id_ != UINT8_MAX) {
// Waveform sensor — buffer the value, don't send directly.
if (this->send_last_value_) {
this->last_value_ = state; // Update will handle setting the buffer
} else {
this->add_to_wave_buffer(state);
}
this->update_component_settings();
return;
}
#endif // USE_NEXTION_WAVEFORM
if (send_to_nextion) {
if (this->nextion_->is_sleeping() || !this->component_flags_.visible) {
this->needs_to_send_update_ = true;
} else {
this->needs_to_send_update_ = false;
if (this->precision_ > 0) {
double to_multiply = pow(10, this->precision_);
int state_value = (int) (state * to_multiply);
this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value);
} else {
this->nextion_->add_no_result_to_queue_with_set(this, (int) state);
}
}
}
float published_state = state;
if (this->wave_chan_id_ == UINT8_MAX) {
if (publish) {
if (this->precision_ > 0) {
double to_multiply = pow(10, -this->precision_);
published_state = (float) (state * to_multiply);
}
this->publish_state(published_state);
if (publish) {
if (this->precision_ > 0) {
double to_multiply = pow(10, -this->precision_);
published_state = (float) (state * to_multiply);
}
this->publish_state(published_state);
}
this->update_component_settings();
ESP_LOGN(TAG, "Write: %s=%lf", this->variable_name_.c_str(), published_state);
}
#ifdef USE_NEXTION_WAVEFORM
void NextionSensor::wave_update_() {
if (this->nextion_->is_sleeping() || this->wave_buffer_.empty()) {
return;
}
#ifdef NEXTION_PROTOCOL_LOG
size_t buffer_to_send =
this->wave_buffer_.size() < 255 ? this->wave_buffer_.size() : 255; // ADDT command can only send 255
ESP_LOGN(TAG, "Wave update: %zu/%zu vals to comp %d ch %d", buffer_to_send, this->wave_buffer_.size(),
this->component_id_, this->wave_chan_id_);
#endif
#endif // NEXTION_PROTOCOL_LOG
this->nextion_->add_addt_command_to_queue(this);
}
#endif // USE_NEXTION_WAVEFORM
} // namespace esphome::nextion
@@ -15,22 +15,30 @@ class NextionSensor : public NextionComponent, public sensor::Sensor, public Pol
void update_component() override { this->update(); }
void update() override;
void add_to_wave_buffer(float state);
void set_precision(uint8_t precision) { this->precision_ = precision; }
void set_component_id(uint8_t component_id) { this->component_id_ = component_id; }
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
void set_wave_max_value(uint32_t wave_maxvalue) { this->wave_maxvalue_ = wave_maxvalue; }
void process_sensor(const std::string &variable_name, int state) override;
void set_state(float state) override { this->set_state(state, true, true); }
void set_state(float state, bool publish) override { this->set_state(state, publish, true); }
void set_state(float state, bool publish, bool send_to_nextion) override;
NextionQueueType get_queue_type() const override {
#ifdef USE_NEXTION_WAVEFORM
return this->wave_chan_id_ == UINT8_MAX ? NextionQueueType::SENSOR : NextionQueueType::WAVEFORM_SENSOR;
#else // USE_NEXTION_WAVEFORM
return NextionQueueType::SENSOR;
#endif // USE_NEXTION_WAVEFORM
}
#ifdef USE_NEXTION_WAVEFORM
void add_to_wave_buffer(float state);
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
void set_wave_max_value(uint32_t wave_maxvalue) { this->wave_maxvalue_ = wave_maxvalue; }
void set_waveform_send_last_value(bool send_last_value) { this->send_last_value_ = send_last_value; }
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
NextionQueueType get_queue_type() const override {
return this->wave_chan_id_ == UINT8_MAX ? NextionQueueType::SENSOR : NextionQueueType::WAVEFORM_SENSOR;
}
#endif // USE_NEXTION_WAVEFORM
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
this->set_state(state_value, publish, send_to_nextion);
@@ -38,10 +46,11 @@ class NextionSensor : public NextionComponent, public sensor::Sensor, public Pol
protected:
uint8_t precision_ = 0;
#ifdef USE_NEXTION_WAVEFORM
uint32_t wave_maxvalue_ = 255;
float last_value_ = 0;
bool send_last_value_ = true;
void wave_update_();
#endif // USE_NEXTION_WAVEFORM
};
} // namespace esphome::nextion
+1
View File
@@ -123,6 +123,7 @@
#define USE_NEXTION_MAX_COMMANDS_PER_LOOP
#define USE_NEXTION_MAX_QUEUE_SIZE
#define USE_NEXTION_TFT_UPLOAD
#define USE_NEXTION_WAVEFORM
#define USE_NUMBER
#define USE_OUTPUT
#define USE_POWER_SUPPLY