ESPHome  2024.11.1
modbus_controller.cpp
Go to the documentation of this file.
1 #include "modbus_controller.h"
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace modbus_controller {
7 
8 static const char *const TAG = "modbus_controller";
9 
11 
12 /*
13  To work with the existing modbus class and avoid polling for responses a command queue is used.
14  send_next_command will submit the command at the top of the queue and set the corresponding callback
15  to handle the response from the device.
16  Once the response has been processed it is removed from the queue and the next command is sent
17 */
19  uint32_t last_send = millis() - this->last_command_timestamp_;
20 
21  if ((last_send > this->command_throttle_) && !waiting_for_response() && !this->command_queue_.empty()) {
22  auto &command = this->command_queue_.front();
23 
24  // remove from queue if command was sent too often
25  if (!command->should_retry(this->max_cmd_retries_)) {
26  if (!this->module_offline_) {
27  ESP_LOGW(TAG, "Modbus device=%d set offline", this->address_);
28 
29  if (this->offline_skip_updates_ > 0) {
30  // Update skip_updates_counter to stop flooding channel with timeouts
31  for (auto &r : this->register_ranges_) {
32  r.skip_updates_counter = this->offline_skip_updates_;
33  }
34  }
35 
36  this->module_offline_ = true;
37  this->offline_callback_.call((int) command->function_code, command->register_address);
38  }
39  ESP_LOGD(TAG, "Modbus command to device=%d register=0x%02X no response received - removed from send queue",
40  this->address_, command->register_address);
41  this->command_queue_.pop_front();
42  } else {
43  ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_,
44  command->register_address, command->register_count);
45  command->send();
46 
48 
49  this->command_sent_callback_.call((int) command->function_code, command->register_address);
50 
51  // remove from queue if no handler is defined
52  if (!command->on_data_func) {
53  this->command_queue_.pop_front();
54  }
55  }
56  }
57  return (!this->command_queue_.empty());
58 }
59 
60 // Queue incoming response
61 void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
62  auto &current_command = this->command_queue_.front();
63  if (current_command != nullptr) {
64  if (this->module_offline_) {
65  ESP_LOGW(TAG, "Modbus device=%d back online", this->address_);
66 
67  if (this->offline_skip_updates_ > 0) {
68  // Restore skip_updates_counter to restore commands updates
69  for (auto &r : this->register_ranges_) {
70  r.skip_updates_counter = 0;
71  }
72  }
73  // Restore module online state
74  this->module_offline_ = false;
75  this->online_callback_.call((int) current_command->function_code, current_command->register_address);
76  }
77 
78  // Move the commandItem to the response queue
79  current_command->payload = data;
80  this->incoming_queue_.push(std::move(current_command));
81  ESP_LOGV(TAG, "Modbus response queued");
82  this->command_queue_.pop_front();
83  }
84 }
85 
86 // Dispatch the response to the registered handler
88  ESP_LOGV(TAG, "Process modbus response for address 0x%X size: %zu", response->register_address,
89  response->payload.size());
90  response->on_data_func(response->register_type, response->register_address, response->payload);
91 }
92 
93 void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) {
94  ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
95  // Remove pending command waiting for a response
96  auto &current_command = this->command_queue_.front();
97  if (current_command != nullptr) {
98  ESP_LOGE(TAG,
99  "Modbus error - last command: function code=0x%X register address = 0x%X "
100  "registers count=%d "
101  "payload size=%zu",
102  function_code, current_command->register_address, current_command->register_count,
103  current_command->payload.size());
104  this->command_queue_.pop_front();
105  }
106 }
107 
108 void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t start_address,
109  uint16_t number_of_registers) {
110  ESP_LOGD(TAG,
111  "Received read holding/input registers for device 0x%X. FC: 0x%X. Start address: 0x%X. Number of registers: "
112  "0x%X.",
113  this->address_, function_code, start_address, number_of_registers);
114 
115  std::vector<uint16_t> sixteen_bit_response;
116  for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
117  bool found = false;
118  for (auto *server_register : this->server_registers_) {
119  if (server_register->address == current_address) {
120  float value = server_register->read_lambda();
121 
122  ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %0.1f.",
123  server_register->address, static_cast<uint8_t>(server_register->value_type),
124  server_register->register_count, value);
125  std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type);
126  sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend());
127  current_address += server_register->register_count;
128  found = true;
129  break;
130  }
131  }
132 
133  if (!found) {
134  ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
135  std::vector<uint8_t> error_response;
136  error_response.push_back(this->address_);
137  error_response.push_back(0x81);
138  error_response.push_back(0x02);
139  this->send_raw(error_response);
140  return;
141  }
142  }
143 
144  std::vector<uint8_t> response;
145  for (auto v : sixteen_bit_response) {
146  auto decoded_value = decode_value(v);
147  response.push_back(decoded_value[0]);
148  response.push_back(decoded_value[1]);
149  }
150 
151  this->send(function_code, start_address, number_of_registers, response.size(), response.data());
152 }
153 
154 SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
155  auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) {
156  return (r.start_address == start_address && r.register_type == register_type);
157  });
158 
159  if (reg_it == register_ranges_.end()) {
160  ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
161  } else {
162  return reg_it->sensors;
163  }
164 
165  // not found
166  return {};
167 }
168 void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address,
169  const std::vector<uint8_t> &data) {
170  ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address);
171 
172  // loop through all sensors with the same start address
173  auto sensors = find_sensors_(register_type, start_address);
174  for (auto *sensor : sensors) {
175  sensor->parse_and_publish(data);
176  }
177 }
178 
180  if (!this->allow_duplicate_commands_) {
181  // check if this command is already qeued.
182  // not very effective but the queue is never really large
183  for (auto &item : this->command_queue_) {
184  if (item->is_equal(command)) {
185  ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u",
186  static_cast<uint8_t>(command.register_type), command.register_address, command.register_count);
187  // update the payload of the queued command
188  // replaces a previous command
189  item->payload = command.payload;
190  return;
191  }
192  }
193  }
194  this->command_queue_.push_back(make_unique<ModbusCommandItem>(command));
195 }
196 
198  ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type,
200  if (r.skip_updates_counter == 0) {
201  // if a custom command is used the user supplied custom_data is only available in the SensorItem.
203  auto sensors = this->find_sensors_(r.register_type, r.start_address);
204  if (!sensors.empty()) {
205  auto sensor = sensors.cbegin();
206  auto command_item = ModbusCommandItem::create_custom_command(
207  this, (*sensor)->custom_data,
208  [this](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
209  this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
210  });
211  command_item.register_address = (*sensor)->start_address;
212  command_item.register_count = (*sensor)->register_count;
213  command_item.function_code = ModbusFunctionCode::CUSTOM;
214  queue_command(command_item);
215  }
216  } else {
218  }
219  r.skip_updates_counter = r.skip_updates; // reset counter to config value
220  } else {
222  }
223 }
224 //
225 // Queue the modbus requests to be send.
226 // Once we get a response to the command it is removed from the queue and the next command is send
227 //
229  if (!this->command_queue_.empty()) {
230  ESP_LOGV(TAG, "%zu modbus commands already in queue", this->command_queue_.size());
231  } else {
232  ESP_LOGV(TAG, "Updating modbus component");
233  }
234 
235  for (auto &r : this->register_ranges_) {
236  ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address);
237  update_range_(r);
238  }
239 }
240 
241 // walk through the sensors and determine the register ranges to read
243  register_ranges_.clear();
244  if (this->parent_->role == modbus::ModbusRole::CLIENT && sensorset_.empty()) {
245  ESP_LOGW(TAG, "No sensors registered");
246  return 0;
247  }
248 
249  // iterator is sorted see SensorItemsComparator for details
250  auto ix = sensorset_.begin();
251  RegisterRange r = {};
252  uint8_t buffer_offset = 0;
253  SensorItem *prev = nullptr;
254  while (ix != sensorset_.end()) {
255  SensorItem *curr = *ix;
256 
257  ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
258  curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr);
259 
260  if (r.register_count == 0) {
261  // this is the first register in range
262  r.start_address = curr->start_address;
263  r.register_count = curr->register_count;
264  r.register_type = curr->register_type;
265  r.sensors.insert(curr);
266  r.skip_updates = curr->skip_updates;
267  r.skip_updates_counter = 0;
268  buffer_offset = curr->get_register_size();
269 
270  ESP_LOGV(TAG, "Started new range");
271  } else {
272  // this is not the first register in range so it might be possible
273  // to reuse the last register or extend the current range
274  if (!curr->force_new_range && r.register_type == curr->register_type &&
276  if (curr->start_address == (r.start_address + r.register_count - prev->register_count) &&
277  curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) {
278  // this register can re-use the data from the previous register
279 
280  // remove this sensore because start_address is changed (sort-order)
281  ix = sensorset_.erase(ix);
282 
283  curr->start_address = r.start_address;
284  curr->offset += prev->offset;
285 
286  sensorset_.insert(curr);
287  // move iterator backwards because it will be incremented later
288  ix--;
289 
290  ESP_LOGV(TAG, "Re-use previous register - change to register: 0x%X %d offset=%u", curr->start_address,
291  curr->register_count, curr->offset);
292  } else if (curr->start_address == (r.start_address + r.register_count)) {
293  // this register can extend the current range
294 
295  // remove this sensore because start_address is changed (sort-order)
296  ix = sensorset_.erase(ix);
297 
298  curr->start_address = r.start_address;
299  curr->offset += buffer_offset;
300  buffer_offset += curr->get_register_size();
301  r.register_count += curr->register_count;
302 
303  sensorset_.insert(curr);
304  // move iterator backwards because it will be incremented later
305  ix--;
306 
307  ESP_LOGV(TAG, "Extend range - change to register: 0x%X %d offset=%u", curr->start_address,
308  curr->register_count, curr->offset);
309  }
310  }
311  }
312 
313  if (curr->start_address == r.start_address && curr->register_type == r.register_type) {
314  // use the lowest non zero value for the whole range
315  // Because zero is the default value for skip_updates it is excluded from getting the min value.
316  if (curr->skip_updates != 0) {
317  if (r.skip_updates != 0) {
318  r.skip_updates = std::min(r.skip_updates, curr->skip_updates);
319  } else {
320  r.skip_updates = curr->skip_updates;
321  }
322  }
323 
324  // add sensor to this range
325  r.sensors.insert(curr);
326 
327  ix++;
328  } else {
329  ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
330  register_ranges_.push_back(r);
331  r = {};
332  buffer_offset = 0;
333  // do not increment the iterator here because the current sensor has to be re-evaluated
334  }
335 
336  prev = curr;
337  }
338 
339  if (r.register_count > 0) {
340  // Add the last range
341  ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
342  register_ranges_.push_back(r);
343  }
344 
345  return register_ranges_.size();
346 }
347 
349  ESP_LOGCONFIG(TAG, "ModbusController:");
350  ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
351  ESP_LOGCONFIG(TAG, " Max Command Retries: %d", this->max_cmd_retries_);
352  ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_);
353 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
354  ESP_LOGCONFIG(TAG, "sensormap");
355  for (auto &it : sensorset_) {
356  ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
357  static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
358  it->get_register_size());
359  }
360  ESP_LOGCONFIG(TAG, "ranges");
361  for (auto &it : register_ranges_) {
362  ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
363  it.start_address, it.register_count, it.skip_updates);
364  }
365  ESP_LOGCONFIG(TAG, "server registers");
366  for (auto &r : server_registers_) {
367  ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address,
368  static_cast<uint8_t>(r->value_type), r->register_count);
369  }
370 #endif
371 }
372 
374  // Incoming data to process?
375  if (!incoming_queue_.empty()) {
376  auto &message = incoming_queue_.front();
377  if (message != nullptr)
378  process_modbus_data_(message.get());
379  incoming_queue_.pop();
380 
381  } else {
382  // all messages processed send pending commands
384  }
385 }
386 
387 void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address,
388  const std::vector<uint8_t> &data) {
389  ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
390 }
391 
393  ESP_LOGV(TAG, "sensors");
394  for (auto &it : sensorset_) {
395  ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
396  it->get_register_size(), it->offset);
397  }
398 }
399 
401  ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count,
402  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
403  &&handler) {
405  cmd.modbusdevice = modbusdevice;
406  cmd.register_type = register_type;
407  cmd.function_code = modbus_register_read_function(register_type);
408  cmd.register_address = start_address;
409  cmd.register_count = register_count;
410  cmd.on_data_func = std::move(handler);
411  return cmd;
412 }
413 
415  ModbusRegisterType register_type, uint16_t start_address,
416  uint16_t register_count) {
418  cmd.modbusdevice = modbusdevice;
419  cmd.register_type = register_type;
420  cmd.function_code = modbus_register_read_function(register_type);
421  cmd.register_address = start_address;
422  cmd.register_count = register_count;
423  cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
424  const std::vector<uint8_t> &data) {
425  modbusdevice->on_register_data(register_type, start_address, data);
426  };
427  return cmd;
428 }
429 
431  uint16_t start_address, uint16_t register_count,
432  const std::vector<uint16_t> &values) {
434  cmd.modbusdevice = modbusdevice;
437  cmd.register_address = start_address;
438  cmd.register_count = register_count;
439  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
440  const std::vector<uint8_t> &data) {
441  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
442  };
443  for (auto v : values) {
444  auto decoded_value = decode_value(v);
445  cmd.payload.push_back(decoded_value[0]);
446  cmd.payload.push_back(decoded_value[1]);
447  }
448  return cmd;
449 }
450 
452  bool value) {
454  cmd.modbusdevice = modbusdevice;
458  cmd.register_count = 1;
459  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
460  const std::vector<uint8_t> &data) {
461  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
462  };
463  cmd.payload.push_back(value ? 0xFF : 0);
464  cmd.payload.push_back(0);
465  return cmd;
466 }
467 
469  const std::vector<bool> &values) {
471  cmd.modbusdevice = modbusdevice;
474  cmd.register_address = start_address;
475  cmd.register_count = values.size();
476  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
477  const std::vector<uint8_t> &data) {
478  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
479  };
480 
481  uint8_t bitmask = 0;
482  int bitcounter = 0;
483  for (auto coil : values) {
484  if (coil) {
485  bitmask |= (1 << bitcounter);
486  }
487  bitcounter++;
488  if (bitcounter % 8 == 0) {
489  cmd.payload.push_back(bitmask);
490  bitmask = 0;
491  }
492  }
493  // add remaining bits
494  if (bitcounter % 8) {
495  cmd.payload.push_back(bitmask);
496  }
497  return cmd;
498 }
499 
501  uint16_t value) {
503  cmd.modbusdevice = modbusdevice;
506  cmd.register_address = start_address;
507  cmd.register_count = 1; // not used here anyways
508  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
509  const std::vector<uint8_t> &data) {
510  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
511  };
512 
513  auto decoded_value = decode_value(value);
514  cmd.payload.push_back(decoded_value[0]);
515  cmd.payload.push_back(decoded_value[1]);
516  return cmd;
517 }
518 
520  ModbusController *modbusdevice, const std::vector<uint8_t> &values,
521  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
522  &&handler) {
524  cmd.modbusdevice = modbusdevice;
526  if (handler == nullptr) {
527  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
528  ESP_LOGI(TAG, "Custom Command sent");
529  };
530  } else {
531  cmd.on_data_func = handler;
532  }
533  cmd.payload = values;
534 
535  return cmd;
536 }
537 
539  ModbusController *modbusdevice, const std::vector<uint16_t> &values,
540  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
541  &&handler) {
542  ModbusCommandItem cmd = {};
543  cmd.modbusdevice = modbusdevice;
545  if (handler == nullptr) {
546  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
547  ESP_LOGI(TAG, "Custom Command sent");
548  };
549  } else {
550  cmd.on_data_func = handler;
551  }
552  for (auto v : values) {
553  cmd.payload.push_back((v >> 8) & 0xFF);
554  cmd.payload.push_back(v & 0xFF);
555  }
556 
557  return cmd;
558 }
559 
561  if (this->function_code != ModbusFunctionCode::CUSTOM) {
562  modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
563  this->payload.empty() ? nullptr : &this->payload[0]);
564  } else {
565  modbusdevice->send_raw(this->payload);
566  }
567  this->send_count_++;
568  ESP_LOGV(TAG, "Command sent %d 0x%X %d send_count: %d", uint8_t(this->function_code), this->register_address,
569  this->register_count, this->send_count_);
570  return true;
571 }
572 
574  // for custom commands we have to check for identical payloads, since
575  // address/count/type fields will be set to zero
576  return this->function_code == ModbusFunctionCode::CUSTOM
577  ? this->payload == other.payload
578  : other.register_address == this->register_address && other.register_count == this->register_count &&
579  other.register_type == this->register_type && other.function_code == this->function_code;
580 }
581 
582 void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
583  switch (value_type) {
586  data.push_back(value & 0xFFFF);
587  break;
591  data.push_back((value & 0xFFFF0000) >> 16);
592  data.push_back(value & 0xFFFF);
593  break;
597  data.push_back(value & 0xFFFF);
598  data.push_back((value & 0xFFFF0000) >> 16);
599  break;
602  data.push_back((value & 0xFFFF000000000000) >> 48);
603  data.push_back((value & 0xFFFF00000000) >> 32);
604  data.push_back((value & 0xFFFF0000) >> 16);
605  data.push_back(value & 0xFFFF);
606  break;
609  data.push_back(value & 0xFFFF);
610  data.push_back((value & 0xFFFF0000) >> 16);
611  data.push_back((value & 0xFFFF00000000) >> 32);
612  data.push_back((value & 0xFFFF000000000000) >> 48);
613  break;
614  default:
615  ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversation: %d",
616  static_cast<uint16_t>(value_type));
617  break;
618  }
619 }
620 
621 int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
622  uint32_t bitmask) {
623  int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
624 
625  switch (sensor_value_type) {
627  value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
628  break;
631  value = get_data<uint32_t>(data, offset);
632  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
633  break;
636  value = get_data<uint32_t>(data, offset);
637  value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
638  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
639  break;
641  value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
642  bitmask); // default is 0xFFFF ;
643  break;
645  value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
646  break;
648  value = get_data<uint32_t>(data, offset);
649  // Currently the high word is at the low position
650  // the sign bit is therefore at low before the switch
651  uint32_t sign_bit = (value & 0x8000) << 16;
653  static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
654  } break;
657  // Ignore bitmask for QWORD
658  value = get_data<uint64_t>(data, offset);
659  break;
662  // Ignore bitmask for QWORD
663  uint64_t tmp = get_data<uint64_t>(data, offset);
664  value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
665  } break;
667  default:
668  break;
669  }
670  return value;
671 }
672 
673 void ModbusController::add_on_command_sent_callback(std::function<void(int, int)> &&callback) {
674  this->command_sent_callback_.add(std::move(callback));
675 }
676 
677 void ModbusController::add_on_online_callback(std::function<void(int, int)> &&callback) {
678  this->online_callback_.add(std::move(callback));
679 }
680 
681 void ModbusController::add_on_offline_callback(std::function<void(int, int)> &&callback) {
682  this->offline_callback_.add(std::move(callback));
683 }
684 
685 } // namespace modbus_controller
686 } // namespace esphome
void queue_command(const ModbusCommandItem &command)
queues a modbus command in the send queue
void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final
called when a modbus request (function code 3 or 4) was parsed without errors
uint8_t max_cmd_retries_
How many times we will retry a command if we get no response.
void add_on_offline_callback(std::function< void(int, int)> &&callback)
Set callback for offline changes.
bool module_offline_
if module didn&#39;t respond the last command
std::vector< ServerRegister * > server_registers_
Collection of all server registers for this component.
CallbackManager< void(int, int)> command_sent_callback_
Command sent callback.
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 add_on_online_callback(std::function< void(int, int)> &&callback)
Set callback for online changes.
std::vector< uint16_t > float_to_payload(float value, SensorValueType value_type)
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
bool allow_duplicate_commands_
if duplicate commands can be sent
uint32_t last_command_timestamp_
when was the last send operation
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
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 add_on_command_sent_callback(std::function< void(int, int)> &&callback)
Set callback for commands.
ModbusRole role
Definition: modbus.h:41
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)
Definition: modbus.h:66
constexpr14 std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
Definition: helpers.h:213
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)
CallbackManager< void(int, int)> online_callback_
Server online callback.
CallbackManager< void(int, int)> offline_callback_
Server offline callback.
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.
Definition: a01nyub.cpp:7
uint8_t address
Definition: bl0906.h:211
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)
Definition: modbus.h:62
uint8_t end[39]
Definition: sun_gtil2.cpp:31
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
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
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.
stm32_cmd_t * cmd
Definition: stm32flash.h:96