ESPHome  2024.6.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() && !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  number_to_payload(sixteen_bit_response, value, server_register->value_type);
120  current_address += server_register->register_count;
121  found = true;
122  break;
123  }
124  }
125 
126  if (!found) {
127  ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
128  std::vector<uint8_t> error_response;
129  error_response.push_back(this->address_);
130  error_response.push_back(0x81);
131  error_response.push_back(0x02);
132  this->send_raw(error_response);
133  return;
134  }
135  }
136 
137  std::vector<uint8_t> response;
138  for (auto v : sixteen_bit_response) {
139  auto decoded_value = decode_value(v);
140  response.push_back(decoded_value[0]);
141  response.push_back(decoded_value[1]);
142  }
143 
144  this->send(function_code, start_address, number_of_registers, response.size(), response.data());
145 }
146 
147 SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
148  auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) {
149  return (r.start_address == start_address && r.register_type == register_type);
150  });
151 
152  if (reg_it == register_ranges_.end()) {
153  ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
154  } else {
155  return reg_it->sensors;
156  }
157 
158  // not found
159  return {};
160 }
161 void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address,
162  const std::vector<uint8_t> &data) {
163  ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address);
164 
165  // loop through all sensors with the same start address
166  auto sensors = find_sensors_(register_type, start_address);
167  for (auto *sensor : sensors) {
168  sensor->parse_and_publish(data);
169  }
170 }
171 
173  // check if this command is already qeued.
174  // not very effective but the queue is never really large
175  for (auto &item : command_queue_) {
176  if (item->is_equal(command)) {
177  ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u",
178  static_cast<uint8_t>(command.register_type), command.register_address, command.register_count);
179  // update the payload of the queued command
180  // replaces a previous command
181  item->payload = command.payload;
182  return;
183  }
184  }
185  command_queue_.push_back(make_unique<ModbusCommandItem>(command));
186 }
187 
189  ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type,
191  if (r.skip_updates_counter == 0) {
192  // if a custom command is used the user supplied custom_data is only available in the SensorItem.
194  auto sensors = this->find_sensors_(r.register_type, r.start_address);
195  if (!sensors.empty()) {
196  auto sensor = sensors.cbegin();
197  auto command_item = ModbusCommandItem::create_custom_command(
198  this, (*sensor)->custom_data,
199  [this](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
200  this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
201  });
202  command_item.register_address = (*sensor)->start_address;
203  command_item.register_count = (*sensor)->register_count;
204  command_item.function_code = ModbusFunctionCode::CUSTOM;
205  queue_command(command_item);
206  }
207  } else {
209  }
210  r.skip_updates_counter = r.skip_updates; // reset counter to config value
211  } else {
213  }
214 }
215 //
216 // Queue the modbus requests to be send.
217 // Once we get a response to the command it is removed from the queue and the next command is send
218 //
220  if (!command_queue_.empty()) {
221  ESP_LOGV(TAG, "%zu modbus commands already in queue", command_queue_.size());
222  } else {
223  ESP_LOGV(TAG, "Updating modbus component");
224  }
225 
226  for (auto &r : this->register_ranges_) {
227  ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address);
228  update_range_(r);
229  }
230 }
231 
232 // walk through the sensors and determine the register ranges to read
234  register_ranges_.clear();
235  if (this->parent_->role == modbus::ModbusRole::CLIENT && sensorset_.empty()) {
236  ESP_LOGW(TAG, "No sensors registered");
237  return 0;
238  }
239 
240  // iterator is sorted see SensorItemsComparator for details
241  auto ix = sensorset_.begin();
242  RegisterRange r = {};
243  uint8_t buffer_offset = 0;
244  SensorItem *prev = nullptr;
245  while (ix != sensorset_.end()) {
246  SensorItem *curr = *ix;
247 
248  ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
249  curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr);
250 
251  if (r.register_count == 0) {
252  // this is the first register in range
253  r.start_address = curr->start_address;
254  r.register_count = curr->register_count;
255  r.register_type = curr->register_type;
256  r.sensors.insert(curr);
257  r.skip_updates = curr->skip_updates;
258  r.skip_updates_counter = 0;
259  buffer_offset = curr->get_register_size();
260 
261  ESP_LOGV(TAG, "Started new range");
262  } else {
263  // this is not the first register in range so it might be possible
264  // to reuse the last register or extend the current range
265  if (!curr->force_new_range && r.register_type == curr->register_type &&
267  if (curr->start_address == (r.start_address + r.register_count - prev->register_count) &&
268  curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) {
269  // this register can re-use the data from the previous register
270 
271  // remove this sensore because start_address is changed (sort-order)
272  ix = sensorset_.erase(ix);
273 
274  curr->start_address = r.start_address;
275  curr->offset += prev->offset;
276 
277  sensorset_.insert(curr);
278  // move iterator backwards because it will be incremented later
279  ix--;
280 
281  ESP_LOGV(TAG, "Re-use previous register - change to register: 0x%X %d offset=%u", curr->start_address,
282  curr->register_count, curr->offset);
283  } else if (curr->start_address == (r.start_address + r.register_count)) {
284  // this register can extend the current range
285 
286  // remove this sensore because start_address is changed (sort-order)
287  ix = sensorset_.erase(ix);
288 
289  curr->start_address = r.start_address;
290  curr->offset += buffer_offset;
291  buffer_offset += curr->get_register_size();
292  r.register_count += curr->register_count;
293 
294  sensorset_.insert(curr);
295  // move iterator backwards because it will be incremented later
296  ix--;
297 
298  ESP_LOGV(TAG, "Extend range - change to register: 0x%X %d offset=%u", curr->start_address,
299  curr->register_count, curr->offset);
300  }
301  }
302  }
303 
304  if (curr->start_address == r.start_address && curr->register_type == r.register_type) {
305  // use the lowest non zero value for the whole range
306  // Because zero is the default value for skip_updates it is excluded from getting the min value.
307  if (curr->skip_updates != 0) {
308  if (r.skip_updates != 0) {
309  r.skip_updates = std::min(r.skip_updates, curr->skip_updates);
310  } else {
311  r.skip_updates = curr->skip_updates;
312  }
313  }
314 
315  // add sensor to this range
316  r.sensors.insert(curr);
317 
318  ix++;
319  } else {
320  ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
321  register_ranges_.push_back(r);
322  r = {};
323  buffer_offset = 0;
324  // do not increment the iterator here because the current sensor has to be re-evaluated
325  }
326 
327  prev = curr;
328  }
329 
330  if (r.register_count > 0) {
331  // Add the last range
332  ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
333  register_ranges_.push_back(r);
334  }
335 
336  return register_ranges_.size();
337 }
338 
340  ESP_LOGCONFIG(TAG, "ModbusController:");
341  ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
342 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
343  ESP_LOGCONFIG(TAG, "sensormap");
344  for (auto &it : sensorset_) {
345  ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
346  static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
347  it->get_register_size());
348  }
349  ESP_LOGCONFIG(TAG, "ranges");
350  for (auto &it : register_ranges_) {
351  ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
352  it.start_address, it.register_count, it.skip_updates);
353  }
354  ESP_LOGCONFIG(TAG, "server registers");
355  for (auto &r : server_registers_) {
356  ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address,
357  static_cast<uint8_t>(r->value_type), r->register_count);
358  }
359 #endif
360 }
361 
363  // Incoming data to process?
364  if (!incoming_queue_.empty()) {
365  auto &message = incoming_queue_.front();
366  if (message != nullptr)
367  process_modbus_data_(message.get());
368  incoming_queue_.pop();
369 
370  } else {
371  // all messages processed send pending commands
373  }
374 }
375 
376 void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address,
377  const std::vector<uint8_t> &data) {
378  ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
379 }
380 
382  ESP_LOGV(TAG, "sensors");
383  for (auto &it : sensorset_) {
384  ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
385  it->get_register_size(), it->offset);
386  }
387 }
388 
390  ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count,
391  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
392  &&handler) {
394  cmd.modbusdevice = modbusdevice;
395  cmd.register_type = register_type;
396  cmd.function_code = modbus_register_read_function(register_type);
397  cmd.register_address = start_address;
398  cmd.register_count = register_count;
399  cmd.on_data_func = std::move(handler);
400  return cmd;
401 }
402 
404  ModbusRegisterType register_type, uint16_t start_address,
405  uint16_t register_count) {
407  cmd.modbusdevice = modbusdevice;
408  cmd.register_type = register_type;
409  cmd.function_code = modbus_register_read_function(register_type);
410  cmd.register_address = start_address;
411  cmd.register_count = register_count;
412  cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
413  const std::vector<uint8_t> &data) {
414  modbusdevice->on_register_data(register_type, start_address, data);
415  };
416  return cmd;
417 }
418 
420  uint16_t start_address, uint16_t register_count,
421  const std::vector<uint16_t> &values) {
423  cmd.modbusdevice = modbusdevice;
426  cmd.register_address = start_address;
427  cmd.register_count = register_count;
428  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
429  const std::vector<uint8_t> &data) {
430  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
431  };
432  for (auto v : values) {
433  auto decoded_value = decode_value(v);
434  cmd.payload.push_back(decoded_value[0]);
435  cmd.payload.push_back(decoded_value[1]);
436  }
437  return cmd;
438 }
439 
441  bool value) {
443  cmd.modbusdevice = modbusdevice;
446  cmd.register_address = address;
447  cmd.register_count = 1;
448  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
449  const std::vector<uint8_t> &data) {
450  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
451  };
452  cmd.payload.push_back(value ? 0xFF : 0);
453  cmd.payload.push_back(0);
454  return cmd;
455 }
456 
458  const std::vector<bool> &values) {
460  cmd.modbusdevice = modbusdevice;
463  cmd.register_address = start_address;
464  cmd.register_count = values.size();
465  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
466  const std::vector<uint8_t> &data) {
467  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
468  };
469 
470  uint8_t bitmask = 0;
471  int bitcounter = 0;
472  for (auto coil : values) {
473  if (coil) {
474  bitmask |= (1 << bitcounter);
475  }
476  bitcounter++;
477  if (bitcounter % 8 == 0) {
478  cmd.payload.push_back(bitmask);
479  bitmask = 0;
480  }
481  }
482  // add remaining bits
483  if (bitcounter % 8) {
484  cmd.payload.push_back(bitmask);
485  }
486  return cmd;
487 }
488 
490  uint16_t value) {
492  cmd.modbusdevice = modbusdevice;
495  cmd.register_address = start_address;
496  cmd.register_count = 1; // not used here anyways
497  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
498  const std::vector<uint8_t> &data) {
499  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
500  };
501 
502  auto decoded_value = decode_value(value);
503  cmd.payload.push_back(decoded_value[0]);
504  cmd.payload.push_back(decoded_value[1]);
505  return cmd;
506 }
507 
509  ModbusController *modbusdevice, const std::vector<uint8_t> &values,
510  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
511  &&handler) {
513  cmd.modbusdevice = modbusdevice;
515  if (handler == nullptr) {
516  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
517  ESP_LOGI(TAG, "Custom Command sent");
518  };
519  } else {
520  cmd.on_data_func = handler;
521  }
522  cmd.payload = values;
523 
524  return cmd;
525 }
526 
528  ModbusController *modbusdevice, const std::vector<uint16_t> &values,
529  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
530  &&handler) {
531  ModbusCommandItem cmd = {};
532  cmd.modbusdevice = modbusdevice;
534  if (handler == nullptr) {
535  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
536  ESP_LOGI(TAG, "Custom Command sent");
537  };
538  } else {
539  cmd.on_data_func = handler;
540  }
541  for (auto v : values) {
542  cmd.payload.push_back((v >> 8) & 0xFF);
543  cmd.payload.push_back(v & 0xFF);
544  }
545 
546  return cmd;
547 }
548 
550  if (this->function_code != ModbusFunctionCode::CUSTOM) {
551  modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
552  this->payload.empty() ? nullptr : &this->payload[0]);
553  } else {
554  modbusdevice->send_raw(this->payload);
555  }
556  ESP_LOGV(TAG, "Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count);
557  send_countdown--;
558  return true;
559 }
560 
562  // for custom commands we have to check for identical payloads, since
563  // address/count/type fields will be set to zero
564  return this->function_code == ModbusFunctionCode::CUSTOM
565  ? this->payload == other.payload
566  : other.register_address == this->register_address && other.register_count == this->register_count &&
567  other.register_type == this->register_type && other.function_code == this->function_code;
568 }
569 
570 void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
571  switch (value_type) {
574  data.push_back(value & 0xFFFF);
575  break;
579  data.push_back((value & 0xFFFF0000) >> 16);
580  data.push_back(value & 0xFFFF);
581  break;
585  data.push_back(value & 0xFFFF);
586  data.push_back((value & 0xFFFF0000) >> 16);
587  break;
590  data.push_back((value & 0xFFFF000000000000) >> 48);
591  data.push_back((value & 0xFFFF00000000) >> 32);
592  data.push_back((value & 0xFFFF0000) >> 16);
593  data.push_back(value & 0xFFFF);
594  break;
597  data.push_back(value & 0xFFFF);
598  data.push_back((value & 0xFFFF0000) >> 16);
599  data.push_back((value & 0xFFFF00000000) >> 32);
600  data.push_back((value & 0xFFFF000000000000) >> 48);
601  break;
602  default:
603  ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversation: %d",
604  static_cast<uint16_t>(value_type));
605  break;
606  }
607 }
608 
609 int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
610  uint32_t bitmask) {
611  int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
612 
613  switch (sensor_value_type) {
615  value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
616  break;
619  value = get_data<uint32_t>(data, offset);
620  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
621  break;
624  value = get_data<uint32_t>(data, offset);
625  value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
626  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
627  break;
629  value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
630  bitmask); // default is 0xFFFF ;
631  break;
633  value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
634  break;
636  value = get_data<uint32_t>(data, offset);
637  // Currently the high word is at the low position
638  // the sign bit is therefore at low before the switch
639  uint32_t sign_bit = (value & 0x8000) << 16;
641  static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
642  } break;
645  // Ignore bitmask for QWORD
646  value = get_data<uint64_t>(data, offset);
647  break;
650  // Ignore bitmask for QWORD
651  uint64_t tmp = get_data<uint64_t>(data, offset);
652  value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
653  } break;
655  default:
656  break;
657  }
658  return value;
659 }
660 
661 } // namespace modbus_controller
662 } // 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...
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.
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
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