6 namespace modbus_controller {
8 static const char *
const TAG =
"modbus_controller";
28 if (command->send_countdown < 1) {
30 ESP_LOGW(TAG,
"Modbus device=%d set offline", this->
address_);
42 "Modbus command to device=%d register=0x%02X countdown=%d no response received - removed from send queue",
43 this->
address_, command->register_address, command->send_countdown);
46 ESP_LOGV(TAG,
"Sending next modbus command to device %d register 0x%02X count %d", this->
address_,
47 command->register_address, command->register_count);
51 if (!command->on_data_func) {
62 if (current_command !=
nullptr) {
64 ESP_LOGW(TAG,
"Modbus device=%d back online", this->
address_);
69 r.skip_updates_counter = 0;
76 current_command->payload = data;
78 ESP_LOGV(TAG,
"Modbus response queued");
85 ESP_LOGV(TAG,
"Process modbus response for address 0x%X size: %zu", response->
register_address,
91 ESP_LOGE(TAG,
"Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
94 if (current_command !=
nullptr) {
96 "Modbus error - last command: function code=0x%X register address = 0x%X " 99 function_code, current_command->register_address, current_command->register_count,
100 current_command->payload.size());
111 ESP_LOGE(TAG,
"No matching range for sensor found - start_address : 0x%X", start_address);
113 return reg_it->sensors;
120 const std::vector<uint8_t> &data) {
121 ESP_LOGV(TAG,
"data for register address : 0x%X : ", start_address);
125 for (
auto *sensor : sensors) {
126 sensor->parse_and_publish(data);
134 if (item->is_equal(command)) {
135 ESP_LOGW(TAG,
"Duplicate modbus command found: type=0x%x address=%u count=%u",
139 item->payload = command.
payload;
143 command_queue_.push_back(make_unique<ModbusCommandItem>(command));
153 if (!sensors.empty()) {
154 auto sensor = sensors.cbegin();
156 this, (*sensor)->custom_data,
157 [
this](
ModbusRegisterType register_type, uint16_t start_address,
const std::vector<uint8_t> &data) {
158 this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
160 command_item.register_address = (*sensor)->start_address;
161 command_item.register_count = (*sensor)->register_count;
179 ESP_LOGV(TAG,
"%zu modbus commands already in queue",
command_queue_.size());
181 ESP_LOGV(TAG,
"Updating modbus component");
185 ESP_LOGVV(TAG,
"Updating range 0x%X", r.start_address);
194 ESP_LOGW(TAG,
"No sensors registered");
201 uint8_t buffer_offset = 0;
219 ESP_LOGV(TAG,
"Started new range");
239 ESP_LOGV(TAG,
"Re-use previous register - change to register: 0x%X %d offset=%u", curr->
start_address,
248 curr->
offset += buffer_offset;
256 ESP_LOGV(TAG,
"Extend range - change to register: 0x%X %d offset=%u", curr->
start_address,
298 ESP_LOGCONFIG(TAG,
"ModbusController:");
299 ESP_LOGCONFIG(TAG,
" Address: 0x%02X", this->
address_);
300 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE 301 ESP_LOGCONFIG(TAG,
"sensormap");
303 ESP_LOGCONFIG(TAG,
" Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
304 static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
305 it->get_register_size());
307 ESP_LOGCONFIG(TAG,
"ranges");
309 ESP_LOGCONFIG(TAG,
" Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
310 it.start_address, it.register_count, it.skip_updates);
319 if (message !=
nullptr)
330 const std::vector<uint8_t> &data) {
331 ESP_LOGV(TAG,
"Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
335 ESP_LOGV(TAG,
"sensors");
337 ESP_LOGV(TAG,
" Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
338 it->get_register_size(), it->offset);
344 std::function<
void(
ModbusRegisterType register_type, uint16_t start_address,
const std::vector<uint8_t> &data)>
358 uint16_t register_count) {
366 const std::vector<uint8_t> &data) {
373 uint16_t start_address, uint16_t register_count,
374 const std::vector<uint16_t> &values) {
382 const std::vector<uint8_t> &data) {
385 for (
auto v : values) {
387 cmd.
payload.push_back(decoded_value[0]);
388 cmd.
payload.push_back(decoded_value[1]);
402 const std::vector<uint8_t> &data) {
405 cmd.
payload.push_back(value ? 0xFF : 0);
411 const std::vector<bool> &values) {
419 const std::vector<uint8_t> &data) {
425 for (
auto coil : values) {
427 bitmask |= (1 << bitcounter);
430 if (bitcounter % 8 == 0) {
431 cmd.
payload.push_back(bitmask);
436 if (bitcounter % 8) {
437 cmd.
payload.push_back(bitmask);
451 const std::vector<uint8_t> &data) {
456 cmd.
payload.push_back(decoded_value[0]);
457 cmd.
payload.push_back(decoded_value[1]);
463 std::function<
void(
ModbusRegisterType register_type, uint16_t start_address,
const std::vector<uint8_t> &data)>
468 if (handler ==
nullptr) {
470 ESP_LOGI(TAG,
"Custom Command sent");
482 std::function<
void(
ModbusRegisterType register_type, uint16_t start_address,
const std::vector<uint8_t> &data)>
487 if (handler ==
nullptr) {
489 ESP_LOGI(TAG,
"Custom Command sent");
494 for (
auto v : values) {
495 cmd.
payload.push_back((v >> 8) & 0xFF);
496 cmd.
payload.push_back(v & 0xFF);
504 modbusdevice->
send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
505 this->payload.empty() ? nullptr : &this->payload[0]);
507 modbusdevice->
send_raw(this->payload);
509 ESP_LOGV(TAG,
"Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count);
518 ? this->payload == other.
payload 524 switch (value_type) {
527 data.push_back(value & 0xFFFF);
532 data.push_back((value & 0xFFFF0000) >> 16);
533 data.push_back(value & 0xFFFF);
538 data.push_back(value & 0xFFFF);
539 data.push_back((value & 0xFFFF0000) >> 16);
543 data.push_back((value & 0xFFFF000000000000) >> 48);
544 data.push_back((value & 0xFFFF00000000) >> 32);
545 data.push_back((value & 0xFFFF0000) >> 16);
546 data.push_back(value & 0xFFFF);
550 data.push_back(value & 0xFFFF);
551 data.push_back((value & 0xFFFF0000) >> 16);
552 data.push_back((value & 0xFFFF00000000) >> 32);
553 data.push_back((value & 0xFFFF000000000000) >> 48);
556 ESP_LOGE(TAG,
"Invalid data type for modbus number to payload conversation: %d",
557 static_cast<uint16_t>(value_type));
566 switch (sensor_value_type) {
572 value = get_data<uint32_t>(data, offset);
577 value = get_data<uint32_t>(data, offset);
578 value =
static_cast<uint32_t
>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
589 value = get_data<uint32_t>(data, offset);
592 uint32_t sign_bit = (value & 0x8000) << 16;
594 static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
599 value = get_data<uint64_t>(data, offset);
604 uint64_t tmp = get_data<uint64_t>(data, offset);
605 value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
void queue_command(const ModbusCommandItem &command)
queues a modbus command in the send queue
bool module_offline_
if module didn't respond the last command
N mask_and_shift_by_rightbit(N data, uint32_t mask)
Extract bits from value and shift right according to the bitmask if the bitmask is 0x00F0 we want the...
void dump_config() override
ModbusRegisterType register_type
virtual size_t get_register_size() const
void on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, const std::vector< uint8_t > &data)
default delegate called by process_modbus_data when a response for a write response has retrieved fro...
static ModbusCommandItem create_write_single_command(ModbusController *modbusdevice, uint16_t start_address, uint16_t value)
Create modbus write multiple registers command Function 16 (10hex) Write Multiple Registers...
uint16_t command_throttle_
min time in ms between sending modbus commands
uint32_t last_command_timestamp_
when was the last send operation
std::vector< uint8_t > payload
ModbusRegisterType register_type
uint32_t IRAM_ATTR HOT millis()
bool waiting_for_response()
bool send_next_command_()
send the next modbus command from the send queue
SensorSet sensorset_
Collection of all sensors for this component.
static ModbusCommandItem create_write_single_coil(ModbusController *modbusdevice, uint16_t address, bool value)
Create modbus write single registers command Function 05 (05hex) Write Single Coil.
SensorSet find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const
void number_to_payload(std::vector< uint16_t > &data, int64_t value, SensorValueType value_type)
Convert float value to vector<uint16_t> suitable for sending.
void dump_sensors_()
dump the parsed sensormap for diagnostics
void send_raw(const std::vector< uint8_t > &payload)
constexpr14 std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
uint16_t skip_updates_counter
void on_modbus_data(const std::vector< uint8_t > &data) override
called when a modbus response was parsed without errors
void on_modbus_error(uint8_t function_code, uint8_t exception_code) override
called when a modbus error response was received
std::function< void(ModbusRegisterType register_type, uint16_t start_address, const std::vector< uint8_t > &data)> on_data_func
void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector< uint8_t > &data)
default delegate called by process_modbus_data when a response has retrieved from the incoming queue ...
void process_modbus_data_(const ModbusCommandItem *response)
parse incoming modbus data
std::list< std::unique_ptr< ModbusCommandItem > > command_queue_
Hold the pending requests to be sent.
static ModbusCommandItem create_read_command(ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count, std::function< void(ModbusRegisterType register_type, uint16_t start_address, const std::vector< uint8_t > &data)> &&handler)
factory methods
ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type)
static ModbusCommandItem create_write_multiple_coils(ModbusController *modbusdevice, uint16_t start_address, const std::vector< bool > &values)
Create modbus write multiple registers command Function 15 (0Fhex) Write Multiple Coils...
int64_t payload_to_number(const std::vector< uint8_t > &data, SensorValueType sensor_value_type, uint8_t offset, uint32_t bitmask)
Convert vector<uint8_t> response payload to number.
static ModbusCommandItem create_write_multiple_command(ModbusController *modbusdevice, uint16_t start_address, uint16_t register_count, const std::vector< uint16_t > &values)
Create modbus read command Function code 02-04.
std::vector< RegisterRange > register_ranges_
Continuous range of modbus registers.
Implementation of SPI Controller mode.
uint16_t offline_skip_updates_
how many updates to skip if module is offline
void update_range_(RegisterRange &r)
submit the read command for the address range to the send queue
bool is_equal(const ModbusCommandItem &other)
void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
ModbusFunctionCode function_code
std::set< SensorItem *, SensorItemsComparator > SensorSet
std::queue< std::unique_ptr< ModbusCommandItem > > incoming_queue_
modbus response data waiting to get processed
size_t create_register_ranges_()
parse sensormap_ and create range of sequential addresses
ModbusRegisterType register_type
ModbusController * modbusdevice
uint16_t register_address
static ModbusCommandItem create_custom_command(ModbusController *modbusdevice, const std::vector< uint8_t > &values, std::function< void(ModbusRegisterType register_type, uint16_t start_address, const std::vector< uint8_t > &data)> &&handler=nullptr)
Create custom modbus command.