#include "modbus_helpers.h" #include "esphome/core/log.h" namespace esphome::modbus::helpers { static const char *const TAG = "modbus_helpers"; static size_t required_payload_size(SensorValueType sensor_value_type) { switch (sensor_value_type) { case SensorValueType::U_WORD: case SensorValueType::S_WORD: return 2; case SensorValueType::U_DWORD: case SensorValueType::FP32: case SensorValueType::U_DWORD_R: case SensorValueType::FP32_R: case SensorValueType::S_DWORD: case SensorValueType::S_DWORD_R: return 4; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: return 8; case SensorValueType::RAW: default: return 0; } } void number_to_payload(std::vector &data, int64_t value, SensorValueType value_type) { switch (value_type) { case SensorValueType::U_WORD: case SensorValueType::S_WORD: data.push_back(value & 0xFFFF); break; case SensorValueType::U_DWORD: case SensorValueType::S_DWORD: case SensorValueType::FP32: data.push_back((value & 0xFFFF0000) >> 16); data.push_back(value & 0xFFFF); break; case SensorValueType::U_DWORD_R: case SensorValueType::S_DWORD_R: case SensorValueType::FP32_R: data.push_back(value & 0xFFFF); data.push_back((value & 0xFFFF0000) >> 16); break; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: data.push_back((value & 0xFFFF000000000000) >> 48); data.push_back((value & 0xFFFF00000000) >> 32); data.push_back((value & 0xFFFF0000) >> 16); data.push_back(value & 0xFFFF); break; case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: data.push_back(value & 0xFFFF); data.push_back((value & 0xFFFF0000) >> 16); data.push_back((value & 0xFFFF00000000) >> 32); data.push_back((value & 0xFFFF000000000000) >> 48); break; default: ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversion: %d", static_cast(value_type)); break; } } int64_t payload_to_number(const std::vector &data, SensorValueType sensor_value_type, uint8_t offset, uint32_t bitmask) { int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits // Validate offset against the buffer for all types, including RAW/unsupported, so // a malformed or misconfigured frame still produces an error log. if (static_cast(offset) > data.size()) { ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu", static_cast(sensor_value_type), static_cast(offset), data.size()); return value; } const size_t required_size = required_payload_size(sensor_value_type); if (required_size == 0) { return value; } if (data.size() - offset < required_size) { ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu required=%zu", static_cast(sensor_value_type), static_cast(offset), data.size(), required_size); return value; } switch (sensor_value_type) { case SensorValueType::U_WORD: value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; break; case SensorValueType::U_DWORD: case SensorValueType::FP32: value = get_data(data, offset); value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); break; case SensorValueType::U_DWORD_R: case SensorValueType::FP32_R: value = get_data(data, offset); value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); break; case SensorValueType::S_WORD: value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; break; case SensorValueType::S_DWORD: value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); break; case SensorValueType::S_DWORD_R: { value = get_data(data, offset); // Currently the high word is at the low position // the sign bit is therefore at low before the switch uint32_t sign_bit = (value & 0x8000) << 16; value = mask_and_shift_by_rightbit( static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); } break; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: // Ignore bitmask for QWORD value = get_data(data, offset); break; case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: { // Ignore bitmask for QWORD uint64_t tmp = get_data(data, offset); value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); } break; case SensorValueType::RAW: default: break; } return value; } } // namespace esphome::modbus::helpers