ESPHome 2026.4.0-dev
Loading...
Searching...
No Matches
modbus_helpers.h
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <vector>
5#include <cmath>
6
9
11
12enum class SensorValueType : uint8_t {
13 RAW = 0x00, // variable length
14 U_WORD = 0x1, // 1 Register unsigned
15 U_DWORD = 0x2, // 2 Registers unsigned
16 S_WORD = 0x3, // 1 Register signed
17 S_DWORD = 0x4, // 2 Registers signed
18 BIT = 0x5,
19 U_DWORD_R = 0x6, // 2 Registers unsigned
20 S_DWORD_R = 0x7, // 2 Registers unsigned
21 U_QWORD = 0x8,
22 S_QWORD = 0x9,
23 U_QWORD_R = 0xA,
24 S_QWORD_R = 0xB,
25 FP32 = 0xC,
26 FP32_R = 0xD
27};
28
32
47
61
62inline uint8_t c_to_hex(char c) { return (c >= 'A') ? (c >= 'a') ? (c - 'a' + 10) : (c - 'A' + 10) : (c - '0'); }
63
72inline uint8_t byte_from_hex_str(const std::string &value, uint8_t pos) {
73 if (value.length() < pos * 2 + 2)
74 return 0;
75 return (c_to_hex(value[pos * 2]) << 4) | c_to_hex(value[pos * 2 + 1]);
76}
77
84inline uint16_t word_from_hex_str(const std::string &value, uint8_t pos) {
85 return byte_from_hex_str(value, pos) << 8 | byte_from_hex_str(value, pos + 1);
86}
87
94inline uint32_t dword_from_hex_str(const std::string &value, uint8_t pos) {
95 return word_from_hex_str(value, pos) << 16 | word_from_hex_str(value, pos + 2);
96}
97
104inline uint64_t qword_from_hex_str(const std::string &value, uint8_t pos) {
105 return static_cast<uint64_t>(dword_from_hex_str(value, pos)) << 32 | dword_from_hex_str(value, pos + 4);
106}
107
108// Extract data from modbus response buffer
115template<typename T> T get_data(const std::vector<uint8_t> &data, size_t buffer_offset) {
116 if (sizeof(T) == sizeof(uint8_t)) {
117 return T(data[buffer_offset]);
118 }
119 if (sizeof(T) == sizeof(uint16_t)) {
120 return T((uint16_t(data[buffer_offset + 0]) << 8) | (uint16_t(data[buffer_offset + 1]) << 0));
121 }
122
123 if (sizeof(T) == sizeof(uint32_t)) {
124 return static_cast<uint32_t>(get_data<uint16_t>(data, buffer_offset)) << 16 |
125 static_cast<uint32_t>(get_data<uint16_t>(data, buffer_offset + 2));
126 }
127
128 if (sizeof(T) == sizeof(uint64_t)) {
129 return static_cast<uint64_t>(get_data<uint32_t>(data, buffer_offset)) << 32 |
130 (static_cast<uint64_t>(get_data<uint32_t>(data, buffer_offset + 4)));
131 }
132
133 static_assert(sizeof(T) == sizeof(uint8_t) || sizeof(T) == sizeof(uint16_t) || sizeof(T) == sizeof(uint32_t) ||
134 sizeof(T) == sizeof(uint64_t),
135 "Unsupported type size in get_data; only 1, 2, 4, or 8-byte integer types are supported.");
136
137 return T{};
138}
139
148inline bool coil_from_vector(int coil, const std::vector<uint8_t> &data) {
149 auto data_byte = coil / 8;
150 return (data[data_byte] & (1 << (coil % 8))) > 0;
151}
152
163template<typename N> N mask_and_shift_by_rightbit(N data, uint32_t mask) {
164 auto result = (mask & data);
165 if (result == 0 || mask == 0xFFFFFFFF) {
166 return result;
167 }
168 for (size_t pos = 0; pos < sizeof(N) << 3; pos++) {
169 if (pos < 32 && (mask & (1UL << pos)) != 0)
170 return result >> pos;
171 }
172 return 0;
173}
174
181void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type);
182
190int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
191 uint32_t bitmask);
192
193inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
194 int64_t val;
195
196 if (value_type_is_float(value_type)) {
197 val = bit_cast<uint32_t>(value);
198 } else {
199 val = llroundf(value);
200 }
201
202 std::vector<uint16_t> data;
203 number_to_payload(data, val, value_type);
204 return data;
205}
206
207} // namespace esphome::modbus::helpers
mopeka_std_values val[3]
ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type)
ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type)
bool value_type_is_float(SensorValueType v)
bool coil_from_vector(int coil, const std::vector< uint8_t > &data)
Extract coil data from modbus response buffer Responses for coil are packed into bytes .
std::vector< uint16_t > float_to_payload(float value, SensorValueType value_type)
T get_data(const std::vector< uint8_t > &data, size_t buffer_offset)
Extract data from modbus response buffer.
uint64_t qword_from_hex_str(const std::string &value, uint8_t pos)
Get a qword from a hex string.
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...
uint32_t dword_from_hex_str(const std::string &value, uint8_t pos)
Get a dword from a hex string.
uint8_t byte_from_hex_str(const std::string &value, uint8_t pos)
Get a byte from a hex string byte_from_hex_str("1122", 1) returns uint_8 value 0x22 == 34 byte_from_h...
uint16_t word_from_hex_str(const std::string &value, uint8_t pos)
Get a word from a hex string.
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.
size_t size_t pos
Definition helpers.h:1082
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:77
static void uint32_t