1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 16:51:52 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
J. Nick Koston
5f80b6c5dd [modbus] Use stack buffer instead of heap vector in send()
Replace std::vector<uint8_t> with a fixed stack buffer in Modbus::send()
to eliminate a heap allocation on every Modbus frame transmission. The
maximum frame size is 264 bytes (bounded by the uint8_t payload_len
parameter), so a 270-byte stack buffer is sufficient.
2026-02-07 20:28:56 -06:00
6 changed files with 52 additions and 91 deletions

View File

@@ -219,39 +219,50 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
return;
}
std::vector<uint8_t> data;
data.push_back(address);
data.push_back(function_code);
static constexpr size_t ADDR_SIZE = 1;
static constexpr size_t FC_SIZE = 1;
static constexpr size_t START_ADDR_SIZE = 2;
static constexpr size_t NUM_ENTITIES_SIZE = 2;
static constexpr size_t BYTE_COUNT_SIZE = 1;
static constexpr size_t MAX_PAYLOAD_SIZE = std::numeric_limits<uint8_t>::max();
static constexpr size_t CRC_SIZE = 2;
static constexpr size_t MAX_FRAME_SIZE =
ADDR_SIZE + FC_SIZE + START_ADDR_SIZE + NUM_ENTITIES_SIZE + BYTE_COUNT_SIZE + MAX_PAYLOAD_SIZE + CRC_SIZE;
uint8_t data[MAX_FRAME_SIZE];
size_t pos = 0;
data[pos++] = address;
data[pos++] = function_code;
if (this->role == ModbusRole::CLIENT) {
data.push_back(start_address >> 8);
data.push_back(start_address >> 0);
data[pos++] = start_address >> 8;
data[pos++] = start_address >> 0;
if (function_code != ModbusFunctionCode::WRITE_SINGLE_COIL &&
function_code != ModbusFunctionCode::WRITE_SINGLE_REGISTER) {
data.push_back(number_of_entities >> 8);
data.push_back(number_of_entities >> 0);
data[pos++] = number_of_entities >> 8;
data[pos++] = number_of_entities >> 0;
}
}
if (payload != nullptr) {
if (this->role == ModbusRole::SERVER || function_code == ModbusFunctionCode::WRITE_MULTIPLE_COILS ||
function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) { // Write multiple
data.push_back(payload_len); // Byte count is required for write
data[pos++] = payload_len; // Byte count is required for write
} else {
payload_len = 2; // Write single register or coil
}
for (int i = 0; i < payload_len; i++) {
data.push_back(payload[i]);
data[pos++] = payload[i];
}
}
auto crc = crc16(data.data(), data.size());
data.push_back(crc >> 0);
data.push_back(crc >> 8);
auto crc = crc16(data, pos);
data[pos++] = crc >> 0;
data[pos++] = crc >> 8;
if (this->flow_control_pin_ != nullptr)
this->flow_control_pin_->digital_write(true);
this->write_array(data);
this->write_array(data, pos);
this->flush();
if (this->flow_control_pin_ != nullptr)
@@ -261,7 +272,7 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char hex_buf[format_hex_pretty_size(MODBUS_MAX_LOG_BYTES)];
#endif
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty_to(hex_buf, data.data(), data.size()));
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty_to(hex_buf, data, pos));
}
// Helper function for lambdas

View File

@@ -371,12 +371,7 @@ async def to_code(config):
if on_timer_tick := config.get(CONF_ON_TIMER_TICK):
await automation.build_automation(
var.get_timer_tick_trigger(),
[
(
cg.std_vector.template(Timer).operator("const").operator("ref"),
"timers",
)
],
[(cg.std_vector.template(Timer), "timers")],
on_timer_tick,
)
has_timers = True

View File

@@ -859,43 +859,35 @@ void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) {
}
void VoiceAssistant::on_timer_event(const api::VoiceAssistantTimerEventResponse &msg) {
// Find existing timer or add a new one
auto it = this->timers_.begin();
for (; it != this->timers_.end(); ++it) {
if (it->id == msg.timer_id)
break;
}
if (it == this->timers_.end()) {
this->timers_.push_back({});
it = this->timers_.end() - 1;
}
it->id = msg.timer_id;
it->name = msg.name;
it->total_seconds = msg.total_seconds;
it->seconds_left = msg.seconds_left;
it->is_active = msg.is_active;
Timer timer = {
.id = msg.timer_id,
.name = msg.name,
.total_seconds = msg.total_seconds,
.seconds_left = msg.seconds_left,
.is_active = msg.is_active,
};
this->timers_[timer.id] = timer;
char timer_buf[Timer::TO_STR_BUFFER_SIZE];
ESP_LOGD(TAG,
"Timer Event\n"
" Type: %" PRId32 "\n"
" %s",
msg.event_type, it->to_str(timer_buf));
msg.event_type, timer.to_str(timer_buf));
switch (msg.event_type) {
case api::enums::VOICE_ASSISTANT_TIMER_STARTED:
this->timer_started_trigger_.trigger(*it);
this->timer_started_trigger_.trigger(timer);
break;
case api::enums::VOICE_ASSISTANT_TIMER_UPDATED:
this->timer_updated_trigger_.trigger(*it);
this->timer_updated_trigger_.trigger(timer);
break;
case api::enums::VOICE_ASSISTANT_TIMER_CANCELLED:
this->timer_cancelled_trigger_.trigger(*it);
this->timers_.erase(it);
this->timer_cancelled_trigger_.trigger(timer);
this->timers_.erase(timer.id);
break;
case api::enums::VOICE_ASSISTANT_TIMER_FINISHED:
this->timer_finished_trigger_.trigger(*it);
this->timers_.erase(it);
this->timer_finished_trigger_.trigger(timer);
this->timers_.erase(timer.id);
break;
}
@@ -909,12 +901,16 @@ void VoiceAssistant::on_timer_event(const api::VoiceAssistantTimerEventResponse
}
void VoiceAssistant::timer_tick_() {
for (auto &timer : this->timers_) {
std::vector<Timer> res;
res.reserve(this->timers_.size());
for (auto &pair : this->timers_) {
auto &timer = pair.second;
if (timer.is_active && timer.seconds_left > 0) {
timer.seconds_left--;
}
res.push_back(timer);
}
this->timer_tick_trigger_.trigger(this->timers_);
this->timer_tick_trigger_.trigger(res);
}
void VoiceAssistant::on_announce(const api::VoiceAssistantAnnounceRequest &msg) {

View File

@@ -24,6 +24,7 @@
#include "esphome/components/socket/socket.h"
#include <span>
#include <unordered_map>
#include <vector>
namespace esphome {
@@ -225,9 +226,9 @@ class VoiceAssistant : public Component {
Trigger<Timer> *get_timer_updated_trigger() { return &this->timer_updated_trigger_; }
Trigger<Timer> *get_timer_cancelled_trigger() { return &this->timer_cancelled_trigger_; }
Trigger<Timer> *get_timer_finished_trigger() { return &this->timer_finished_trigger_; }
Trigger<const std::vector<Timer> &> *get_timer_tick_trigger() { return &this->timer_tick_trigger_; }
Trigger<std::vector<Timer>> *get_timer_tick_trigger() { return &this->timer_tick_trigger_; }
void set_has_timers(bool has_timers) { this->has_timers_ = has_timers; }
const std::vector<Timer> &get_timers() const { return this->timers_; }
const std::unordered_map<std::string, Timer> &get_timers() const { return this->timers_; }
protected:
bool allocate_buffers_();
@@ -266,13 +267,13 @@ class VoiceAssistant : public Component {
api::APIConnection *api_client_{nullptr};
std::vector<Timer> timers_;
std::unordered_map<std::string, Timer> timers_;
void timer_tick_();
Trigger<Timer> timer_started_trigger_;
Trigger<Timer> timer_finished_trigger_;
Trigger<Timer> timer_updated_trigger_;
Trigger<Timer> timer_cancelled_trigger_;
Trigger<const std::vector<Timer> &> timer_tick_trigger_;
Trigger<std::vector<Timer>> timer_tick_trigger_;
bool has_timers_{false};
bool timer_tick_running_{false};

View File

@@ -68,24 +68,3 @@ voice_assistant:
- logger.log:
format: "Voice assistant error - code %s, message: %s"
args: [code.c_str(), message.c_str()]
on_timer_started:
- logger.log:
format: "Timer started: %s"
args: [timer.id.c_str()]
on_timer_updated:
- logger.log:
format: "Timer updated: %s"
args: [timer.id.c_str()]
on_timer_cancelled:
- logger.log:
format: "Timer cancelled: %s"
args: [timer.id.c_str()]
on_timer_finished:
- logger.log:
format: "Timer finished: %s"
args: [timer.id.c_str()]
on_timer_tick:
- lambda: |-
for (auto &timer : timers) {
ESP_LOGD("timer", "Timer %s: %" PRIu32 "s left", timer.name.c_str(), timer.seconds_left);
}

View File

@@ -58,24 +58,3 @@ voice_assistant:
- logger.log:
format: "Voice assistant error - code %s, message: %s"
args: [code.c_str(), message.c_str()]
on_timer_started:
- logger.log:
format: "Timer started: %s"
args: [timer.id.c_str()]
on_timer_updated:
- logger.log:
format: "Timer updated: %s"
args: [timer.id.c_str()]
on_timer_cancelled:
- logger.log:
format: "Timer cancelled: %s"
args: [timer.id.c_str()]
on_timer_finished:
- logger.log:
format: "Timer finished: %s"
args: [timer.id.c_str()]
on_timer_tick:
- lambda: |-
for (auto &timer : timers) {
ESP_LOGD("timer", "Timer %s: %" PRIu32 "s left", timer.name.c_str(), timer.seconds_left);
}