How to send/receive data from PLC through Modbus(RS485)

Hi Experts,
Currently i am working for receive data from PLC through RS485(here i have used Microcontroller like Arduino), here i have used Modscan 32 and modscan64 software(traffic data) for find a correct request data for my microcontroller then i have a below doubt
1. i have worked with RS485 to collect data from flow meters and RS485 enable sensors, usually that is need 8 byte request for send the data that is still now working fine but now i have checked the PLC data through modscan32 and 64 software's those are send 9 bytes data for receive the data, i dont know how to send the request data from microcontroller,

2. i have attached modscan software traffic data and previously i used format (which is i send from the microcontroller for receive the data from RS485 enabled meter or sensors ) for your reference, kindly check and help me to solve this problem,

file name
8byte req- i have send 8byte request from MC and receive data from RS485 enabled devices(meter&sensors)
char request[] = {0x01, 0x03, 0x30, 0x04, 0x00, 0x04, 0x0A, 0xC8};// i have used this line for send the request from my microcontroller
9byte req 32 and 64 is MODSCAN software traffic data
so i changed my controller request like
char request[] = {0x01, 0x03, 0x10, 0x82, 0x00, 0x10, 0x5A, 0x13,0x10};

i can't get any data from PLC kindly help to find my mistakes


regards
kannannatesh
 

Attachments

Just looking at your pictures, I can tell you that you are using two different protocols.

The "8 byte req" images are Modbus RTU.

The "9 byte req" images are Modbus ASCII, and are not actually 9 bytes at all. The messages all start with the ':' character and end with carriage return, line-feed characters '[013][010]'. Additionally, the data is not hex, it's ASCII. So the request (in hex) is actually as follows:

0x3a 0x30 0x31 0x30 0x33 0x31 0x30 0x38 0x32 0x30 0x30 0x31 0x30 0x35 0x41 0x0D 0x0A

Please note that the CR and LF from your ModScan images are shown in decimal representation in the brackets, hexadecimal representation would be 0D 0A.

Please refer to the Modbus specification for the differences in encoding between RTU and ASCII:
https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
 
Just looking at your pictures, I can tell you that you are using two different protocols.

The "8 byte req" images are Modbus RTU.

The "9 byte req" images are Modbus ASCII, and are not actually 9 bytes at all. The messages all start with the ':' character and end with carriage return, line-feed characters '[013][010]'. Additionally, the data is not hex, it's ASCII. So the request (in hex) is actually as follows:

0x3a 0x30 0x31 0x30 0x33 0x31 0x30 0x38 0x32 0x30 0x30 0x31 0x30 0x35 0x41 0x0D 0x0A

Please note that the CR and LF from your ModScan images are shown in decimal representation in the brackets, hexadecimal representation would be 0D 0A.

Please refer to the Modbus specification for the differences in encoding between RTU and ASCII:
https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
Hi @jschulze
Thanks for the clarification, i will check and let you know the result tomorrow

Regards
Kannannatesh
 
Hi @jschulze
Thanks for the clarification, i will check and let you know the result tomorrow

Regards
Kannannatesh
Just looking at your pictures, I can tell you that you are using two different protocols.

The "8 byte req" images are Modbus RTU.

The "9 byte req" images are Modbus ASCII, and are not actually 9 bytes at all. The messages all start with the ':' character and end with carriage return, line-feed characters '[013][010]'. Additionally, the data is not hex, it's ASCII. So the request (in hex) is actually as follows:

0x3a 0x30 0x31 0x30 0x33 0x31 0x30 0x38 0x32 0x30 0x30 0x31 0x30 0x35 0x41 0x0D 0x0A

Please note that the CR and LF from your ModScan images are shown in decimal representation in the brackets, hexadecimal representation would be 0D 0A.

Please refer to the Modbus specification for the differences in encoding between RTU and ASCII:
https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
Hi @jschulze
Thanks for the suggestion, i have tried two different methods
1)
char request[] = {0x3A, 0x30, 0x31, 0x30, 0x33, 0x31, 0x30, 0x38, 0x32, 0x30, 0x30, 0x31, 0x30, 0x35, 0x41, 0x0D, 0x0A};
SerialWrite(PORT2,request,17);
2)
SerialPrint(":0103108200105A\r\n")

i am getting the same response, but here my doubt is the 1st byte 3A represents :
the next byte must be a Device ID 01 so the data became a 30 33 but i am getting 30 B1 i can't understand this, then the next two bytes are 30 33

i have converted my bytes to hex using the below code then print them in UART

u8 Hex11[200],Hex[10];
for(int i=0;i<buflen;i++)
{
sprintf(Hex, "%X",Buffer[count]);
int con =(int)Buffer[count];
if(con<16)
{
strcat(Hex11,"0");
strcat(Hex11,Hex);
}
else
strcat(Hex11,Hex);
}
SerialPrint("data:%s,%d\r\n",Hex11,strlen(Buffer));

data:3A30B13033B230B1393333303030303030B436303030303030B235303030303030
B1C5303030303030303030303030B133B1303030303030303030303030303030303030303030C5B48D0A,75

kindly help me to find the mistakes

regards
kannannatesh
 
Receiving 30 B1 is an indication you configured your serial port for 8 data bits and no parity when your Modbus ASCII device uses 7 data bits and even parity. Try changing your serial port settings to 7 data bits and even parity.
 
Receiving 30 B1 is an indication you configured your serial port for 8 data bits and no parity when your Modbus ASCII device uses 7 data bits and even parity. Try changing your serial port settings to 7 data bits and even parity.
Hi @jschulze
Thanks for the input i will check and let you know the result

regards
kannannatesh
 
Receiving 30 B1 is an indication you configured your serial port for 8 data bits and no parity when your Modbus ASCII device uses 7 data bits and even parity. Try changing your serial port settings to 7 data bits and even parity.
Hi @jschulze
i got below value, here how can i find readed data length
3A3031303332303139333430303030303034323030303030303233303030303030314330303030303030303030303030303332303030303030303030303030373533303030303033370D0A,0,75

i need your help to separate register value to decimal

the register value is 4227 to 4242

4227 is temperature 1
4228 is temperature 2
4229 is temperature 3
4230 is temperature 4
4231 is steam pressure
4237 is steam flow
4241 is pH

regards
kannannatesh
 
I assume changing your serial port settings to 7 data bits and even parity solved the previous issue you were having?

To decode the Modbus Read Holding Registers response, refer to section 6.3 in the Modbus specification here (note that the specification only shows the Request and Response PDU, the entire ADU in Modbus ASCII also includes the start character ':' and slave address at the beginning of the packet and the LRC and carriage return/line feed characters at the end):
https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf

To extract the values from a response, first convert the hex numbers to ASCII. Your received packet is the following in ASCII:
:010320193400000042000000230000001C00000000000000320000000000007530000037

The packet format is as follows
:[Slave Address][Function Code][Byte Count][Register Values (each value is 2 bytes, i.e. 4 characters)][LRC][CR][LF]

So decoding the packet above (each pair of characters is one hex number)
: 01 03 20 1934 0000 0042 0000 0023 0000 001C 0000 0000 0000 0032 0000 0000 0000 7530 0000 37
Slave Address = 0x01
Function Code = 0x03 (Read Holding Registers)
Byte Count = 0x20 (32 bytes = 16 registers)
Register 4227 = 0x1934 (6452 decimal)
Register 4228 = 0x0000 (0 decimal)
Register 4229 = 0x0042 (66 decimal)
....
Register 4241 = 0x7530 (30000 decimal)
Register 4242 = 0x0000 (0 decimal)

The last 37 byte should be part of the LRC, but the LRC is supposed to be 2 bytes, so you may not have received or reported the response correctly.
 
I assume changing your serial port settings to 7 data bits and even parity solved the previous issue you were having?

To decode the Modbus Read Holding Registers response, refer to section 6.3 in the Modbus specification here (note that the specification only shows the Request and Response PDU, the entire ADU in Modbus ASCII also includes the start character ':' and slave address at the beginning of the packet and the LRC and carriage return/line feed characters at the end):
https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf

To extract the values from a response, first convert the hex numbers to ASCII. Your received packet is the following in ASCII:
:010320193400000042000000230000001C00000000000000320000000000007530000037

The packet format is as follows
:[Slave Address][Function Code][Byte Count][Register Values (each value is 2 bytes, i.e. 4 characters)][LRC][CR][LF]

So decoding the packet above (each pair of characters is one hex number)
: 01 03 20 1934 0000 0042 0000 0023 0000 001C 0000 0000 0000 0032 0000 0000 0000 7530 0000 37
Slave Address = 0x01
Function Code = 0x03 (Read Holding Registers)
Byte Count = 0x20 (32 bytes = 16 registers)
Register 4227 = 0x1934 (6452 decimal)
Register 4228 = 0x0000 (0 decimal)
Register 4229 = 0x0042 (66 decimal)
....
Register 4241 = 0x7530 (30000 decimal)
Register 4242 = 0x0000 (0 decimal)

The last 37 byte should be part of the LRC, but the LRC is supposed to be 2 bytes, so you may not have received or reported the response correctly.
Hi @jschulze

Thanks for the quick response, sorry i am not mentioned "i have changed my serial settings as per your input" then i get below value
3A3031303332303139333330303030303036363030303030303233303030303030323030303030303246373030303030393738303030303030384330303030373533303030303033430D0A

this is the original converted value

4227 is temperature 1 - 452
4228 is temperature 2 - 102
4229 is temperature 3 - 35
4230 is temperature 4 - 32
4231 is steam pressure - 6.82kg
4237 is steam flow - 2286
4241 is liquid flow -30000
4241 is pH - 0

here my doubt is how can i convert those values to above decimal in my controller, i have shared some lines which are i used "the below lines are also your suggestion too" to convert hex string to decimal

unsigned long temp32;
temp32 = (((unsigned long)byteSend[3]) << 24) | (((unsigned long)byteSend[4]) << 16) | (((unsigned long)byteSend[5]) << 8) | ((unsigned long)byteSend[6]);
Serial.print("long=");
Serial.println(temp32);

regards
kannannatesh
 
The last 37 byte should be part of the LRC, but the LRC is supposed to be 2 bytes, so you may not have received or reported the response correctly.
I apologize, I was mistaken on the LRC and wanted to correct myself. The LRC is 2 characters and therefore only 1 byte. So the packet you show is a complete, valid Modbus packet.

Thanks for the quick response, sorry i am not mentioned "i have changed my serial settings as per your input" then i get below value
3A3031303332303139333330303030303036363030303030303233303030303030323030303030303246373030303030393738303030303030384330303030373533303030303033430D0A
It appears that each value is a 32-bit value and the first register contains the least significant portion of the 32-bit value. So from this data string converted to ASCII as follows:
: 01 03 20 1933 0000 0066 0000 0023 0000 0020 0000 02F7 0000 0978 0000 008C 0000 7530 0000 3C

The data is as follows:
Register 4227 = 0x00001933 (6451 decimal)
Register 4229 = 0x00000066 (102 decimal)
Register 4231 = 0x00000023 (35 decimal)
Register 4233 = 0x00000020 (32 decimal)
Register 4235 = 0x000002F7 (759 decimal)
Register 4237 = 0x00000978 (2424 decimal)
Register 4239 = 0x0000008C (140 decimal)
Register 4241 = 0x00007530 (30000 decimal)

this is the original converted value

4227 is temperature 1 - 452
4228 is temperature 2 - 102
4229 is temperature 3 - 35
4230 is temperature 4 - 32
4231 is steam pressure - 6.82kg
4237 is steam flow - 2286
4241 is liquid flow -30000
4241 is pH - 0
Where are these values coming from? What do you see in Modscan for the values of registers 4227 - 4242 (note that you will need to select Setup->Display Options->Long Integer->Least Significant Register First)? I think you may be numbering the registers incorrectly.

here my doubt is how can i convert those values to above decimal in my controller, i have shared some lines which are i used "the below lines are also your suggestion too" to convert hex string to decimal

unsigned long temp32;
temp32 = (((unsigned long)byteSend[3]) << 24) | (((unsigned long)byteSend[4]) << 16) | (((unsigned long)byteSend[5]) << 8) | ((unsigned long)byteSend[6]);
Serial.print("long=");
Serial.println(temp32);
This code cannot be used verbatim and actually uses Most Significant Register First ordering, not Least Significant Register first.

You first need to convert the ASCII characters you've received into hexadecimal bytes. For example, the ASCII characters you received for the value of the first register (register 4227) are "19330000", which is stored in your buffer as [31 39 33 33 30 30 30 30] (all values in hexadecimal). You need to convert this buffer into a buffer that looks like this [19 33 00 00] (all values in hexadecimal). Here is a function that will allow you to convert each pair of ASCII characters into a single byte.
Code:
unsigned char AsciiToInt8U (char* p_val)
{
    unsigned char number;


    number = (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10))) << 4;
    p_val++;        // point to next character
    number += (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10)));

    return number;
}
Next you need to convert each 4-byte value to a 32-bit number using least significant register first ordering (but most-significant byte ordering within each 16-bit register).
Code:
unsigned long temp32;
temp32 = (((unsigned long)ByteBuffer[2]) << 24) | (((unsigned long)ByteBuffer[3]) << 16) | (((unsigned long)ByteBuffer[0]) << 8) | ((unsigned long)ByteBuffer[1]);
 
I apologize, I was mistaken on the LRC and wanted to correct myself. The LRC is 2 characters and therefore only 1 byte. So the packet you show is a complete, valid Modbus packet.


It appears that each value is a 32-bit value and the first register contains the least significant portion of the 32-bit value. So from this data string converted to ASCII as follows:
: 01 03 20 1933 0000 0066 0000 0023 0000 0020 0000 02F7 0000 0978 0000 008C 0000 7530 0000 3C

The data is as follows:
Register 4227 = 0x00001933 (6451 decimal)
Register 4229 = 0x00000066 (102 decimal)
Register 4231 = 0x00000023 (35 decimal)
Register 4233 = 0x00000020 (32 decimal)
Register 4235 = 0x000002F7 (759 decimal)
Register 4237 = 0x00000978 (2424 decimal)
Register 4239 = 0x0000008C (140 decimal)
Register 4241 = 0x00007530 (30000 decimal)


Where are these values coming from? What do you see in Modscan for the values of registers 4227 - 4242 (note that you will need to select Setup->Display Options->Long Integer->Least Significant Register First)? I think you may be numbering the registers incorrectly.


This code cannot be used verbatim and actually uses Most Significant Register First ordering, not Least Significant Register first.

You first need to convert the ASCII characters you've received into hexadecimal bytes. For example, the ASCII characters you received for the value of the first register (register 4227) are "19330000", which is stored in your buffer as [31 39 33 33 30 30 30 30] (all values in hexadecimal). You need to convert this buffer into a buffer that looks like this [19 33 00 00] (all values in hexadecimal). Here is a function that will allow you to convert each pair of ASCII characters into a single byte.
Code:
unsigned char AsciiToInt8U (char* p_val)
{
    unsigned char number;


    number = (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10))) << 4;
    p_val++;        // point to next character
    number += (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10)));

    return number;
}
Next you need to convert each 4-byte value to a 32-bit number using least significant register first ordering (but most-significant byte ordering within each 16-bit register).
Code:
unsigned long temp32;
temp32 = (((unsigned long)ByteBuffer[2]) << 24) | (((unsigned long)ByteBuffer[3]) << 16) | (((unsigned long)ByteBuffer[0]) << 8) | ((unsigned long)ByteBuffer[1]);
Hi @jschulze
thanks a lot for help and super fast reply, i will explain situation it will help you to understand my situation
i am trying to connect the instruments and PLC(which is have a RS485) with IoT,
currently my client have a Delta PLC that is installed in remote location so i can't go there to check the values with Modscan software,
Yes i have shared modscan screen shot with you that is taken by my friend but now he is not available, my controller have OTA firmware upload option so i am working remotely
PLC connected with HMI so i shared the values which is showed in HMI
4227 is temperature 1 - 452
4228 is temperature 2 - 102
4229 is temperature 3 - 35
4230 is temperature 4 - 32
4231 is steam pressure - 6.82kg
4237 is steam flow - 2286
4241 is liquid flow -30000
4241 is pH - 0

this is my first project with PLC's so i don't have a much knowledge about these conversion too

you got very close values except temperature 1 i am damn sure the temperature does not greater than 600 kindly recheck the data to find the correct value and find my mistakes,

when i take screen shot(modscan) my settings is
device id = 1
register address- 4227
len = 16

regards
kannannatesh
 
Keep in mind when working with PLC's, the Programmable part of Programmable Logic Controller is very important. The data being returned via Modbus is being return because of some program loaded onto the PLC. Only the programmer of the PLC can tell you what the data is, the format of the data, and which Modbus registers the data is mapped to.

Yes, you are correct that the values seem close. I can only tell you the values that the Modbus packet contains, and I am quite sure the value for the first register is 6451 in the Modbus packet. You would also see these same values using ModScan. Perhaps 4227 is not the correct register for "temperature 1" or perhaps the data is not encoded into the register like you expect.
 
Hi @jschulze
Thanks once again for your super fast help, ok i will check once on my side then letvyou know the result,
Yes you given very close to the HMI value because of the time gab to my device send the value to server time and HMI image taken time,
You convert temperature1 value is 6451, but HMI shows 452 it may be 451, so i am going add -6000 to the code before show the value, i don't know its correct or not anyway i will check tomorrow and let you know the result

Regards
Kannannatesh
 
Keep in mind when working with PLCs, the Programmable part of the Programmable Logic Controller is very important. The data being returned via Modbus is being returned because of some program loaded onto the PLC. Only the programmer of the PLC can tell you what the data is, the format of the data, and which Modbus registers the data is mapped to.

Yes, you are correct that the values seem close. I can only tell you the values that the Modbus packet contains, and I am quite sure the value for the first register is 6451 in the Modbus packet. You would also see these same values using ModScan. Perhaps 4227 is not the correct register for "temperature 1" or perhaps the data is not encoded into the register as you expect.
Hi @jschulze
After some string split and string function i got below string
00001934,00000095,00000025,00000021,000002EB,00000A4C,00000000,00007530
here i am facing one problem 00001934 my device print this length is 8 so i want to convert two-byte(16bit) into one byte(8bit)
for example, 1 represents 00000001 and 9 represents 000010001 here i need to shift "1" last 4bits as a "19" first 4 bit then shift "9" last 4 bit as a "19" last 4 bit but i don't know how to do that if you have any suggestion code lines kindly suggest to me,

regards
kannannatesh
 
here i am facing one problem 00001934 my device print this length is 8 so i want to convert two-byte(16bit) into one byte(8bit)
for example, 1 represents 00000001 and 9 represents 000010001 here i need to shift "1" last 4bits as a "19" first 4 bit then shift "9" last 4 bit as a "19" last 4 bit but i don't know how to do that if you have any suggestion code lines kindly suggest to me,
I'm sorry, but I don't understand what you're saying here. Are you using binary representation of 8-bit numbers ("1 represents 00000001 and 9 represents 000010001")? What is the purpose of shifting?

Please explain and provide more details.
 
Hi @jschulze
After some string split and string function i got below string
00001934,00000095,00000025,00000021,000002EB,00000A4C,00000000,00007530
here i am facing one problem 00001934 my device print this length is 8 so i want to convert two-byte(16bit) into one byte(8bit)
for example, 1 represents 00000001 and 9 represents 000010001 here i need to shift "1" last 4bits as a "19" first 4 bit then shift "9" last 4 bit as a "19" last 4 bit but i don't know how to do that if you have any suggestion code lines kindly suggest to me,

regards
kannannatesh
Sorry, I think I understand what you're asking now. You have ASCII characters, for example 00001934, so there are indeed 8 characters. You want to convert these 8 characters to a 32-bit binary number (i.e. 4 bytes). For example, the ASCII characters 00001934 should be converted to the binary value 0x00001934.

I posted code on how to convert 2 ASCII characters into an 8-bit binary number (1 byte) in post #10.

Here is that code modified to take 8 ASCII characters and convert it into a 32-bit binary number.
Code:
unsigned long AsciiToInt32U (char* p_val)
{
    unsigned long number = 0;

    for (int i = 0; i < 8; i++)
    {
        number += ((p_val[i] <= '9' ? p_val[i] - '0' : (p_val[i] <= 'F' ? (p_val[i] - 'A' + 10) : (p_val[i] - 'a' + 10))) << ((7 - i) * 4));
    }
    
    return number;
}
 
Sorry, I think I understand what you're asking now. You have ASCII characters, for example 00001934, so there are indeed 8 characters. You want to convert these 8 characters to a 32-bit binary number (i.e. 4 bytes). For example, the ASCII characters 00001934 should be converted to the binary value 0x00001934.

I posted code on how to convert 2 ASCII characters into an 8-bit binary number (1 byte) in post #10.

Here is that code modified to take 8 ASCII characters and convert it into a 32-bit binary number.
Code:
unsigned long AsciiToInt32U (char* p_val)
{
    unsigned long number = 0;

    for (int i = 0; i < 8; i++)
    {
        number += ((p_val[i] <= '9' ? p_val[i] - '0' : (p_val[i] <= 'F' ? (p_val[i] - 'A' + 10) : (p_val[i] - 'a' + 10))) << ((7 - i) * 4));
    }
   
    return number;
}
Hi @jschulze
Sorry for the confusion, i will try to explain clearly,
This is the PLC response string without any split and swift function used
:0103201934000000950000002500000021000002EB00000A4C00000000000075300000CC
here i want to convert this string into decimal value like
19340000 totally 8byte i need to convert this string to 6452 decimal value
i can't use the below line for this conversion because this is an 8byte string

unsigned long temp32;
temp32 = (((unsigned long)ByteBuffer[2]) << 24) | (((unsigned long)ByteBuffer[3]) << 16) | (((unsigned long)ByteBuffer[0]) << 8) | ((unsigned long)ByteBuffer[1]);

so i am decided to move the two-byte value to a single-byte for an example 19 this is two bytes i like to move this value to a single-byte value as 19, after that i can use the above lines for convertions

so i am going to use the below lines to move two bytes into a single byte

unsigned char AsciiToInt8U (char* p_val)
{
unsigned char number;
number = (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10))) << 4;
p_val++; // point to next character
number += (*p_val <= '9' ? *p_val-'0' : (*p_val <= 'F' ? (*p_val - 'A' + 10) : (*p_val - 'a' + 10)));
return number;
}

i will check with the above suggestion and i will let you know the result

regards
kannannatesh
 
Top