ESPHome  2024.9.0
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  ESP_LOGD(TAG, "Modbus command to device=%d register=0x%02X no response received - removed from send queue",
38  this->address_, command->register_address);
39  this->command_queue_.pop_front();
40  } else {
41  ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_,
42  command->register_address, command->register_count);
43  command->send();
44 
46 
47  this->command_sent_callback_.call((int) command->function_code, command->register_address);
48 
49  // remove from queue if no handler is defined
50  if (!command->on_data_func) {
51  this->command_queue_.pop_front();
52  }
53  }
54  }
55  return (!this->command_queue_.empty());
56 }
57 
58 // Queue incoming response
59 void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
60  auto &current_command = this->command_queue_.front();
61  if (current_command != nullptr) {
62  if (this->module_offline_) {
63  ESP_LOGW(TAG, "Modbus device=%d back online", this->address_);
64 
65  if (this->offline_skip_updates_ > 0) {
66  // Restore skip_updates_counter to restore commands updates
67  for (auto &r : this->register_ranges_) {
68  r.skip_updates_counter = 0;
69  }
70  }
71  }
72  this->module_offline_ = false;
73 
74  // Move the commandItem to the response queue
75  current_command->payload = data;
76  this->incoming_queue_.push(std::move(current_command));
77  ESP_LOGV(TAG, "Modbus response queued");
78  this->command_queue_.pop_front();
79  }
80 }
81 
82 // Dispatch the response to the registered handler
84  ESP_LOGV(TAG, "Process modbus response for address 0x%X size: %zu", response->register_address,
85  response->payload.size());
86  response->on_data_func(response->register_type, response->register_address, response->payload);
87 }
88 
89 void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) {
90  ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
91  // Remove pending command waiting for a response
92  auto &current_command = this->command_queue_.front();
93  if (current_command != nullptr) {
94  ESP_LOGE(TAG,
95  "Modbus error - last command: function code=0x%X register address = 0x%X "
96  "registers count=%d "
97  "payload size=%zu",
98  function_code, current_command->register_address, current_command->register_count,
99  current_command->payload.size());
100  this->command_queue_.pop_front();
101  }
102 }
103 
104 void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t start_address,
105  uint16_t number_of_registers) {
106  ESP_LOGD(TAG,
107  "Received read holding/input registers for device 0x%X. FC: 0x%X. Start address: 0x%X. Number of registers: "
108  "0x%X.",
109  this->address_, function_code, start_address, number_of_registers);
110 
111  std::vector<uint16_t> sixteen_bit_response;
112  for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
113  bool found = false;
114  for (auto *server_register : this->server_registers_) {
115  if (server_register->address == current_address) {
116  float value = server_register->read_lambda();
117 
118  ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %0.1f.",
119  server_register->address, static_cast<uint8_t>(server_register->value_type),
120  server_register->register_count, value);
121  std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type);
122  sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend());
123  current_address += server_register->register_count;
124  found = true;
125  break;
126  }
127  }
128 
129  if (!found) {
130  ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
131  std::vector<uint8_t> error_response;
132  error_response.push_back(this->address_);
133  error_response.push_back(0x81);
134  error_response.push_back(0x02);
135  this->send_raw(error_response);
136  return;
137  }
138  }
139 
140  std::vector<uint8_t> response;
141  for (auto v : sixteen_bit_response) {
142  auto decoded_value = decode_value(v);
143  response.push_back(decoded_value[0]);
144  response.push_back(decoded_value[1]);
145  }
146 
147  this->send(function_code, start_address, number_of_registers, response.size(), response.data());
148 }
149 
150 SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
151  auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) {
152  return (r.start_address == start_address && r.register_type == register_type);
153  });
154 
155  if (reg_it == register_ranges_.end()) {
156  ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
157  } else {
158  return reg_it->sensors;
159  }
160 
161  // not found
162  return {};
163 }
164 void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address,
165  const std::vector<uint8_t> &data) {
166  ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address);
167 
168  // loop through all sensors with the same start address
169  auto sensors = find_sensors_(register_type, start_address);
170  for (auto *sensor : sensors) {
171  sensor->parse_and_publish(data);
172  }
173 }
174 
176  if (!this->allow_duplicate_commands_) {
177  // check if this command is already qeued.
178  // not very effective but the queue is never really large
179  for (auto &item : this->command_queue_) {
180  if (item->is_equal(command)) {
181  ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u",
182  static_cast<uint8_t>(command.register_type), command.register_address, command.register_count);
183  // update the payload of the queued command
184  // replaces a previous command
185  item->payload = command.payload;
186  return;
187  }
188  }
189  }
190  this->command_queue_.push_back(make_unique<ModbusCommandItem>(command));
191 }
192 
194  ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type,
196  if (r.skip_updates_counter == 0) {
197  // if a custom command is used the user supplied custom_data is only available in the SensorItem.
199  auto sensors = this->find_sensors_(r.register_type, r.start_address);
200  if (!sensors.empty()) {
201  auto sensor = sensors.cbegin();
202  auto command_item = ModbusCommandItem::create_custom_command(
203  this, (*sensor)->custom_data,
204  [this](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
205  this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
206  });
207  command_item.register_address = (*sensor)->start_address;
208  command_item.register_count = (*sensor)->register_count;
209  command_item.function_code = ModbusFunctionCode::CUSTOM;
210  queue_command(command_item);
211  }
212  } else {
214  }
215  r.skip_updates_counter = r.skip_updates; // reset counter to config value
216  } else {
218  }
219 }
220 //
221 // Queue the modbus requests to be send.
222 // Once we get a response to the command it is removed from the queue and the next command is send
223 //
225  if (!this->command_queue_.empty()) {
226  ESP_LOGV(TAG, "%zu modbus commands already in queue", this->command_queue_.size());
227  } else {
228  ESP_LOGV(TAG, "Updating modbus component");
229  }
230 
231  for (auto &r : this->register_ranges_) {
232  ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address);
233  update_range_(r);
234  }
235 }
236 
237 // walk through the sensors and determine the register ranges to read
239  register_ranges_.clear();
240  if (this->parent_->role == modbus::ModbusRole::CLIENT && sensorset_.empty()) {
241  ESP_LOGW(TAG, "No sensors registered");
242  return 0;
243  }
244 
245  // iterator is sorted see SensorItemsComparator for details
246  auto ix = sensorset_.begin();
247  RegisterRange r = {};
248  uint8_t buffer_offset = 0;
249  SensorItem *prev = nullptr;
250  while (ix != sensorset_.end()) {
251  SensorItem *curr = *ix;
252 
253  ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
254  curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr);
255 
256  if (r.register_count == 0) {
257  // this is the first register in range
258  r.start_address = curr->start_address;
259  r.register_count = curr->register_count;
260  r.register_type = curr->register_type;
261  r.sensors.insert(curr);
262  r.skip_updates = curr->skip_updates;
263  r.skip_updates_counter = 0;
264  buffer_offset = curr->get_register_size();
265 
266  ESP_LOGV(TAG, "Started new range");
267  } else {
268  // this is not the first register in range so it might be possible
269  // to reuse the last register or extend the current range
270  if (!curr->force_new_range && r.register_type == curr->register_type &&
272  if (curr->start_address == (r.start_address + r.register_count - prev->register_count) &&
273  curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) {
274  // this register can re-use the data from the previous register
275 
276  // remove this sensore because start_address is changed (sort-order)
277  ix = sensorset_.erase(ix);
278 
279  curr->start_address = r.start_address;
280  curr->offset += prev->offset;
281 
282  sensorset_.insert(curr);
283  // move iterator backwards because it will be incremented later
284  ix--;
285 
286  ESP_LOGV(TAG, "Re-use previous register - change to register: 0x%X %d offset=%u", curr->start_address,
287  curr->register_count, curr->offset);
288  } else if (curr->start_address == (r.start_address + r.register_count)) {
289  // this register can extend the current range
290 
291  // remove this sensore because start_address is changed (sort-order)
292  ix = sensorset_.erase(ix);
293 
294  curr->start_address = r.start_address;
295  curr->offset += buffer_offset;
296  buffer_offset += curr->get_register_size();
297  r.register_count += curr->register_count;
298 
299  sensorset_.insert(curr);
300  // move iterator backwards because it will be incremented later
301  ix--;
302 
303  ESP_LOGV(TAG, "Extend range - change to register: 0x%X %d offset=%u", curr->start_address,
304  curr->register_count, curr->offset);
305  }
306  }
307  }
308 
309  if (curr->start_address == r.start_address && curr->register_type == r.register_type) {
310  // use the lowest non zero value for the whole range
311  // Because zero is the default value for skip_updates it is excluded from getting the min value.
312  if (curr->skip_updates != 0) {
313  if (r.skip_updates != 0) {
314  r.skip_updates = std::min(r.skip_updates, curr->skip_updates);
315  } else {
316  r.skip_updates = curr->skip_updates;
317  }
318  }
319 
320  // add sensor to this range
321  r.sensors.insert(curr);
322 
323  ix++;
324  } else {
325  ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
326  register_ranges_.push_back(r);
327  r = {};
328  buffer_offset = 0;
329  // do not increment the iterator here because the current sensor has to be re-evaluated
330  }
331 
332  prev = curr;
333  }
334 
335  if (r.register_count > 0) {
336  // Add the last range
337  ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
338  register_ranges_.push_back(r);
339  }
340 
341  return register_ranges_.size();
342 }
343 
345  ESP_LOGCONFIG(TAG, "ModbusController:");
346  ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
347  ESP_LOGCONFIG(TAG, " Max Command Retries: %d", this->max_cmd_retries_);
348  ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_);
349 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
350  ESP_LOGCONFIG(TAG, "sensormap");
351  for (auto &it : sensorset_) {
352  ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
353  static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
354  it->get_register_size());
355  }
356  ESP_LOGCONFIG(TAG, "ranges");
357  for (auto &it : register_ranges_) {
358  ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
359  it.start_address, it.register_count, it.skip_updates);
360  }
361  ESP_LOGCONFIG(TAG, "server registers");
362  for (auto &r : server_registers_) {
363  ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address,
364  static_cast<uint8_t>(r->value_type), r->register_count);
365  }
366 #endif
367 }
368 
370  // Incoming data to process?
371  if (!incoming_queue_.empty()) {
372  auto &message = incoming_queue_.front();
373  if (message != nullptr)
374  process_modbus_data_(message.get());
375  incoming_queue_.pop();
376 
377  } else {
378  // all messages processed send pending commands
380  }
381 }
382 
383 void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address,
384  const std::vector<uint8_t> &data) {
385  ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
386 }
387 
389  ESP_LOGV(TAG, "sensors");
390  for (auto &it : sensorset_) {
391  ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
392  it->get_register_size(), it->offset);
393  }
394 }
395 
397  ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count,
398  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
399  &&handler) {
401  cmd.modbusdevice = modbusdevice;
402  cmd.register_type = register_type;
403  cmd.function_code = modbus_register_read_function(register_type);
404  cmd.register_address = start_address;
405  cmd.register_count = register_count;
406  cmd.on_data_func = std::move(handler);
407  return cmd;
408 }
409 
411  ModbusRegisterType register_type, uint16_t start_address,
412  uint16_t register_count) {
414  cmd.modbusdevice = modbusdevice;
415  cmd.register_type = register_type;
416  cmd.function_code = modbus_register_read_function(register_type);
417  cmd.register_address = start_address;
418  cmd.register_count = register_count;
419  cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
420  const std::vector<uint8_t> &data) {
421  modbusdevice->on_register_data(register_type, start_address, data);
422  };
423  return cmd;
424 }
425 
427  uint16_t start_address, uint16_t register_count,
428  const std::vector<uint16_t> &values) {
430  cmd.modbusdevice = modbusdevice;
433  cmd.register_address = start_address;
434  cmd.register_count = register_count;
435  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
436  const std::vector<uint8_t> &data) {
437  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
438  };
439  for (auto v : values) {
440  auto decoded_value = decode_value(v);
441  cmd.payload.push_back(decoded_value[0]);
442  cmd.payload.push_back(decoded_value[1]);
443  }
444  return cmd;
445 }
446 
448  bool value) {
450  cmd.modbusdevice = modbusdevice;
454  cmd.register_count = 1;
455  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
456  const std::vector<uint8_t> &data) {
457  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
458  };
459  cmd.payload.push_back(value ? 0xFF : 0);
460  cmd.payload.push_back(0);
461  return cmd;
462 }
463 
465  const std::vector<bool> &values) {
467  cmd.modbusdevice = modbusdevice;
470  cmd.register_address = start_address;
471  cmd.register_count = values.size();
472  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
473  const std::vector<uint8_t> &data) {
474  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
475  };
476 
477  uint8_t bitmask = 0;
478  int bitcounter = 0;
479  for (auto coil : values) {
480  if (coil) {
481  bitmask |= (1 << bitcounter);
482  }
483  bitcounter++;
484  if (bitcounter % 8 == 0) {
485  cmd.payload.push_back(bitmask);
486  bitmask = 0;
487  }
488  }
489  // add remaining bits
490  if (bitcounter % 8) {
491  cmd.payload.push_back(bitmask);
492  }
493  return cmd;
494 }
495 
497  uint16_t value) {
499  cmd.modbusdevice = modbusdevice;
502  cmd.register_address = start_address;
503  cmd.register_count = 1; // not used here anyways
504  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
505  const std::vector<uint8_t> &data) {
506  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
507  };
508 
509  auto decoded_value = decode_value(value);
510  cmd.payload.push_back(decoded_value[0]);
511  cmd.payload.push_back(decoded_value[1]);
512  return cmd;
513 }
514 
516  ModbusController *modbusdevice, const std::vector<uint8_t> &values,
517  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
518  &&handler) {
520  cmd.modbusdevice = modbusdevice;
522  if (handler == nullptr) {
523  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
524  ESP_LOGI(TAG, "Custom Command sent");
525  };
526  } else {
527  cmd.on_data_func = handler;
528  }
529  cmd.payload = values;
530 
531  return cmd;
532 }
533 
535  ModbusController *modbusdevice, const std::vector<uint16_t> &values,
536  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
537  &&handler) {
538  ModbusCommandItem cmd = {};
539  cmd.modbusdevice = modbusdevice;
541  if (handler == nullptr) {
542  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
543  ESP_LOGI(TAG, "Custom Command sent");
544  };
545  } else {
546  cmd.on_data_func = handler;
547  }
548  for (auto v : values) {
549  cmd.payload.push_back((v >> 8) & 0xFF);
550  cmd.payload.push_back(v & 0xFF);
551  }
552 
553  return cmd;
554 }
555 
557  if (this->function_code != ModbusFunctionCode::CUSTOM) {
558  modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
559  this->payload.empty() ? nullptr : &this->payload[0]);
560  } else {
561  modbusdevice->send_raw(this->payload);
562  }
563  this->send_count_++;
564  ESP_LOGV(TAG, "Command sent %d 0x%X %d send_count: %d", uint8_t(this->function_code), this->register_address,
565  this->register_count, this->send_count_);
566  return true;
567 }
568 
570  // for custom commands we have to check for identical payloads, since
571  // address/count/type fields will be set to zero
572  return this->function_code == ModbusFunctionCode::CUSTOM
573  ? this->payload == other.payload
574  : other.register_address == this->register_address && other.register_count == this->register_count &&
575  other.register_type == this->register_type && other.function_code == this->function_code;
576 }
577 
578 void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
579  switch (value_type) {
582  data.push_back(value & 0xFFFF);
583  break;
587  data.push_back((value & 0xFFFF0000) >> 16);
588  data.push_back(value & 0xFFFF);
589  break;
593  data.push_back(value & 0xFFFF);
594  data.push_back((value & 0xFFFF0000) >> 16);
595  break;
598  data.push_back((value & 0xFFFF000000000000) >> 48);
599  data.push_back((value & 0xFFFF00000000) >> 32);
600  data.push_back((value & 0xFFFF0000) >> 16);
601  data.push_back(value & 0xFFFF);
602  break;
605  data.push_back(value & 0xFFFF);
606  data.push_back((value & 0xFFFF0000) >> 16);
607  data.push_back((value & 0xFFFF00000000) >> 32);
608  data.push_back((value & 0xFFFF000000000000) >> 48);
609  break;
610  default:
611  ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversation: %d",
612  static_cast<uint16_t>(value_type));
613  break;
614  }
615 }
616 
617 int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
618  uint32_t bitmask) {
619  int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
620 
621  switch (sensor_value_type) {
623  value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
624  break;
627  value = get_data<uint32_t>(data, offset);
628  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
629  break;
632  value = get_data<uint32_t>(data, offset);
633  value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
634  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
635  break;
637  value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
638  bitmask); // default is 0xFFFF ;
639  break;
641  value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
642  break;
644  value = get_data<uint32_t>(data, offset);
645  // Currently the high word is at the low position
646  // the sign bit is therefore at low before the switch
647  uint32_t sign_bit = (value & 0x8000) << 16;
649  static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
650  } break;
653  // Ignore bitmask for QWORD
654  value = get_data<uint64_t>(data, offset);
655  break;
658  // Ignore bitmask for QWORD
659  uint64_t tmp = get_data<uint64_t>(data, offset);
660  value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
661  } break;
663  default:
664  break;
665  }
666  return value;
667 }
668 
669 void ModbusController::add_on_command_sent_callback(std::function<void(int, int)> &&callback) {
670  this->command_sent_callback_.add(std::move(callback));
671 }
672 
673 } // namespace modbus_controller
674 } // 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.
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_
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...
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:212
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.
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:37
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