Reputation: 11
I am currently running Modbus RTU via RS485 on ESP32 to read data from the Modbus Slave emulator. The code is able to read/write from the virtual PLCs using the ArduinoModbus library (I had to adjust a few parts since it doesn't support ESP32). However, the library still can't read/write other data types (float, long, double...) other than binary and int. I'm using PlatformIO in VSCode. ArduinoModbus ArduinoR485 Modbus Slave emulator
Here is the modified read()
function that I'm testing in the library:
long ModbusClient::read()
{
if (_available <= 0) {
return -1;
}
long result = -1;
switch (_type) {
case COILS:
case DISCRETE_INPUTS:
result = ((uint8_t*)_values)[_read];
if (result != -1) {
_available--;
_read++;
}
break;
case HOLDING_REGISTERS:
case INPUT_REGISTERS:
if (_available >= 2) {
result = ((uint16_t*)_values)[_read];
result = (result << 16) | ((uint16_t*)_values)[_read + 1];
if (result != -1) {
_available -= 2;
_read += 2;
}
}
break;
default:
break;
}
return result;
}
P/S: Alternatively, the comments suggest using another function in the library, but I'm still studying it:
long ModbusClient::holdingRegisterRead(int id, int address)
{
uint16_t value;
modbus_set_slave(_mb, id);
if (modbus_read_registers(_mb, address, 1, &value) < 0) {
return -1;
}
return value;
}
My main()
loop:
void loop() {
readHoldingRegisterValues();
delay(5000);
Serial.println();
}
void readHoldingRegisterValues() {
Serial.print("Reading Input Register values ... ");
// read 10 Input Register values from (slave) id 1, address 0x00
if (!ModbusRTUClient.requestFrom(1, HOLDING_REGISTERS, 0x00, 10)) {
Serial.print("failed! ");
Serial.println(ModbusRTUClient.lastError());
} else {
Serial.println("success");
while (ModbusRTUClient.available()) {
Serial.print(ModbusRTUClient.read());
Serial.print(' ');
}
Serial.println();
}
}
Here is the output:
1111170744 0 0 0 0
From my understanding, a register can only contain 16-bit value. Float and double are 32-bit and 64-bit, so I'm trying to read each register in turn, then combine them to get the result.
Upvotes: 0
Views: 304
Reputation: 11
Replace holdingRegisterRead()
function with a template function:
#include "ModbusClient.h"
using namespace std;
template<typename T>
T ModbusClient::holdingRegisterRead(int id, int address, ByteOrder byteOrder){
uint16_t values[4];
int numRegisters = 0;
if(is_same<T, uint16_t>::value){
numRegisters = 1;
} else if(is_same<T, float>::value){
numRegisters = 2;
} else if(is_same<T, double>::value){
numRegisters = 4;
} else {
throw runtime_error("Unsupported data type");
}
modbus_set_slave(_mb, id);
if(modbus_read_registers(_mb, address, numRegisters, values) < 0){
return -1;
}
if(is_same<T, uint16_t>::value){
return static_cast<uint16_t>(values[0]);
} else if (is_same<T, float>::value) {
uint32_t temp = ((uint32_t)values[0] << 16) | values[1];
if (byteOrder == BIGEND){
return *reinterpret_cast<float*>(&temp);
} else if(byteOrder == LILEND){
temp = (temp << 24) |
((temp << 8) & 0x00FF0000) |
((temp >> 8) & 0x0000FF00) |
(temp >> 24);
return *reinterpret_cast<float*>(&temp);
} else if(byteOrder == BSWAP){
temp = ((temp << 8) & 0xFF00FF00) |
((temp >> 8) & 0x00FF00FF);
return *reinterpret_cast<float*>(&temp);
} else if(byteOrder == LSWAP){
temp = (temp << 16) | (temp >> 16);
return *reinterpret_cast<float*>(&temp);
}
} else if(is_same<T, double>::value){
uint64_t temp = ((uint64_t)values[0] << 48) |
((uint64_t)values[1] << 32) |
((uint64_t)values[2] << 16) |
values[3];
if(byteOrder == BIGEND){
return *reinterpret_cast<double*>(&temp);
} else if(byteOrder == LILEND){
temp = (temp << 56) |
((temp << 40) & 0x00FF000000000000) |
((temp << 24) & 0x0000FF0000000000) |
((temp << 8 ) & 0x000000FF00000000) |
((temp >> 8 ) & 0x00000000FF000000) |
((temp >> 24) & 0x0000000000FF0000) |
((temp >> 40) & 0x000000000000FF00) |
(temp >> 56);
return *reinterpret_cast<double*>(&temp);
} else if(byteOrder == BSWAP){
temp = ((temp << 8) & 0xFF00FF00FF00FF00) |
((temp >> 8) & 0x00FF00FF00FF00FF);
return *reinterpret_cast<double*>(&temp);
} else if(byteOrder == LSWAP){
temp = (temp << 48) |
((temp << 16) & 0x0000FFFF00000000) |
((temp >> 16) & 0x00000000FFFF0000) |
(temp >> 48);
return *reinterpret_cast<double*>(&temp);
}
}
return 0;
}
Upvotes: 0
Reputation: 43
I suppose,
_values
is a uint16_t array, stored as a class field or as a static variable (I'm not familiar with this library). Since modbus doesn't transfer data types, they're only defined in your program and in a client, you can re-write this _values variable and it's usage in library as it suits you. For example, you can change it for a union or a struct:
struct {
uint16_t uintValues[REG_NUM];
int16_t intValues[REG_NUM];
float floatValues[REG_NUM];
etc...
} sValues;
Then you can pass uintValues or floatValues member of sValues (_values) variable in read/write functions depending on which registers with which data types is requested by a client.
Upvotes: 0