mirror of
https://github.com/esphome/esphome.git
synced 2025-07-31 15:37:49 +00:00
Reduce modbus heap alloc
This commit is contained in:
parent
58b4e7dab2
commit
ab9287e959
@ -204,49 +204,82 @@ 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);
|
||||
// Calculate the expected message size
|
||||
size_t msg_size = 4; // address + function + CRC(2)
|
||||
if (this->role == ModbusRole::CLIENT) {
|
||||
data.push_back(start_address >> 8);
|
||||
data.push_back(start_address >> 0);
|
||||
msg_size += 2; // start_address
|
||||
if (function_code != 0x5 && function_code != 0x6) {
|
||||
data.push_back(number_of_entities >> 8);
|
||||
data.push_back(number_of_entities >> 0);
|
||||
msg_size += 2; // number_of_entities
|
||||
}
|
||||
}
|
||||
if (payload != nullptr) {
|
||||
if (this->role == ModbusRole::SERVER || function_code == 0xF || function_code == 0x10) {
|
||||
msg_size += 1 + payload_len; // byte count + payload
|
||||
} else {
|
||||
msg_size += 2; // single register value
|
||||
}
|
||||
}
|
||||
|
||||
// Use stack buffer for small messages (most common case)
|
||||
static constexpr size_t STACK_BUFFER_SIZE = 64;
|
||||
uint8_t stack_buffer[STACK_BUFFER_SIZE];
|
||||
std::vector<uint8_t> heap_buffer;
|
||||
|
||||
uint8_t *data;
|
||||
if (msg_size <= STACK_BUFFER_SIZE) {
|
||||
data = stack_buffer;
|
||||
} else {
|
||||
heap_buffer.resize(msg_size);
|
||||
data = heap_buffer.data();
|
||||
}
|
||||
|
||||
// Build the message
|
||||
size_t pos = 0;
|
||||
data[pos++] = address;
|
||||
data[pos++] = function_code;
|
||||
|
||||
if (this->role == ModbusRole::CLIENT) {
|
||||
data[pos++] = start_address >> 8;
|
||||
data[pos++] = start_address >> 0;
|
||||
if (function_code != 0x5 && function_code != 0x6) {
|
||||
data[pos++] = number_of_entities >> 8;
|
||||
data[pos++] = number_of_entities >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload != nullptr) {
|
||||
if (this->role == ModbusRole::SERVER || function_code == 0xF || function_code == 0x10) { // 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)
|
||||
this->flow_control_pin_->digital_write(false);
|
||||
waiting_for_response = address;
|
||||
last_send_ = millis();
|
||||
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty(data).c_str());
|
||||
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty(data, pos).c_str());
|
||||
}
|
||||
|
||||
// Helper function for lambdas
|
||||
// Send raw command. Except CRC everything must be contained in payload
|
||||
void Modbus::send_raw(const std::vector<uint8_t> &payload) {
|
||||
void Modbus::send_raw(const std::vector<uint8_t> &payload) { send_raw(std::span<const uint8_t>(payload)); }
|
||||
|
||||
void Modbus::send_raw(std::span<const uint8_t> payload) {
|
||||
if (payload.empty()) {
|
||||
return;
|
||||
}
|
||||
@ -255,14 +288,14 @@ void Modbus::send_raw(const std::vector<uint8_t> &payload) {
|
||||
this->flow_control_pin_->digital_write(true);
|
||||
|
||||
auto crc = crc16(payload.data(), payload.size());
|
||||
this->write_array(payload);
|
||||
this->write_array(payload.data(), payload.size());
|
||||
this->write_byte(crc & 0xFF);
|
||||
this->write_byte((crc >> 8) & 0xFF);
|
||||
this->flush();
|
||||
if (this->flow_control_pin_ != nullptr)
|
||||
this->flow_control_pin_->digital_write(false);
|
||||
waiting_for_response = payload[0];
|
||||
ESP_LOGV(TAG, "Modbus write raw: %s", format_hex_pretty(payload).c_str());
|
||||
ESP_LOGV(TAG, "Modbus write raw: %s", format_hex_pretty(payload.data(), payload.size()).c_str());
|
||||
last_send_ = millis();
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@ -32,6 +33,7 @@ class Modbus : public uart::UARTDevice, public Component {
|
||||
void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities,
|
||||
uint8_t payload_len = 0, const uint8_t *payload = nullptr);
|
||||
void send_raw(const std::vector<uint8_t> &payload);
|
||||
void send_raw(std::span<const uint8_t> payload);
|
||||
void set_role(ModbusRole role) { this->role = role; }
|
||||
void set_flow_control_pin(GPIOPin *flow_control_pin) { this->flow_control_pin_ = flow_control_pin; }
|
||||
uint8_t waiting_for_response{0};
|
||||
@ -65,13 +67,10 @@ class ModbusDevice {
|
||||
this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload);
|
||||
}
|
||||
void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); }
|
||||
void send_raw(std::span<const uint8_t> payload) { this->parent_->send_raw(payload); }
|
||||
void send_error(uint8_t function_code, uint8_t exception_code) {
|
||||
std::vector<uint8_t> error_response;
|
||||
error_response.reserve(3);
|
||||
error_response.push_back(this->address_);
|
||||
error_response.push_back(function_code | 0x80);
|
||||
error_response.push_back(exception_code);
|
||||
this->send_raw(error_response);
|
||||
uint8_t error_response[3] = {this->address_, static_cast<uint8_t>(function_code | 0x80), exception_code};
|
||||
this->send_raw(std::span<const uint8_t>(error_response, 3));
|
||||
}
|
||||
// If more than one device is connected block sending a new command before a response is received
|
||||
bool waiting_for_response() { return parent_->waiting_for_response != 0; }
|
||||
|
@ -224,12 +224,11 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> response;
|
||||
response.reserve(6);
|
||||
response.push_back(this->address_);
|
||||
response.push_back(function_code);
|
||||
response.insert(response.end(), data.begin(), data.begin() + 4);
|
||||
this->send_raw(response);
|
||||
uint8_t response[6];
|
||||
response[0] = this->address_;
|
||||
response[1] = function_code;
|
||||
std::copy(data.begin(), data.begin() + 4, response + 2);
|
||||
this->send_raw(std::span<const uint8_t>(response, 6));
|
||||
}
|
||||
|
||||
SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
|
||||
|
@ -77,10 +77,8 @@ void PZEMAC::dump_config() {
|
||||
}
|
||||
|
||||
void PZEMAC::reset_energy_() {
|
||||
std::vector<uint8_t> cmd;
|
||||
cmd.push_back(this->address_);
|
||||
cmd.push_back(PZEM_CMD_RESET_ENERGY);
|
||||
this->send_raw(cmd);
|
||||
uint8_t cmd[2] = {this->address_, PZEM_CMD_RESET_ENERGY};
|
||||
this->send_raw(std::span<const uint8_t>(cmd, 2));
|
||||
}
|
||||
|
||||
} // namespace pzemac
|
||||
|
@ -65,10 +65,8 @@ void PZEMDC::dump_config() {
|
||||
}
|
||||
|
||||
void PZEMDC::reset_energy() {
|
||||
std::vector<uint8_t> cmd;
|
||||
cmd.push_back(this->address_);
|
||||
cmd.push_back(PZEM_CMD_RESET_ENERGY);
|
||||
this->send_raw(cmd);
|
||||
uint8_t cmd[2] = {this->address_, PZEM_CMD_RESET_ENERGY};
|
||||
this->send_raw(std::span<const uint8_t>(cmd, 2));
|
||||
}
|
||||
|
||||
} // namespace pzemdc
|
||||
|
Loading…
x
Reference in New Issue
Block a user