Modbus details¶
Modbus data types¶
The Modbus standard defines storage in:
- Bits. Each bit has its own address.
- Registers (16-bit). Each register has its own address. Can hold integers in the range 0 to 65535 (dec), which is 0 to ffff (hex). Also called ‘unsigned INT16’ or ‘unsigned short’.
Modbus defines “table” names dependent on whether the storage is in a single bit or in a 16-bit register, and whether it is possible to write to the storage.
Storage in | Access | Modbus “table” | Example use on instrument |
---|---|---|---|
Bits | Read only | Discrete inputs | A digital input |
Bits | Read and write | Coils | A digital output |
16-bit register | Read only | Input registers | Several digital inputs, or an analog input |
16-bit register | Read and write | Holding registers | Several digital outputs, or a setting parameter |
Function codes are used to describe the read or write operations (shown in decimal in the table below)
Storage | Read | Write | ||
---|---|---|---|---|
Single | Multiple | Single | Multiple | |
Read-and-write bits (coils) | 1 | 5 | 15 | |
Read-only bits (discrete inputs) | 2 | None | None | |
Read-and-write registers (holding registers) | 3, 23 | 6 | 16, 23 | |
Read-only registers (input registers) | 4, 23 | None | None |
Note that function code 23 not is implemented by this software (it is for reading and writing in same request).
Function codes 128 and larger are used by slaves to indicate errors.
Some extensions not covered by the official standard¶
- Scaling of register values
- Some manufacturers store a temperature value of 77.0 C as 770 in the register, to allow room for one decimal.
- Negative numbers (INT16 = short)
- Some manufacturers allow negative values for some registers. Instead of an allowed integer range 0-65535, a range -32768 to 32767 is allowed. This is implemented as any received value in the upper range (32768-65535) is interpreted as negative value (in the range -32768 to -1). This is two’s complement and is described at https://en.wikipedia.org/wiki/Two%27s_complement. Help functions to calculate the two’s complement value (and back) are provided in MinimalModbus.
- Long integers (‘Unsigned INT32’ or ‘INT32’)
- These require 32 bits, and are implemented as two consecutive 16-bit registers. The range is 0 to 4294967295, which is called ‘unsigned INT32’. Alternatively negative values can be stored if the instrument is defined that way, and is then called ‘INT32’ which has the range -2147483648 to 2147483647. Unfortunately the byte order might differ between manufacturers of Modbus instruments.
- Long long integers (‘Unsigned INT64’ or ‘INT64’)
- These require 64 bits, and are implemented as four consecutive 16-bit registers. The range is 0 to 18446744073709551615, which is called ‘unsigned INT64’. Alternatively negative values can be stored if the instrument is defined that way, and is then called ‘INT64’ which has the range -9223372036854775808 to 9223372036854775807. Unfortunately the byte order might differ between manufacturers of Modbus instruments.
- Floats (single or double precision)
- Single precision floating point values (binary32) are defined by 32 bits (4 bytes), and are implemented as two consecutive 16-bit registers. Correspondingly, double precision floating point values (binary64) use 64 bits (8 bytes) and are implemented as four consecutive 16-bit registers. How to convert from the bit values to the floating point value is described in the standard IEEE 754, as seen in https://en.wikipedia.org/wiki/Floating_point. Unfortunately the byte order might differ between manufacturers of Modbus instruments.
- Strings
- Each register (16 bits) is interpreted as two ASCII characters (each 1 byte = 8 bits). Often 16 consecutive registers are used, allowing 32 characters in the string. Unicode/UTF-8 is typically not supported.
- 8-bit registers
- For example Danfoss use 8-bit registers for storage of some settings internally in the instruments. The data is nevertherless transmitted as 16 bit over the serial link, so you can read and write like normal (but with values limited to the range 0-255).
- 32-bit registers
- “Enron Modbus” allows larger registers where you can store 32 bits in a single register (instead of two consecutive 16 bit registers). Not supported by this software.
- Bit fields in integers
- Some manufacturers store multiple bits in a 16-bit register, instead of as individually adressable bits. This is also known as flag registers. See below for how to use them with this software.
- JBUS adressing
- From Eurotherm Modbus/ProfibusCommunications Handbook: “The JBUS protocol is identical in all respects but one to the Modbus protocol. The one difference concerns the parameter or register address. Both use a numeric index but the JBUS index starts at 0 while the Modbus index starts at 1.”
- Slave addresses larger than 255
- Sometimes the slave address is encoded in two bytes to allow values larger than 255. Not supported by this software.
Implemented functions¶
These are the functions to use for reading and writing registers and bits of your instrument. Study the documentation of your instrument to find which Modbus function code to use. The function codes (F code) are given in decimal in this table.
Data type in slave | Read | F code | Write | F code |
---|---|---|---|---|
Bit | read_bit() |
2 [or 1] | write_bit() |
5 [or 15] |
Bits
Simultaneous access
|
2 [or 1]
|
15
|
||
Register
Integer, possibly scaled
|
3 [or 4]
|
16 [or 6]
|
||
Long integer
(32 or 64 bits =
2 or 4 registers)
|
3 [or 4]
|
16
|
||
Float
(32 or 64 bits =
2 or 4 registers)
|
3 [or 4]
|
16
|
||
String
2 characters per register
|
3 [or 4]
|
16
|
||
Registers
Integers
|
3 [or 4]
|
16
|
See the API for MinimalModbus: API for MinimalModbus.
Byte order for data stored in serveral registers¶
Floats and long integers does not fit in a single 16-bit register, so typically consecutive registers are used. However different manufacturers store the bytes in different order.
The functions handling floats and long integers have a parameter for changing which byte order that is used.
Name | Description | Use | Example |
---|---|---|---|
Big endian (Motorola) | High order byte first | BYTEORDER_BIG | ABCD |
PDP endian (PDP-11) | Big endian with byte swap | BYTEORDER_BIG_SWAP | BADC |
? | Little endian with byte swap | BYTEORDER_LITTLE_SWAP | CDAB |
Little endian (Intel) | Low order byte first | BYTEORDER_LITTLE | DCBA |
The example column show how the bytes are ordered on the wire (assuming byte A is the most significant byte).
Read more on Modbus byte ordering in these articles:
Modbus implementation details¶
In Modbus RTU, the request message is sent from the master in this format:
- Slave address [1 Byte]
- Function code [1 Byte]. Allowed range is 1 to 127 (in decimal).
- Payload data [0 to 252 Bytes]
- CRC [2 Bytes]. It is a Cyclic Redundancy Check code, for error checking of the message
The response from the client is similar, but with other payload data.
Function code
(in decimal)
|
Payload data to slave
(Request)
|
Payload data from slave
(Response)
|
---|---|---|
1
Read bits (coils)
|
Start address [2 Bytes]
Number of coils [2 Bytes]
|
Byte count [1 Byte]
Value [k Bytes]
|
2
Read discrete inputs
|
Start address [2 Bytes]
Number of inputs [2 Bytes]
|
Byte count [1 Byte]
Value [k Bytes]
|
3
Read holding registers
|
Start address [2 Bytes]
Number of registers [2 Bytes]
|
Byte count [1 Byte]
Value [n*2 Bytes]
|
4
Read input registers
|
Start address [2 Bytes]
Number of registers [2 Bytes]
|
Byte count [1 Byte]
Value [n*2 Bytes]
|
5
Write single bit (coil)
|
Output address [2 Bytes]
Value [2 Bytes]
|
Output address [2 Bytes]
Value [2 Bytes]
|
6
Write single register
|
Register address [2 Bytes]
Value [2 Bytes]
|
Register address [2 Bytes]
Value [2 Bytes]
|
15
Write multiple bits (coils)
|
Start address [2 Bytes]
Number of outputs [2 Bytes]
Byte count [1 Byte]
Value [k Bytes]
|
Start address [2 Bytes]
Number of outputs [2 Bytes]
|
16
Write multiple registers
|
Start address [2 Bytes]
Number of registers [2 Bytes]
Byte count [1 Byte]
Value [n*2 Bytes]
|
Start address [2 Bytes]
Number of regist [2 Bytes]
|
23
Read and write multiple registers
|
?
|
?
|
For function code 5, the only valid values are 0000 (hex) or FF00 (hex), representing OFF and ON respectively.
It is seen in the table above that the request and response messages are similar for function code 1 to 4. The same can be said about function code 5 and 6, and also about 15 and 16.
For finding how the k Bytes for the value relates to the number of registers etc (n), see the Modbus documents referred to above.
Reading individual bits from a 16-bit register¶
Some manufacturers use 16-bit registers to store individual boolean values (bits), so with a single read from a single address, 16 booleans could be retrieved. This is sometimes called a flag register.
You need to read the register as an integer, and then extract the bit you are interested in. For example to extract the third bit from right:
registervalue = instrument.read_register(4143)
is_my_bit_set = (registervalue & 0b0000000000000100) > 0
or if using hexadecimal numbers in your code instead:
is_my_bit_set = (registervalue & 0x0004) > 0
More information on bit manipulation in Python, see the “Single bits” section of https://wiki.python.org/moin/BitManipulation
Known deviations from the standard¶
Some instruments:
- sets more than one bit in the response when one bit is requested.
- add an extra 0xFE byte after some messages.
MODBUS ASCII format¶
This driver also supports Modbus ASCII mode.
Basically, a byte with value 0-255 in Modbus RTU mode will in Modbus ASCII mode be sent as two characters corresponding to the hex value of that byte.
For example a value of 76 (dec) = 4C (hex) is sent as the byte 0x4C in Modbus
RTU mode. This byte happens to correspond to the character ‘L’ in the ASCII encoding.
Thus for Modbus RTU this byte is sent: b'\x4C'
, which could be interpreted as a
string of length 1 and would print as ‘L’.
The same value will in Modbus ASCII be sent as the string ‘4C’, which has a length of 2.
The frame format is slightly different for Modbus ASCII. The request message is sent from the master in this format:
- Start [1 character]. It is the colon
:
.- Slave Address [2 characters]
- Function code [2 characters]
- Payload data [0 to 2*252 characters]
- LRC [2 characters]. The LRC is a Longitudinal Redundancy Check code, for error checking of the message.
- Stop [2 characters]. The stop characters are carriage return (
'\r'
='\x0D'
) and line feed ('\n'
='\x0A'
).
Manual testing of Modbus equipment¶
Look in your equipment’s manual to find working communication examples.
You can make a small Python program to test the communication:
TODO: Change this to a RTU example
import serial
ser = serial.Serial('/dev/ttyUSB0', 19200, timeout=1)
print(ser)
ser.write(':010310010001EA\r\n')
print(repr(ser.read(1000))) # Read 1000 bytes, or wait for timeout
It should print something like:
Serial<id=0x9faa08c, open=True>(port='/dev/ttyUSB0', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)
:0103020136C3
Correspondingly for Modbus ASCII, change the write command to for example:
TODO: Verify
ser.write(':010310010001EA\r\n')
It should then print something like:
Serial<id=0x9faa08c, open=True>(port='/dev/ttyUSB0', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)
:0103020136C3
It is also easy to test Modbus ASCII equipment from Linux command line. First must the appropriate serial port be set up properly:
- Print port settings:
stty -F /dev/ttyUSB0
- Print all settings for a port:
stty -F /dev/ttyUSB0 -a
- Reset port to default values:
stty -F /dev/ttyUSB0 sane
- Change port to raw behavior:
stty -F /dev/ttyUSB0 raw
- and:
stty -F /dev/ttyUSB0 -echo -echoe -echok
- Change port baudrate:
stty -F /dev/ttyUSB0 19200
To send out a Modbus ASCII request (read register 0x1001 on slave 1), and print out the response:
cat /dev/ttyUSB0 &
echo -e ":010310010001EA\r\n" > /dev/ttyUSB0
The reponse will be something like:
:0103020136C3