ESPHome  2024.4.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  // Modbus::setup();
13 }
14 
15 /*
16  To work with the existing modbus class and avoid polling for responses a command queue is used.
17  send_next_command will submit the command at the top of the queue and set the corresponding callback
18  to handle the response from the device.
19  Once the response has been processed it is removed from the queue and the next command is sent
20 */
22  uint32_t last_send = millis() - this->last_command_timestamp_;
23 
24  if ((last_send > this->command_throttle_) && !waiting_for_response() && !command_queue_.empty()) {
25  auto &command = command_queue_.front();
26 
27  // remove from queue if command was sent too often
28  if (command->send_countdown < 1) {
29  if (!this->module_offline_) {
30  ESP_LOGW(TAG, "Modbus device=%d set offline", this->address_);
31 
32  if (this->offline_skip_updates_ > 0) {
33  // Update skip_updates_counter to stop flooding channel with timeouts
34  for (auto &r : this->register_ranges_) {
35  r.skip_updates_counter = this->offline_skip_updates_;
36  }
37  }
38  }
39  this->module_offline_ = true;
40  ESP_LOGD(
41  TAG,
42  "Modbus command to device=%d register=0x%02X countdown=%d no response received - removed from send queue",
43  this->address_, command->register_address, command->send_countdown);
44  command_queue_.pop_front();
45  } else {
46  ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_,
47  command->register_address, command->register_count);
48  command->send();
50  // remove from queue if no handler is defined
51  if (!command->on_data_func) {
52  command_queue_.pop_front();
53  }
54  }
55  }
56  return (!command_queue_.empty());
57 }
58 
59 // Queue incoming response
60 void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
61  auto &current_command = this->command_queue_.front();
62  if (current_command != nullptr) {
63  if (this->module_offline_) {
64  ESP_LOGW(TAG, "Modbus device=%d back online", this->address_);
65 
66  if (this->offline_skip_updates_ > 0) {
67  // Restore skip_updates_counter to restore commands updates
68  for (auto &r : this->register_ranges_) {
69  r.skip_updates_counter = 0;
70  }
71  }
72  }
73  this->module_offline_ = false;
74 
75  // Move the commandItem to the response queue
76  current_command->payload = data;
77  this->incoming_queue_.push(std::move(current_command));
78  ESP_LOGV(TAG, "Modbus response queued");
79  command_queue_.pop_front();
80  }
81 }
82 
83 // Dispatch the response to the registered handler
85  ESP_LOGV(TAG, "Process modbus response for address 0x%X size: %zu", response->register_address,
86  response->payload.size());
87  response->on_data_func(response->register_type, response->register_address, response->payload);
88 }
89 
90 void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) {
91  ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
92  // Remove pending command waiting for a response
93  auto &current_command = this->command_queue_.front();
94  if (current_command != nullptr) {
95  ESP_LOGE(TAG,
96  "Modbus error - last command: function code=0x%X register address = 0x%X "
97  "registers count=%d "
98  "payload size=%zu",
99  function_code, current_command->register_address, current_command->register_count,
100  current_command->payload.size());
101  command_queue_.pop_front();
102  }
103 }
104 
105 SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
106  auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) {
107  return (r.start_address == start_address && r.register_type == register_type);
108  });
109 
110  if (reg_it == register_ranges_.end()) {
111  ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
112  } else {
113  return reg_it->sensors;
114  }
115 
116  // not found
117  return {};
118 }
119 void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address,
120  const std::vector<uint8_t> &data) {
121  ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address);
122 
123  // loop through all sensors with the same start address
124  auto sensors = find_sensors_(register_type, start_address);
125  for (auto *sensor : sensors) {
126  sensor->parse_and_publish(data);
127  }
128 }
129 
131  // check if this command is already qeued.
132  // not very effective but the queue is never really large
133  for (auto &item : command_queue_) {
134  if (item->is_equal(command)) {
135  ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u",
136  static_cast<uint8_t>(command.register_type), command.register_address, command.register_count);
137  // update the payload of the queued command
138  // replaces a previous command
139  item->payload = command.payload;
140  return;
141  }
142  }
143  command_queue_.push_back(make_unique<ModbusCommandItem>(command));
144 }
145 
147  ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type,
149  if (r.skip_updates_counter == 0) {
150  // if a custom command is used the user supplied custom_data is only available in the SensorItem.
152  auto sensors = this->find_sensors_(r.register_type, r.start_address);
153  if (!sensors.empty()) {
154  auto sensor = sensors.cbegin();
155  auto command_item = ModbusCommandItem::create_custom_command(
156  this, (*sensor)->custom_data,
157  [this](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
158  this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
159  });
160  command_item.register_address = (*sensor)->start_address;
161  command_item.register_count = (*sensor)->register_count;
162  command_item.function_code = ModbusFunctionCode::CUSTOM;
163  queue_command(command_item);
164  }
165  } else {
167  }
168  r.skip_updates_counter = r.skip_updates; // reset counter to config value
169  } else {
171  }
172 }
173 //
174 // Queue the modbus requests to be send.
175 // Once we get a response to the command it is removed from the queue and the next command is send
176 //
178  if (!command_queue_.empty()) {
179  ESP_LOGV(TAG, "%zu modbus commands already in queue", command_queue_.size());
180  } else {
181  ESP_LOGV(TAG, "Updating modbus component");
182  }
183 
184  for (auto &r : this->register_ranges_) {
185  ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address);
186  update_range_(r);
187  }
188 }
189 
190 // walk through the sensors and determine the register ranges to read
192  register_ranges_.clear();
193  if (sensorset_.empty()) {
194  ESP_LOGW(TAG, "No sensors registered");
195  return 0;
196  }
197 
198  // iterator is sorted see SensorItemsComparator for details
199  auto ix = sensorset_.begin();
200  RegisterRange r = {};
201  uint8_t buffer_offset = 0;
202  SensorItem *prev = nullptr;
203  while (ix != sensorset_.end()) {
204  SensorItem *curr = *ix;
205 
206  ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
207  curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr);
208 
209  if (r.register_count == 0) {
210  // this is the first register in range
211  r.start_address = curr->start_address;
212  r.register_count = curr->register_count;
213  r.register_type = curr->register_type;
214  r.sensors.insert(curr);
215  r.skip_updates = curr->skip_updates;
216  r.skip_updates_counter = 0;
217  buffer_offset = curr->get_register_size();
218 
219  ESP_LOGV(TAG, "Started new range");
220  } else {
221  // this is not the first register in range so it might be possible
222  // to reuse the last register or extend the current range
223  if (!curr->force_new_range && r.register_type == curr->register_type &&
225  if (curr->start_address == (r.start_address + r.register_count - prev->register_count) &&
226  curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) {
227  // this register can re-use the data from the previous register
228 
229  // remove this sensore because start_address is changed (sort-order)
230  ix = sensorset_.erase(ix);
231 
232  curr->start_address = r.start_address;
233  curr->offset += prev->offset;
234 
235  sensorset_.insert(curr);
236  // move iterator backwards because it will be incremented later
237  ix--;
238 
239  ESP_LOGV(TAG, "Re-use previous register - change to register: 0x%X %d offset=%u", curr->start_address,
240  curr->register_count, curr->offset);
241  } else if (curr->start_address == (r.start_address + r.register_count)) {
242  // this register can extend the current range
243 
244  // remove this sensore because start_address is changed (sort-order)
245  ix = sensorset_.erase(ix);
246 
247  curr->start_address = r.start_address;
248  curr->offset += buffer_offset;
249  buffer_offset += curr->get_register_size();
250  r.register_count += curr->register_count;
251 
252  sensorset_.insert(curr);
253  // move iterator backwards because it will be incremented later
254  ix--;
255 
256  ESP_LOGV(TAG, "Extend range - change to register: 0x%X %d offset=%u", curr->start_address,
257  curr->register_count, curr->offset);
258  }
259  }
260  }
261 
262  if (curr->start_address == r.start_address && curr->register_type == r.register_type) {
263  // use the lowest non zero value for the whole range
264  // Because zero is the default value for skip_updates it is excluded from getting the min value.
265  if (curr->skip_updates != 0) {
266  if (r.skip_updates != 0) {
267  r.skip_updates = std::min(r.skip_updates, curr->skip_updates);
268  } else {
269  r.skip_updates = curr->skip_updates;
270  }
271  }
272 
273  // add sensor to this range
274  r.sensors.insert(curr);
275 
276  ix++;
277  } else {
278  ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
279  register_ranges_.push_back(r);
280  r = {};
281  buffer_offset = 0;
282  // do not increment the iterator here because the current sensor has to be re-evaluated
283  }
284 
285  prev = curr;
286  }
287 
288  if (r.register_count > 0) {
289  // Add the last range
290  ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
291  register_ranges_.push_back(r);
292  }
293 
294  return register_ranges_.size();
295 }
296 
298  ESP_LOGCONFIG(TAG, "ModbusController:");
299  ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
300 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
301  ESP_LOGCONFIG(TAG, "sensormap");
302  for (auto &it : sensorset_) {
303  ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
304  static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
305  it->get_register_size());
306  }
307  ESP_LOGCONFIG(TAG, "ranges");
308  for (auto &it : register_ranges_) {
309  ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
310  it.start_address, it.register_count, it.skip_updates);
311  }
312 #endif
313 }
314 
316  // Incoming data to process?
317  if (!incoming_queue_.empty()) {
318  auto &message = incoming_queue_.front();
319  if (message != nullptr)
320  process_modbus_data_(message.get());
321  incoming_queue_.pop();
322 
323  } else {
324  // all messages processed send pending commands
326  }
327 }
328 
329 void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address,
330  const std::vector<uint8_t> &data) {
331  ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
332 }
333 
335  ESP_LOGV(TAG, "sensors");
336  for (auto &it : sensorset_) {
337  ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
338  it->get_register_size(), it->offset);
339  }
340 }
341 
343  ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count,
344  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
345  &&handler) {
347  cmd.modbusdevice = modbusdevice;
348  cmd.register_type = register_type;
349  cmd.function_code = modbus_register_read_function(register_type);
350  cmd.register_address = start_address;
351  cmd.register_count = register_count;
352  cmd.on_data_func = std::move(handler);
353  return cmd;
354 }
355 
357  ModbusRegisterType register_type, uint16_t start_address,
358  uint16_t register_count) {
360  cmd.modbusdevice = modbusdevice;
361  cmd.register_type = register_type;
362  cmd.function_code = modbus_register_read_function(register_type);
363  cmd.register_address = start_address;
364  cmd.register_count = register_count;
365  cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
366  const std::vector<uint8_t> &data) {
367  modbusdevice->on_register_data(register_type, start_address, data);
368  };
369  return cmd;
370 }
371 
373  uint16_t start_address, uint16_t register_count,
374  const std::vector<uint16_t> &values) {
376  cmd.modbusdevice = modbusdevice;
379  cmd.register_address = start_address;
380  cmd.register_count = register_count;
381  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
382  const std::vector<uint8_t> &data) {
383  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
384  };
385  for (auto v : values) {
386  auto decoded_value = decode_value(v);
387  cmd.payload.push_back(decoded_value[0]);
388  cmd.payload.push_back(decoded_value[1]);
389  }
390  return cmd;
391 }
392 
394  bool value) {
396  cmd.modbusdevice = modbusdevice;
399  cmd.register_address = address;
400  cmd.register_count = 1;
401  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
402  const std::vector<uint8_t> &data) {
403  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
404  };
405  cmd.payload.push_back(value ? 0xFF : 0);
406  cmd.payload.push_back(0);
407  return cmd;
408 }
409 
411  const std::vector<bool> &values) {
413  cmd.modbusdevice = modbusdevice;
416  cmd.register_address = start_address;
417  cmd.register_count = values.size();
418  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
419  const std::vector<uint8_t> &data) {
420  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
421  };
422 
423  uint8_t bitmask = 0;
424  int bitcounter = 0;
425  for (auto coil : values) {
426  if (coil) {
427  bitmask |= (1 << bitcounter);
428  }
429  bitcounter++;
430  if (bitcounter % 8 == 0) {
431  cmd.payload.push_back(bitmask);
432  bitmask = 0;
433  }
434  }
435  // add remaining bits
436  if (bitcounter % 8) {
437  cmd.payload.push_back(bitmask);
438  }
439  return cmd;
440 }
441 
443  uint16_t value) {
445  cmd.modbusdevice = modbusdevice;
448  cmd.register_address = start_address;
449  cmd.register_count = 1; // not used here anyways
450  cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
451  const std::vector<uint8_t> &data) {
452  modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
453  };
454 
455  auto decoded_value = decode_value(value);
456  cmd.payload.push_back(decoded_value[0]);
457  cmd.payload.push_back(decoded_value[1]);
458  return cmd;
459 }
460 
462  ModbusController *modbusdevice, const std::vector<uint8_t> &values,
463  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
464  &&handler) {
466  cmd.modbusdevice = modbusdevice;
468  if (handler == nullptr) {
469  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
470  ESP_LOGI(TAG, "Custom Command sent");
471  };
472  } else {
473  cmd.on_data_func = handler;
474  }
475  cmd.payload = values;
476 
477  return cmd;
478 }
479 
481  ModbusController *modbusdevice, const std::vector<uint16_t> &values,
482  std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
483  &&handler) {
484  ModbusCommandItem cmd = {};
485  cmd.modbusdevice = modbusdevice;
487  if (handler == nullptr) {
488  cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
489  ESP_LOGI(TAG, "Custom Command sent");
490  };
491  } else {
492  cmd.on_data_func = handler;
493  }
494  for (auto v : values) {
495  cmd.payload.push_back((v >> 8) & 0xFF);
496  cmd.payload.push_back(v & 0xFF);
497  }
498 
499  return cmd;
500 }
501 
503  if (this->function_code != ModbusFunctionCode::CUSTOM) {
504  modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
505  this->payload.empty() ? nullptr : &this->payload[0]);
506  } else {
507  modbusdevice->send_raw(this->payload);
508  }
509  ESP_LOGV(TAG, "Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count);
510  send_countdown--;
511  return true;
512 }
513 
515  // for custom commands we have to check for identical payloads, since
516  // address/count/type fields will be set to zero
517  return this->function_code == ModbusFunctionCode::CUSTOM
518  ? this->payload == other.payload
519  : other.register_address == this->register_address && other.register_count == this->register_count &&
520  other.register_type == this->register_type && other.function_code == this->function_code;
521 }
522 
523 void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
524  switch (value_type) {
527  data.push_back(value & 0xFFFF);
528  break;
532  data.push_back((value & 0xFFFF0000) >> 16);
533  data.push_back(value & 0xFFFF);
534  break;
538  data.push_back(value & 0xFFFF);
539  data.push_back((value & 0xFFFF0000) >> 16);
540  break;
543  data.push_back((value & 0xFFFF000000000000) >> 48);
544  data.push_back((value & 0xFFFF00000000) >> 32);
545  data.push_back((value & 0xFFFF0000) >> 16);
546  data.push_back(value & 0xFFFF);
547  break;
550  data.push_back(value & 0xFFFF);
551  data.push_back((value & 0xFFFF0000) >> 16);
552  data.push_back((value & 0xFFFF00000000) >> 32);
553  data.push_back((value & 0xFFFF000000000000) >> 48);
554  break;
555  default:
556  ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversation: %d",
557  static_cast<uint16_t>(value_type));
558  break;
559  }
560 }
561 
562 int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
563  uint32_t bitmask) {
564  int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
565 
566  switch (sensor_value_type) {
568  value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
569  break;
572  value = get_data<uint32_t>(data, offset);
573  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
574  break;
577  value = get_data<uint32_t>(data, offset);
578  value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
579  value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
580  break;
582  value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
583  bitmask); // default is 0xFFFF ;
584  break;
586  value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
587  break;
589  value = get_data<uint32_t>(data, offset);
590  // Currently the high word is at the low position
591  // the sign bit is therefore at low before the switch
592  uint32_t sign_bit = (value & 0x8000) << 16;
594  static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
595  } break;
598  // Ignore bitmask for QWORD
599  value = get_data<uint64_t>(data, offset);
600  break;
603  // Ignore bitmask for QWORD
604  uint64_t tmp = get_data<uint64_t>(data, offset);
605  value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
606  } break;
608  default:
609  break;
610  }
611  return value;
612 }
613 
614 } // namespace modbus_controller
615 } // namespace esphome
void queue_command(const ModbusCommandItem &command)
queues a modbus command in the send queue
bool module_offline_
if module didn&#39;t respond the last command
N mask_and_shift_by_rightbit(N data, uint32_t mask)
Extract bits from value and shift right according to the bitmask if the bitmask is 0x00F0 we want the...
void 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
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:57
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:53
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