Modbus for a Newby

Hi Everyone,

I just recently started exploring the modbus protocol. Generally, I get the idea of how modbus protocol works and the structure of a modbus query frame and response frame. However, I am curious on how I will integrate its concept in programming. Here is my idea on how I will implement modbus RTU in programming:

For example, the query that I would like to transmit is 11 04 0008 0001 B298.

In order to transmit it, I will simply need to have a string variable that contains the characters "110400080001B298" and then use a command to transmit that string variable to the modbus slave.

Take for example that the response that I am expecting should be 11 04 02 000A F8F4 (assuming that there will be no errors).

I will use a command to receive an incoming serial data and store that data into a string variable. That string variable should now contain the characters "110402000AF8F4". And then I will just have to conduct several steps to extract the Register Value that I wanted to read from that string variable.


I find that this idea of mine about implementing modbus in programing is too simple to be true or correct. I also have limited knowledge in programming but I really I want to learn how to implement modbus in programming. I'm not really asking for a specific syntax or code. I just want to ask you guys about the idea on how I can store the message that I want to transmit and how I can capture the response of the modbus slave that I am communicating with.

I hope I was able to express my idea clearly and I'm looking forward to reading your feedbacks.

NOTE: I am still waiting for the modbus slave that I ordered to arrive that's why I haven't tested this idea yet.
 
I think your general idea is correct. There are a few corrections and suggestions I can make, though.

Since you're using Modbus RTU, the packets will probably be stored in a byte array, not a string, since Modbus RTU is a binary protocol. There is an ASCII version of Modbus, fittingly called Modbus ASCII, that does use ASCII characters, so each packet could be considered a string of characters.

I recommend that you try not to simply hard-code your requests and expected responses. Instead, you should create functions to dynamically build requests for each function code you intend to use, based on the arguments you pass in, such as slave address, register address, number of registers, etc. Similarly you should have decode functions for the received packets that first validates the CRC, performs other validation to confirm the packet is valid and expected, then extracts the response data.

What programming language do you intend to use?

There are many Modbus library resources (some with source code) available on the Modbus site here:
https://www.modbus.org/tech.php

For example, here is the GitHub page for libmodbus
https://github.com/stephane/libmodbus

You can use these resources as a reference to see how others have implemented the Modbus protocol.
 
I think your general idea is correct. There are a few corrections and suggestions I can make, though.

Since you're using Modbus RTU, the packets will probably be stored in a byte array, not a string, since Modbus RTU is a binary protocol. There is an ASCII version of Modbus, fittingly called Modbus ASCII, that does use ASCII characters, so each packet could be considered a string of characters.

I recommend that you try not to simply hard-code your requests and expected responses. Instead, you should create functions to dynamically build requests for each function code you intend to use, based on the arguments you pass in, such as slave address, register address, number of registers, etc. Similarly you should have decode functions for the received packets that first validates the CRC, performs other validation to confirm the packet is valid and expected, then extracts the response data.

What programming language do you intend to use?

There are many Modbus library resources (some with source code) available on the Modbus site here:
https://www.modbus.org/tech.php

For example, here is the GitHub page for libmodbus
https://github.com/stephane/libmodbus

You can use these resources as a reference to see how others have implemented the Modbus protocol.
Hi,

This helps me a lot! I will definitely check out these resources. By the way, I will be using a hardware and software called Opto22 to communicate with a modbus slave. I will do the programming within the Opto22 software which uses a language very similar if not the same with the C language.

I forgot to include that the modbus slave that I will be using requires me to use Modbus RTU specifically.

I appreciate the response. Thank you.
 
Hi,

Just to give an update, I'm now quite familiar with implementing Modbus in programming. I was able to communicate with several Modbus Energy Meters with different brands, successfully. I was able to read Voltage, Current, Frequency, and kWh. However, I'm having troubles with communicating with a UPS via Modbus.

To test my program, I constructed a Modbus query frame that would request to read the battery capacity (unit is Ah) of the UPS. Based on my assessment, I am transmitting the correct Query Frame. I am also receiving a valid Response frame, so that gives me a clue that I have no problems in communicating with the UPS. However, when I try to extract the data from the response frame to display it in Engineering units, my value that I am getting does not correspond to the battery capacity value that I am expecting.

My query frame contains:
01 03 00 57 00 02 75 DB

The response frame that I am receiving contains:
01 03 04 07 D0 01 2C FA F3

The value that I am getting from the response frame is:
3.129705e-034

Just to give you an idea, here is the code that I used to extract values from the response frame. I've used this code in several energy meters, and it works perfectly. By the way, I am using Opto 22 PAC Control:

fReturnValue =Int32ToFloatBits((GetNthCharacter(sReceivedMessage,3) <<24) bitor
(GetNthCharacter(sReceivedMessage,4) <<16) bitor
(GetNthCharacter(sReceivedMessage,5) <<8) bitor
(GetNthCharacter(sReceivedMessage,6))); //Store data

Maybe you guys have an idea on what I am overlooking. Being new to Modbus, I am pretty sure that there is a concept that I am overlooking since I am pretty certain that there is no problem with the communication.
 
The value is a 32 bit floating point (FP) value consisting of four 8 bit bytes. There are four IEEE754 word/byte order formats to produce a 32 bit FP value, as shown in the graphic below from vendor documentation. The terminology varies by vendor, some call it "endianness".

floating point word bit order defined in UDC manual.jpg

Out of the four formats, there are two common formats, identified as FB B and FBLB in the graphic above. The correct interpretation for the 32 bits you received is very likely the other word/byte order.

I don't know what support Opto 22 PAC Control offers for Modbus, but most packages that support Modbus master functionality will have a selection somewhere for a choice of which word/byte order is used to interpret floating point values.

Otherwise you have to do some coding to get the data bytes in the correct order.
 
The Modbus specification defines a register as a 16-bit value and specifies that each register must be encoded using most-significant-byte-first ordering in the Modbus packet. The specification does not define 32-bit values, nor how they should be encoded in the Modbus packet.

Therefore, vendors have implemented their own encodings (which are not all the same) for 32-bit values where each 32-bit value is comprised of two 16-bit registers (i.e. two register addresses per 32-bit value). Although the Modbus specificaiton defines the byte ordering of these two registers within the Modbus packet, it does not define which register shall be interpreted as the most-significant or least-significant. As such, some vendors encode the most-significant register first (i.e. the lower register number) and others encode the least-significant register first.

There is actually another encoding used only by specific vendors (Enron, Daniel, Omni) for 32-bit values, which is the Enron/Daniel version of Modbus where a 32-bit value is comprised of only one register (i.e. one register address), but this likely doesn't apply here.

In addition to this, there are two types of 32-bit value encodings: integers and floating point. You are clearly trying to decode this value as a 32-bit floating point, but are you sure it is indeed a floating point number and not an integer?

Also, some vendors use 0-based Modbus register addresses, while others use 1-based register numbers. So you can run into an "off by one" issue with your register addresses and may need to add or subtract 1 from the register you're requesting.

If you can provide the Modbus documentation for the UPS you're using and what value you're expecting to see, I can provide additional guidance as to the specific registers you should be requesting and how to decode the value.
 
The Modbus specification defines a register as a 16-bit value and specifies that each register must be encoded using most-significant-byte-first ordering in the Modbus packet. The specification does not define 32-bit values, nor how they should be encoded in the Modbus packet.

Therefore, vendors have implemented their own encodings (which are not all the same) for 32-bit values where each 32-bit value is comprised of two 16-bit registers (i.e. two register addresses per 32-bit value). Although the Modbus specificaiton defines the byte ordering of these two registers within the Modbus packet, it does not define which register shall be interpreted as the most-significant or least-significant. As such, some vendors encode the most-significant register first (i.e. the lower register number) and others encode the least-significant register first.

There is actually another encoding used only by specific vendors (Enron, Daniel, Omni) for 32-bit values, which is the Enron/Daniel version of Modbus where a 32-bit value is comprised of only one register (i.e. one register address), but this likely doesn't apply here.

In addition to this, there are two types of 32-bit value encodings: integers and floating point. You are clearly trying to decode this value as a 32-bit floating point, but are you sure it is indeed a floating point number and not an integer?

Also, some vendors use 0-based Modbus register addresses, while others use 1-based register numbers. So you can run into an "off by one" issue with your register addresses and may need to add or subtract 1 from the register you're requesting.

If you can provide the Modbus documentation for the UPS you're using and what value you're expecting to see, I can provide additional guidance as to the specific registers you should be requesting and how to decode the value.
Hi guys,

I will definitely check out your suggestions. I will be back on site today for testing. For your reference, here is the documentation of the UPS that I am currently working on right now.

I appreciate your help guys. Thank you.
 

Attachments

According to that documentation, the Battery Capacity is a 16-bit integer, not a 32-bit floating point value.

Therefore, your query frame should be as follows (note that the number of registers is 1, not 2):
01 03 00 57 00 01 35 DA

From your original request/response for reading registers 0x0057 and 0x0058:
My query frame contains:
01 03 00 57 00 02 75 DB

The response frame that I am receiving contains:
01 03 04 07 D0 01 2C FA F3
The values that the UPS returns are as follows:
Register 0x0057 = 0x07D0 (2000 decimal)
Register 0x0058 = 0x012C (300 decimal)
 
The value is a 32 bit floating point (FP) value consisting of four 8 bit bytes. There are four IEEE754 word/byte order formats to produce a 32 bit FP value, as shown in the graphic below from vendor documentation. The terminology varies by vendor, some call it "endianness".

View attachment 1742

Out of the four formats, there are two common formats, identified as FB B and FBLB in the graphic above. The correct interpretation for the 32 bits you received is very likely the other word/byte order.

I don't know what support Opto 22 PAC Control offers for Modbus, but most packages that support Modbus master functionality will have a selection somewhere for a choice of which word/byte order is used to interpret floating point values.

Otherwise you have to do some coding to get the data bytes in the correct order.
According to that documentation, the Battery Capacity is a 16-bit integer, not a 32-bit floating point value.

Therefore, your query frame should be as follows (note that the number of registers is 1, not 2):
01 03 00 57 00 01 35 DA

From your original request/response for reading registers 0x0057 and 0x0058:


The values that the UPS returns are as follows:
Register 0x0057 = 0x07D0 (2000 decimal)
Register 0x0058 = 0x012C (300 decimal)
Wow! It really worked. Thank you. I appreciate you taking time to help me. I'll make sure to keep this in mind for future reference.
 
Top