Modbus

Modbus is a well known standard to communicate to devices. The modbus specification is free available from http://www.modbus.org

Different media is available:

  1. Serial communication point to point via RS232

  2. Multidrop bus RS485

  3. TCP/IP Ethernet port 502 (and for non root users 1502. Non root users may or may not run in trouble with 502)

Terms and definitions

In simple terms, modbus has a server (slave) that has been polled by a master (client). The data exchanged can be either bits or 16bit wide words. Modbus supports the following data objects, that can exist multiple times in a device and therefore an address is used to select:

  1. Coils are bit outputs, that probably traditionally controlled relays coils. Their value can be read back.

  2. Discrete inputs are bit inputs

  3. Input registers are 16 bit wide

  4. Holding registers are 16 bit wide output registers that can be read back.

Inside the modbus protocol the address can be a value starting from 0. To know what is addressed the address plus the data object (Coils, Discrete Input, Input register or Holding register) must be specified as well. Occasionally an additional number gets added to the address defining the data object. So the following assignment results, where the first number is just a prefix and does not take part of the modbus address:

0xxxx - Coils (outputs) can be read and written (bit oriented)
1xxxx - Discrete inputs, ( input_status) can be read (bit oriented)
3xxxx - Input registers (it is not a bug 3 and not 2 is the prefix), can be read (16bit oriented)
4xxxx - Holding registers (output registers) can be read and written (16bit oriented)
A special feature is that those object addresses can be considered as be in a table and those tables could overlap, so writing a single bit might affect a single bit in a register.

An other variation might exist, the lowest modbus address is 0, inside documentation of a device it might be 1. This is just a redefinition or mapping issue.

Commands are:

  1. Write Single Coil (function code 0x05) means write an output bit, probably the first modbus implementations used relays to exchange data

  2. Read Coils (function code 0x01) means read back the status of the output bits

  3. Read Discrete Inputs (function code 0x02) means read the input bits

Data is exchanged in the following formats:

  1. Protocol Data Unit (PDU) is the pure data free of addressing information

  2. Application Data Unit (ADU) is PDU plus addressing data and checksum. In case of Modbus TCP/IP, no check sum is there since this is already very well handled by TCP/IP.

Within the PDU there are two data files:

  1. The 8bit wide function Function code. Usually the function code is sent is echoed back. However on a failure, the most significant bit is set to indicated that the transmission of the data failed. This is called an exception.

  2. Contains the data to be transmitted. It can be data to be sent or to be received or in case of failure, it can be an exception code that indicates the reason. Due to historic reasons the data can be maximum 253 byte, since the size of the RS232/485 ADU was set to 256 byte, on TCP/IP the ADU size can be up to 260 bytes.

The data transmission mode must be per default RTU (Remote Terminal Unit) that uses binary data to be more compact, as an option it can also be human readable ASCII.

The addressing within TCP/IP is:

2 Bytes Transaction number that is incremented by every transaction
2 Bytes protocol header that is 0x0000 for modbus
2 Bytes length of the following data
1 Byte Unit identifier, to allow to route the modbus telegram within the TCP/IP computer to other devices attached to the computer (e.g. the TCP/IP computer could have many modbus RS485 nodes attached). Set to 0x00 or 0xff if not used

Libmodbus

Libmodbus is a library to get modbus support under Linux. First install libmodbus on the PC. The installation of the libraries does not necessarily install the sample code. Therefore get the archive (for Gentoo Linux /usr/portage/distfiles/libmodbus-<n.m.o>.tar.gz) and copy/extract the libmodbus archive to a directory where you like to work.

Goto the test directory where sample applications are. There read README. There are always two sample applications a client application that requests the connection and a server application that responses. Now compile them all with make or individually as:

gcc random-test-server.c -o random-test-server `pkg-config --libs --cflags libmodbus`

Note

If the pkg-config program does not find the desired files (as if the system administrator refuses to install libmodbus):

echo $PKG_CONFIG_PATH

The result shows if this environmental variable points to the files. If not, reinitialize the variable and export it (Without export the variable is set in the current process but not passed to the child process within later gcc is running):

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/

If the commands are put into a bash script then the script needs to be called as follows:

source ./<name of bash script>

Otherwise the variables are just exported to the process of the bash script but not to the console that calls the bash scripts. However the system administrator of the Linux computer should be fixed to set the environment variables correctly.

Running pkg-config --libs --cflags libmodbus will show -I/usr/include/modbus -lmodbus that needs to be told to gcc to find and use libmodbus.

To test you need to start first the server in a window and then the slave in an other window. The programs communicates just with the IP address 127.0.0.1 and therefore the data does not leave the computer.

ctx = modbus_new_tcp("127.0.0.1", 1502);

To communicate to an other device, the source code must be modified. Therefore check out the IP address and port and add it to the master code.

ctx = modbus_new_tcp("192.168.1.30", 502);

Now compile it gcc random-test-client.c -o random-test-client `pkg-config --libs --cflags libmodbus` and run it ./random-test-client Since it randomly reads, it try to read also from locations that do not exist and therefore lots of errors are produced.

Modbus with Python

For Python many implementations are available:

Freemodbus

The freemodbus package is more targeted to small cpu's not necessary running an operating system. See https://www.freemodbus.org/. There are sample demo projects for different microcontrollers as for the AVR family. However your specific AVR might not be directly supported, as the ATmega324P. To support it, you have to modify just the files in the subdirectory /port. Files to modify are:

  1. port.h

  2. portserial.c

Since the freemodbus packed is targeted to small microprocessors and such microprocessors do not have a huge amount of RAM, no modbus table is preset. Instead freemodbus allows to access the peripherals directly without the need to copy the data into RAM. The following functions are called to get and write the data:

  1. eMBRegInputCB

  2. eMBRegHoldingCB

  3. eMBRegCoilsCB

  4. eMBRegDiscrete

A parameter can be passed to the functions eMBRegCoilsCB and eMBRegCoilsCB to define if the function writes or reads.

Freemodbus supports RS485. With a #define RTS_ENABLE a pin of the microcontroller gets active that can control the direction of the RS485 driver chip (75176).

Support for modbus TCP/IP is there but this has a higher complexity since a TCP/IP stack is required.


Linurs Hosttech startpage