ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
modbus_helpers.cpp
Go to the documentation of this file.
1#include "modbus_helpers.h"
2#include "esphome/core/log.h"
3
5
6static const char *const TAG = "modbus_helpers";
7
8static size_t required_payload_size(SensorValueType sensor_value_type) {
9 switch (sensor_value_type) {
12 return 2;
19 return 4;
24 return 8;
26 default:
27 return 0;
28 }
29}
30
31void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
32 switch (value_type) {
35 data.push_back(value & 0xFFFF);
36 break;
40 data.push_back((value & 0xFFFF0000) >> 16);
41 data.push_back(value & 0xFFFF);
42 break;
46 data.push_back(value & 0xFFFF);
47 data.push_back((value & 0xFFFF0000) >> 16);
48 break;
51 data.push_back((value & 0xFFFF000000000000) >> 48);
52 data.push_back((value & 0xFFFF00000000) >> 32);
53 data.push_back((value & 0xFFFF0000) >> 16);
54 data.push_back(value & 0xFFFF);
55 break;
58 data.push_back(value & 0xFFFF);
59 data.push_back((value & 0xFFFF0000) >> 16);
60 data.push_back((value & 0xFFFF00000000) >> 32);
61 data.push_back((value & 0xFFFF000000000000) >> 48);
62 break;
63 default:
64 ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversion: %d", static_cast<uint16_t>(value_type));
65 break;
66 }
67}
68
69int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
70 uint32_t bitmask) {
71 int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
72
73 // Validate offset against the buffer for all types, including RAW/unsupported, so
74 // a malformed or misconfigured frame still produces an error log.
75 if (static_cast<size_t>(offset) > data.size()) {
76 ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu", static_cast<unsigned int>(sensor_value_type),
77 static_cast<unsigned int>(offset), data.size());
78 return value;
79 }
80
81 const size_t required_size = required_payload_size(sensor_value_type);
82 if (required_size == 0) {
83 return value;
84 }
85
86 if (data.size() - offset < required_size) {
87 ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu required=%zu",
88 static_cast<unsigned int>(sensor_value_type), static_cast<unsigned int>(offset), data.size(),
89 required_size);
90 return value;
91 }
92
93 switch (sensor_value_type) {
95 value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
96 break;
99 value = get_data<uint32_t>(data, offset);
100 value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
101 break;
104 value = get_data<uint32_t>(data, offset);
105 value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
106 value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
107 break;
109 value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset), bitmask); // default is 0xFFFF ;
110 break;
112 value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
113 break;
115 value = get_data<uint32_t>(data, offset);
116 // Currently the high word is at the low position
117 // the sign bit is therefore at low before the switch
118 uint32_t sign_bit = (value & 0x8000) << 16;
120 static_cast<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
121 } break;
124 // Ignore bitmask for QWORD
125 value = get_data<uint64_t>(data, offset);
126 break;
129 // Ignore bitmask for QWORD
130 uint64_t tmp = get_data<uint64_t>(data, offset);
131 value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
132 } break;
134 default:
135 break;
136 }
137 return value;
138}
139} // namespace esphome::modbus::helpers
T get_data(const std::vector< uint8_t > &data, size_t buffer_offset)
Extract data from modbus response buffer.
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...
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.
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.
static void uint32_t