8static const char *
const TAG =
"modbus_helpers";
12 return MIN_FRAME_SIZE;
22 return 5 + (
size > 2 ? std::min(frame[2], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0);
32 return 5 + (
size > 2 ? std::min(frame[2], uint8_t(MAX_FRAME_SIZE - 5)) : 0);
37 return 5 + (
size > 2 ? std::min(frame[2], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0);
42 return MIN_FRAME_SIZE;
48 return MIN_FRAME_SIZE;
61 return 9 + (
size > 6 ? std::min(frame[6], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE * 2)) : 0);
66 return 5 + (
size > 2 ? std::min(frame[2], uint8_t(MAX_FRAME_SIZE - 5)) : 0);
72 return 13 + (
size > 10 ? std::min(frame[10], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE * 2)) : 0);
77 return MIN_FRAME_SIZE;
82 switch (sensor_value_type) {
105 ESP_LOGE(TAG,
"Invalid data type for modbus number to payload conversion: %d",
static_cast<uint16_t
>(value_type));
114 if (
static_cast<size_t>(offset) >
size) {
115 ESP_LOGE(TAG,
"not enough data for value type=%u offset=%u size=%zu",
static_cast<unsigned int>(sensor_value_type),
116 static_cast<unsigned int>(offset),
size);
120 const size_t required_size = required_payload_size(sensor_value_type);
121 if (required_size == 0) {
125 if (
size - offset < required_size) {
126 ESP_LOGE(TAG,
"not enough data for value type=%u offset=%u size=%zu required=%zu",
127 static_cast<unsigned int>(sensor_value_type),
static_cast<unsigned int>(offset),
size, required_size);
131 switch (sensor_value_type) {
143 value =
static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
156 uint32_t sign_bit = (value & 0x8000) << 16;
158 static_cast<int32_t
>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
169 value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000);
179 const size_t required_size = required_payload_size(sensor_value_type);
180 if (required_size == 0) {
183 const size_t required_words = required_size / 2;
184 if (required_words > count) {
185 ESP_LOGE(TAG,
"not enough registers for value type=%u count=%zu required=%zu",
186 static_cast<unsigned int>(sensor_value_type), count, required_words);
192 for (
size_t i = 0; i < required_words; i++) {
193 uint16_t reg = registers[i];
194 bytes[i * 2] =
static_cast<uint8_t
>(reg >> 8);
195 bytes[i * 2 + 1] =
static_cast<uint8_t
>(reg & 0xFF);
197 return payload_to_number(bytes, required_size, sensor_value_type, 0, 0xFFFFFFFF);
201 uint16_t number_of_entities,
const uint8_t *values,
204 if (values !=
nullptr || values_len > 0) {
205 ESP_LOGW(TAG,
"Values provided for read function code %02X, but will be ignored",
206 static_cast<uint8_t
>(function_code));
209 if (values ==
nullptr || values_len == 0) {
210 ESP_LOGE(TAG,
"No values provided for write function code %02X",
static_cast<uint8_t
>(function_code));
214 ESP_LOGE(TAG,
"Unsupported function code %02X for client PDU creation",
static_cast<uint8_t
>(function_code));
218 if (number_of_entities == 0) {
219 ESP_LOGE(TAG,
"Number of entities is zero for function code %02X",
static_cast<uint8_t
>(function_code));
223 switch (function_code) {
225 if (number_of_entities > MAX_NUM_OF_COILS_TO_READ) {
226 ESP_LOGE(TAG,
"number_of_entities %u exceeds maximum coils to read %u for function code %02X",
227 number_of_entities, MAX_NUM_OF_COILS_TO_READ,
static_cast<uint8_t
>(function_code));
232 if (number_of_entities > MAX_NUM_OF_DISCRETE_INPUTS_TO_READ) {
233 ESP_LOGE(TAG,
"number_of_entities %u exceeds maximum discrete inputs to read %u for function code %02X",
234 number_of_entities, MAX_NUM_OF_DISCRETE_INPUTS_TO_READ,
static_cast<uint8_t
>(function_code));
240 if (number_of_entities > MAX_NUM_OF_REGISTERS_TO_READ) {
241 ESP_LOGE(TAG,
"number_of_entities %u exceeds maximum registers to read %u for function code %02X",
242 number_of_entities, MAX_NUM_OF_REGISTERS_TO_READ,
static_cast<uint8_t
>(function_code));
251 if (number_of_entities > MAX_NUM_OF_REGISTERS_TO_WRITE) {
252 ESP_LOGE(TAG,
"number_of_entities %u exceeds maximum registers to write %u for function code %02X",
253 number_of_entities, MAX_NUM_OF_REGISTERS_TO_WRITE,
static_cast<uint8_t
>(function_code));
258 ESP_LOGE(TAG,
"Unsupported function code %u for client PDU creation",
static_cast<unsigned int>(function_code));
263 pdu.
push_back(
static_cast<uint8_t
>(function_code));
276 static constexpr size_t MAX_WRITE_MULTIPLE_VALUES_LEN = MAX_PDU_SIZE - 6;
277 if (values_len > MAX_WRITE_MULTIPLE_VALUES_LEN) {
278 ESP_LOGE(TAG,
"values_len %zu exceeds PDU capacity %zu, dropping request", values_len,
279 MAX_WRITE_MULTIPLE_VALUES_LEN);
283 for (
size_t i = 0; i < values_len; i++)
287 if (values_len < 2) {
288 ESP_LOGE(TAG,
"values_len %zu too small for write-single command (need 2), dropping request", values_len);
Minimal static vector - saves memory by avoiding std::vector overhead.
void push_back(const T &value)
std::span< const uint8_t > data
T get_data(const uint8_t *data, size_t buffer_offset)
Extract data from modbus response buffer.
bool is_function_code_read(uint8_t function_code)
bool is_function_code_write(uint8_t function_code)
uint16_t client_frame_length(const uint8_t *frame, size_t size)
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...
uint16_t server_frame_length(const uint8_t *frame, size_t size)
void log_unsupported_value_type(SensorValueType value_type)
StaticVector< uint8_t, MAX_PDU_SIZE > create_client_pdu(ModbusFunctionCode function_code, uint16_t start_address, uint16_t number_of_entities, const uint8_t *values, size_t values_len)
Create a modbus clinet pdu for reading/writing single/multiple coils/register/inputs.
std::optional< int64_t > registers_to_number(const uint16_t *registers, size_t count, SensorValueType sensor_value_type)
Reconstruct a number from register words (host byte order).
bool is_function_code_exception(uint8_t function_code)
std::optional< int64_t > payload_to_number(const uint8_t *data, size_t size, SensorValueType sensor_value_type, uint8_t offset, uint32_t bitmask)
Convert a raw response payload to a number.
@ WRITE_MULTIPLE_REGISTERS
@ READ_WRITE_MULTIPLE_REGISTERS