mirror of
https://github.com/esphome/esphome.git
synced 2024-12-02 19:54:14 +01:00
600 lines
23 KiB
C++
600 lines
23 KiB
C++
#include "modbus_controller.h"
|
|
#include "esphome/core/application.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
namespace esphome {
|
|
namespace modbus_controller {
|
|
|
|
static const char *const TAG = "modbus_controller";
|
|
|
|
void ModbusController::setup() {
|
|
// Modbus::setup();
|
|
this->create_register_ranges_();
|
|
}
|
|
|
|
/*
|
|
To work with the existing modbus class and avoid polling for responses a command queue is used.
|
|
send_next_command will submit the command at the top of the queue and set the corresponding callback
|
|
to handle the response from the device.
|
|
Once the response has been processed it is removed from the queue and the next command is sent
|
|
*/
|
|
bool ModbusController::send_next_command_() {
|
|
uint32_t last_send = millis() - this->last_command_timestamp_;
|
|
|
|
if ((last_send > this->command_throttle_) && !waiting_for_response() && !command_queue_.empty()) {
|
|
auto &command = command_queue_.front();
|
|
|
|
// remove from queue if command was sent too often
|
|
if (command->send_countdown < 1) {
|
|
ESP_LOGD(
|
|
TAG,
|
|
"Modbus command to device=%d register=0x%02X countdown=%d no response received - removed from send queue",
|
|
this->address_, command->register_address, command->send_countdown);
|
|
command_queue_.pop_front();
|
|
} else {
|
|
ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_,
|
|
command->register_address, command->register_count);
|
|
command->send();
|
|
this->last_command_timestamp_ = millis();
|
|
// remove from queue if no handler is defined
|
|
if (!command->on_data_func) {
|
|
command_queue_.pop_front();
|
|
}
|
|
}
|
|
}
|
|
return (!command_queue_.empty());
|
|
}
|
|
|
|
// Queue incoming response
|
|
void ModbusController::on_modbus_data(const std::vector<uint8_t> &data) {
|
|
auto ¤t_command = this->command_queue_.front();
|
|
if (current_command != nullptr) {
|
|
// Move the commandItem to the response queue
|
|
current_command->payload = data;
|
|
this->incoming_queue_.push(std::move(current_command));
|
|
ESP_LOGV(TAG, "Modbus response queued");
|
|
command_queue_.pop_front();
|
|
}
|
|
}
|
|
|
|
// Dispatch the response to the registered handler
|
|
void ModbusController::process_modbus_data_(const ModbusCommandItem *response) {
|
|
ESP_LOGV(TAG, "Process modbus response for address 0x%X size: %zu", response->register_address,
|
|
response->payload.size());
|
|
response->on_data_func(response->register_type, response->register_address, response->payload);
|
|
}
|
|
|
|
void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) {
|
|
ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code);
|
|
// Remove pending command waiting for a response
|
|
auto ¤t_command = this->command_queue_.front();
|
|
if (current_command != nullptr) {
|
|
ESP_LOGE(TAG,
|
|
"Modbus error - last command: function code=0x%X register adddress = 0x%X "
|
|
"registers count=%d "
|
|
"payload size=%zu",
|
|
function_code, current_command->register_address, current_command->register_count,
|
|
current_command->payload.size());
|
|
command_queue_.pop_front();
|
|
}
|
|
}
|
|
|
|
SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
|
|
auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) {
|
|
return (r.start_address == start_address && r.register_type == register_type);
|
|
});
|
|
|
|
if (reg_it == register_ranges_.end()) {
|
|
ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
|
|
} else {
|
|
return reg_it->sensors;
|
|
}
|
|
|
|
// not found
|
|
return {};
|
|
}
|
|
void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address);
|
|
|
|
// loop through all sensors with the same start address
|
|
auto sensors = find_sensors_(register_type, start_address);
|
|
for (auto sensor : sensors) {
|
|
sensor->parse_and_publish(data);
|
|
}
|
|
}
|
|
|
|
void ModbusController::queue_command(const ModbusCommandItem &command) {
|
|
// check if this commmand is already qeued.
|
|
// not very effective but the queue is never really large
|
|
for (auto &item : command_queue_) {
|
|
if (item->register_address == command.register_address && item->register_count == command.register_count &&
|
|
item->register_type == command.register_type && item->function_code == command.function_code) {
|
|
ESP_LOGW(TAG, "Duplicate modbus command found");
|
|
// update the payload of the queued command
|
|
// replaces a previous command
|
|
item->payload = command.payload;
|
|
return;
|
|
}
|
|
}
|
|
command_queue_.push_back(make_unique<ModbusCommandItem>(command));
|
|
}
|
|
|
|
void ModbusController::update_range_(RegisterRange &r) {
|
|
ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type,
|
|
r.skip_updates_counter);
|
|
if (r.skip_updates_counter == 0) {
|
|
// if a custom command is used the user supplied custom_data is only available in the SensorItem.
|
|
if (r.register_type == ModbusRegisterType::CUSTOM) {
|
|
auto sensors = this->find_sensors_(r.register_type, r.start_address);
|
|
if (!sensors.empty()) {
|
|
auto sensor = sensors.cbegin();
|
|
auto command_item = ModbusCommandItem::create_custom_command(
|
|
this, (*sensor)->custom_data,
|
|
[this](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
|
|
this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data);
|
|
});
|
|
command_item.register_address = (*sensor)->start_address;
|
|
command_item.register_count = (*sensor)->register_count;
|
|
command_item.function_code = ModbusFunctionCode::CUSTOM;
|
|
queue_command(command_item);
|
|
}
|
|
} else {
|
|
queue_command(ModbusCommandItem::create_read_command(this, r.register_type, r.start_address, r.register_count));
|
|
}
|
|
r.skip_updates_counter = r.skip_updates; // reset counter to config value
|
|
} else {
|
|
r.skip_updates_counter--;
|
|
}
|
|
}
|
|
//
|
|
// Queue the modbus requests to be send.
|
|
// Once we get a response to the command it is removed from the queue and the next command is send
|
|
//
|
|
void ModbusController::update() {
|
|
if (!command_queue_.empty()) {
|
|
ESP_LOGV(TAG, "%zu modbus commands already in queue", command_queue_.size());
|
|
} else {
|
|
ESP_LOGV(TAG, "Updating modbus component");
|
|
}
|
|
|
|
for (auto &r : this->register_ranges_) {
|
|
ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address);
|
|
update_range_(r);
|
|
}
|
|
}
|
|
|
|
// walk through the sensors and determine the register ranges to read
|
|
size_t ModbusController::create_register_ranges_() {
|
|
register_ranges_.clear();
|
|
if (sensorset_.empty()) {
|
|
ESP_LOGW(TAG, "No sensors registered");
|
|
return 0;
|
|
}
|
|
|
|
// iterator is sorted see SensorItemsComparator for details
|
|
auto ix = sensorset_.begin();
|
|
RegisterRange r = {};
|
|
uint8_t buffer_offset = 0;
|
|
SensorItem *prev = nullptr;
|
|
while (ix != sensorset_.end()) {
|
|
SensorItem *curr = *ix;
|
|
|
|
ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
|
|
curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr);
|
|
|
|
if (r.register_count == 0) {
|
|
// this is the first register in range
|
|
r.start_address = curr->start_address;
|
|
r.register_count = curr->register_count;
|
|
r.register_type = curr->register_type;
|
|
r.sensors.insert(curr);
|
|
r.skip_updates = curr->skip_updates;
|
|
r.skip_updates_counter = 0;
|
|
buffer_offset = curr->get_register_size();
|
|
|
|
ESP_LOGV(TAG, "Started new range");
|
|
} else {
|
|
// this is not the first register in range so it might be possible
|
|
// to reuse the last register or extend the current range
|
|
if (!curr->force_new_range && r.register_type == curr->register_type &&
|
|
curr->register_type != ModbusRegisterType::CUSTOM) {
|
|
if (curr->start_address == (r.start_address + r.register_count - prev->register_count) &&
|
|
curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) {
|
|
// this register can re-use the data from the previous register
|
|
|
|
// remove this sensore because start_address is changed (sort-order)
|
|
ix = sensorset_.erase(ix);
|
|
|
|
curr->start_address = r.start_address;
|
|
curr->offset += prev->offset;
|
|
|
|
sensorset_.insert(curr);
|
|
// move iterator backwards because it will be incremented later
|
|
ix--;
|
|
|
|
ESP_LOGV(TAG, "Re-use previous register - change to register: 0x%X %d offset=%u", curr->start_address,
|
|
curr->register_count, curr->offset);
|
|
} else if (curr->start_address == (r.start_address + r.register_count)) {
|
|
// this register can extend the current range
|
|
|
|
// remove this sensore because start_address is changed (sort-order)
|
|
ix = sensorset_.erase(ix);
|
|
|
|
curr->start_address = r.start_address;
|
|
curr->offset += buffer_offset;
|
|
buffer_offset += curr->get_register_size();
|
|
r.register_count += curr->register_count;
|
|
|
|
sensorset_.insert(curr);
|
|
// move iterator backwards because it will be incremented later
|
|
ix--;
|
|
|
|
ESP_LOGV(TAG, "Extend range - change to register: 0x%X %d offset=%u", curr->start_address,
|
|
curr->register_count, curr->offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (curr->start_address == r.start_address) {
|
|
// use the lowest non zero value for the whole range
|
|
// Because zero is the default value for skip_updates it is excluded from getting the min value.
|
|
if (curr->skip_updates != 0) {
|
|
if (r.skip_updates != 0) {
|
|
r.skip_updates = std::min(r.skip_updates, curr->skip_updates);
|
|
} else {
|
|
r.skip_updates = curr->skip_updates;
|
|
}
|
|
}
|
|
|
|
// add sensor to this range
|
|
r.sensors.insert(curr);
|
|
|
|
ix++;
|
|
} else {
|
|
ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
|
|
register_ranges_.push_back(r);
|
|
r = {};
|
|
buffer_offset = 0;
|
|
// do not increment the iterator here because the current sensor has to be re-evaluated
|
|
}
|
|
|
|
prev = curr;
|
|
}
|
|
|
|
if (r.register_count > 0) {
|
|
// Add the last range
|
|
ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
|
|
register_ranges_.push_back(r);
|
|
}
|
|
|
|
return register_ranges_.size();
|
|
}
|
|
|
|
void ModbusController::dump_config() {
|
|
ESP_LOGCONFIG(TAG, "ModbusController:");
|
|
ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
|
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
|
ESP_LOGCONFIG(TAG, "sensormap");
|
|
for (auto &it : sensorset_) {
|
|
ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
|
|
static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
|
|
it->get_register_size());
|
|
}
|
|
ESP_LOGCONFIG(TAG, "ranges");
|
|
for (auto &it : register_ranges_) {
|
|
ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
|
|
it.start_address, it.register_count, it.skip_updates);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ModbusController::loop() {
|
|
// Incoming data to process?
|
|
if (!incoming_queue_.empty()) {
|
|
auto &message = incoming_queue_.front();
|
|
if (message != nullptr)
|
|
process_modbus_data_(message.get());
|
|
incoming_queue_.pop();
|
|
|
|
} else {
|
|
// all messages processed send pending commmands
|
|
send_next_command_();
|
|
}
|
|
}
|
|
|
|
void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data<uint16_t>(data, 0), get_data<int16_t>(data, 1));
|
|
}
|
|
|
|
void ModbusController::dump_sensors_() {
|
|
ESP_LOGV(TAG, "sensors");
|
|
for (auto &it : sensorset_) {
|
|
ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
|
|
it->get_register_size(), it->offset);
|
|
}
|
|
}
|
|
|
|
ModbusCommandItem 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) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = register_type;
|
|
cmd.function_code = modbus_register_read_function(register_type);
|
|
cmd.register_address = start_address;
|
|
cmd.register_count = register_count;
|
|
cmd.on_data_func = std::move(handler);
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem ModbusCommandItem::create_read_command(ModbusController *modbusdevice,
|
|
ModbusRegisterType register_type, uint16_t start_address,
|
|
uint16_t register_count) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = register_type;
|
|
cmd.function_code = modbus_register_read_function(register_type);
|
|
cmd.register_address = start_address;
|
|
cmd.register_count = register_count;
|
|
cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
modbusdevice->on_register_data(register_type, start_address, data);
|
|
};
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem ModbusCommandItem::create_write_multiple_command(ModbusController *modbusdevice,
|
|
uint16_t start_address, uint16_t register_count,
|
|
const std::vector<uint16_t> &values) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = ModbusRegisterType::HOLDING;
|
|
cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS;
|
|
cmd.register_address = start_address;
|
|
cmd.register_count = register_count;
|
|
cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
|
|
};
|
|
for (auto v : values) {
|
|
cmd.payload.push_back((v / 256) & 0xFF);
|
|
cmd.payload.push_back(v & 0xFF);
|
|
}
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem ModbusCommandItem::create_write_single_coil(ModbusController *modbusdevice, uint16_t address,
|
|
bool value) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = ModbusRegisterType::COIL;
|
|
cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_COIL;
|
|
cmd.register_address = address;
|
|
cmd.register_count = 1;
|
|
cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
|
|
};
|
|
cmd.payload.push_back(value ? 0xFF : 0);
|
|
cmd.payload.push_back(0);
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem ModbusCommandItem::create_write_multiple_coils(ModbusController *modbusdevice, uint16_t start_address,
|
|
const std::vector<bool> &values) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = ModbusRegisterType::COIL;
|
|
cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_COILS;
|
|
cmd.register_address = start_address;
|
|
cmd.register_count = values.size();
|
|
cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
|
|
};
|
|
|
|
uint8_t bitmask = 0;
|
|
int bitcounter = 0;
|
|
for (auto coil : values) {
|
|
if (coil) {
|
|
bitmask |= (1 << bitcounter);
|
|
}
|
|
bitcounter++;
|
|
if (bitcounter % 8 == 0) {
|
|
cmd.payload.push_back(bitmask);
|
|
bitmask = 0;
|
|
}
|
|
}
|
|
// add remaining bits
|
|
if (bitcounter % 8) {
|
|
cmd.payload.push_back(bitmask);
|
|
}
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusController *modbusdevice, uint16_t start_address,
|
|
int16_t value) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.register_type = ModbusRegisterType::HOLDING;
|
|
cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_REGISTER;
|
|
cmd.register_address = start_address;
|
|
cmd.register_count = 1; // not used here anyways
|
|
cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address,
|
|
const std::vector<uint8_t> &data) {
|
|
modbusdevice->on_write_register_response(cmd.register_type, start_address, data);
|
|
};
|
|
cmd.payload.push_back((value / 256) & 0xFF);
|
|
cmd.payload.push_back((value % 256) & 0xFF);
|
|
return cmd;
|
|
}
|
|
|
|
ModbusCommandItem 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) {
|
|
ModbusCommandItem cmd;
|
|
cmd.modbusdevice = modbusdevice;
|
|
cmd.function_code = ModbusFunctionCode::CUSTOM;
|
|
if (handler == nullptr) {
|
|
cmd.on_data_func = [](ModbusRegisterType, uint16_t, const std::vector<uint8_t> &data) {
|
|
ESP_LOGI(TAG, "Custom Command sent");
|
|
};
|
|
} else {
|
|
cmd.on_data_func = handler;
|
|
}
|
|
cmd.payload = values;
|
|
|
|
return cmd;
|
|
}
|
|
|
|
bool ModbusCommandItem::send() {
|
|
if (this->function_code != ModbusFunctionCode::CUSTOM) {
|
|
modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
|
|
this->payload.empty() ? nullptr : &this->payload[0]);
|
|
} else {
|
|
modbusdevice->send_raw(this->payload);
|
|
}
|
|
ESP_LOGV(TAG, "Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count);
|
|
send_countdown--;
|
|
return true;
|
|
}
|
|
|
|
std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
|
|
union {
|
|
float float_value;
|
|
uint32_t raw;
|
|
} raw_to_float;
|
|
|
|
std::vector<uint16_t> data;
|
|
int32_t val;
|
|
|
|
switch (value_type) {
|
|
case SensorValueType::U_WORD:
|
|
case SensorValueType::S_WORD:
|
|
// cast truncates the float do some rounding here
|
|
data.push_back(lroundf(value) & 0xFFFF);
|
|
break;
|
|
case SensorValueType::U_DWORD:
|
|
case SensorValueType::S_DWORD:
|
|
val = lroundf(value);
|
|
data.push_back((val & 0xFFFF0000) >> 16);
|
|
data.push_back(val & 0xFFFF);
|
|
break;
|
|
case SensorValueType::U_DWORD_R:
|
|
case SensorValueType::S_DWORD_R:
|
|
val = lroundf(value);
|
|
data.push_back(val & 0xFFFF);
|
|
data.push_back((val & 0xFFFF0000) >> 16);
|
|
break;
|
|
case SensorValueType::FP32:
|
|
raw_to_float.float_value = value;
|
|
data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16);
|
|
data.push_back(raw_to_float.raw & 0xFFFF);
|
|
break;
|
|
case SensorValueType::FP32_R:
|
|
raw_to_float.float_value = value;
|
|
data.push_back(raw_to_float.raw & 0xFFFF);
|
|
data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16);
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "Invalid data type for modbus float to payload conversation");
|
|
break;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
float payload_to_float(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
|
|
uint32_t bitmask) {
|
|
union {
|
|
float float_value;
|
|
uint32_t raw;
|
|
} raw_to_float;
|
|
|
|
int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
|
|
float result = NAN;
|
|
|
|
switch (sensor_value_type) {
|
|
case SensorValueType::U_WORD:
|
|
value = mask_and_shift_by_rightbit(get_data<uint16_t>(data, offset), bitmask); // default is 0xFFFF ;
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::U_DWORD:
|
|
value = get_data<uint32_t>(data, offset);
|
|
value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::U_DWORD_R:
|
|
value = get_data<uint32_t>(data, offset);
|
|
value = static_cast<uint32_t>(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16;
|
|
value = mask_and_shift_by_rightbit((uint32_t) value, bitmask);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::S_WORD:
|
|
value = mask_and_shift_by_rightbit(get_data<int16_t>(data, offset),
|
|
bitmask); // default is 0xFFFF ;
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::S_DWORD:
|
|
value = mask_and_shift_by_rightbit(get_data<int32_t>(data, offset), bitmask);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::S_DWORD_R: {
|
|
value = get_data<uint32_t>(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<int32_t>(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask);
|
|
result = static_cast<float>(value);
|
|
} break;
|
|
case SensorValueType::U_QWORD:
|
|
// Ignore bitmask for U_QWORD
|
|
value = get_data<uint64_t>(data, offset);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
|
|
case SensorValueType::S_QWORD:
|
|
// Ignore bitmask for S_QWORD
|
|
value = get_data<int64_t>(data, offset);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::U_QWORD_R:
|
|
// Ignore bitmask for U_QWORD
|
|
value = get_data<uint64_t>(data, offset);
|
|
value = static_cast<uint64_t>(value & 0xFFFF) << 48 | (value & 0xFFFF000000000000) >> 48 |
|
|
static_cast<uint64_t>(value & 0xFFFF0000) << 32 | (value & 0x0000FFFF00000000) >> 32 |
|
|
static_cast<uint64_t>(value & 0xFFFF00000000) << 16 | (value & 0x00000000FFFF0000) >> 16;
|
|
result = static_cast<float>(value);
|
|
break;
|
|
|
|
case SensorValueType::S_QWORD_R:
|
|
// Ignore bitmask for S_QWORD
|
|
value = get_data<int64_t>(data, offset);
|
|
result = static_cast<float>(value);
|
|
break;
|
|
case SensorValueType::FP32:
|
|
raw_to_float.raw = get_data<uint32_t>(data, offset);
|
|
ESP_LOGD(TAG, "FP32 = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value);
|
|
result = raw_to_float.float_value;
|
|
break;
|
|
case SensorValueType::FP32_R: {
|
|
auto tmp = get_data<uint32_t>(data, offset);
|
|
raw_to_float.raw = static_cast<uint32_t>(tmp & 0xFFFF) << 16 | (tmp & 0xFFFF0000) >> 16;
|
|
ESP_LOGD(TAG, "FP32_R = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value);
|
|
result = raw_to_float.float_value;
|
|
} break;
|
|
case SensorValueType::RAW:
|
|
result = NAN;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace modbus_controller
|
|
} // namespace esphome
|