Closed Hasenburg closed 4 years ago
I'll take a look on how to improve.
@Hasenburg: you are taking the data pointer, which is pointing to a single byte, and force it with the reinterpret_cast to point to an int - 4 bytes in a row. So no wonder you get whatever is behind the data. The response to a write request is an echo of the request, so the data you got looks like it.
I am picking up this issue because it shows a significant weakness of the "raw request" feature I am working on, as that will depend on a response length byte not all responses will hold - as in your example. Thank you for the hint!
@Miq1: What do you mean that I cast it 4 bytes in a row?
Do you have a hint how to convert that data pointer into an (for me) usable data type (int, char, string)?
It seems that the data only shows its value within
for (uint8_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
}
without any additional bytes. I tried to convert it in many different ways, but it always results in some totally different values.
Reversing the data and reinterpret_cast it to int, was the closest I got to show the correct values...
@Miq1: What do you mean that I cast it 4 bytes in a row?
You have the data
pointer, that has a type of uint8_t *
, it will point you to a single uint8_t
byte. Your reinterpret_cast<int*>
forces the compiler to take the data
pointer as if it were a pointer to int
, which is 4 bytes long. Hence the printf takes the 4 bytes for an int
starting from the byte data
is pointing at. So whatever is behind the data data
was supposed to point at, is taken as a part of an int
value. So you will get data printed even if dataLen was shorter than 4.
Do you have a hint how to convert that data pointer into an (for me) usable data type (int, char, string)?
As I wrote, you are using FC 0x06 here. The Modbus standard mandates the response to a writing access (as FC 0x06 is) to be an echo of the request. The correct full response for your request then would have been 01 06 00 63 17 0E CC CC
(CC CC for the two CRC bytes), but it did not, because you missed to give the register address as uint16_t
= 0063
, but used a single byte 63
.
So no wonder the response was 01 06 63 17 0E CC CC
- the echo of your malformed request.
The device you are using seems to be pretty ignorant to format errors, by the way - another would have responded with an error response like ILLEGAL_ADDRESS or ILLEGAL_DATA_VALUE.
It seems that the data only shows its value within
for (uint8_t i = 0; i < length; ++i) { Serial.printf("%02x", data[i]); }
without any additional bytes. I tried to convert it in many different ways, but it always results in some totally different values.
Reversing the data and reinterpret_cast it to int, was the closest I got to show the correct values...
You better should stick to the for
loop to print out the data byte-wise in hexadecimal. Since Modbus messages are MSB-first by definition, you will need to write a conversion function yourself, that takes care of the different lengths of the data fields in a well-formed Modbus message. (Side note: beware the inverse order of the CRC bytes 😁 )
@Miq1: Thanks for your help!
Sorry, there was some confusion in my communication as I did not accurately re-read my first message in Jun. The big problem wasn't FC0x06 as you explained twice. I struggled storing the 2 byte data of FC0x03 data into one variable that I can work with. But your answer gave me a much better understanding of how the response works.
I wrote a little function that, seems (at least to me) to be quite universal. It converts the answer into an uint16_t array (adapting according to length of data), which works like a charm.
Function
std::vector<uint16_t> readValues(size_t length, uint8_t* data) {
std::vector<uint16_t> value;
for (size_t i = 0; i < length; i+=2) {
value.push_back(((uint16_t)data[i] << 8) | data[i+1]);
}
return value;
}
handleData Function Pointer
void handleData(uint8_t serverAddress, uint8_t fc, uint8_t* data, size_t length) {
Serial.printf("id 0x%02x fc 0x%02x len %u: 0x", serverAddress, fc, length);
for (size_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
}
Serial.print("\n");
std::vector<uint16_t> myValue = readValues(length, data);
for (int i = 0; i < myValue.size(); i++) {
Serial.printf("Value #%d: ", i);
Serial.printf("Hex %02x, ", myValue[i]);
Serial.printf("Dec: %d\n\n", (int)myValue[i]);
}
}
I did not face any problems with inverse order of CRC bytes. Do you see any problems with that code?
Good that you found something usable in my comments 😀
I see one tiny issue in the function returning the vector<uint16_t>
: if you will encounter a data message with an odd (not even) length value, you will grab a byte behind the data proper, potentially causing a SEGFAULT error.
As a side note, the FC 0x03 will return a length byte in the response, immediately before the uint16_t
values are beginning. Bert's library does filter that byte for FCs 01, 02, 03 and 04, but if you are going to use another library, you may be surprised 😉
I am playing around with the library for some days and was able to make it communicate with a Siemens Sinamics V20 VFD.
So far so good, thanks to your great library, I was able to write and read registers correctly (unfortunately I am not able to start the motor, but that problem relates to the VFD).
When I tried to make the response of the VFD more readable, I got pretty confused.
Code example:
When I send a read request i.e. to register 0x02 of the VFD with 1 byte, I get the following response from the VFD:
Console output:
which is kind of fine.
But if I use write single register 0x06 i.e. register 0x63, value 0x107E
Console output:
The for loop does not print any data, but the reinterpret_cast<int> does show some data. I don't understand, why that happens?
Furthermore the data shown in the int* cast shows 63(register), 107e(value) and 35(1st crc byte). The second crc byte is cut out for some reason.
Do you understand why all this is happening? I guess the differences has something to do with adding 4 bytes in the reverse function, but I am not sure what to do in order to print out the full received data in order to process it the way I want to