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