Browse our Blog. You will find multiple applications, solutions, code examples. Navigate using the tag cloud or search using specific criteris

Modbus RTU Master library for industrial automation

Introduction


In the Arduino automation area, the Modbus RTU protocol is a means of communication that allows the exchange of data between programmable logic controllers (PLC controller Arduino) and computers. Electronic devices can exchange information through serial lines using the Modbus protocol.

On this post, we will talk about how to use the Modbus RTU with our libraries. First of all, it would be good for you to have a look to this other post so you can get the information about what it is, its connection and characteristics. 

Communicating by MODBUS RTU through a RS485 serial interface (Seneca Z-D-in Module)

It is important to say that this type of communication has some limitations:

  • Large binary objects are not supported.

  • It does not exist a standard way for a node to find the description of a data object.

  • There is no way (except for Ethernet TCP-IP ) to know if there is a change in one of our peripheral devices. The only way to get this information is asking constantly and look for changes in the data, but this fact consumes bandwidth and network time in apps where bandwidth may be expensive, such as over a low-bit-rate radio link.

  • Restriction to addressing 254 devices on one data link, which limits the number of field devices that we can connect to our master (once again with Ethernet TCP/IP we can solve this problem).

  • The transmissions must be contiguous, which limits the types of remote communications  devices to those that can buffer data to avoid gaps in the transmission.

  • This protocol does not provides security against unauthorized commands or interception of data, but it is not usual to find those facts in industry.

In summary, Modbus RTU is used in serial communication and makes use of a compact, binary representation of the data for protocol communication. The RTU format follows the data with a cyclic redundancy-check checksum as an error tests mechanism to ensure the reliability of data. Modbus RTU is the most common implementation available for Modbus. This protocol message must be transmitted continuously without inter-character hesitations. Modbus messages are separated by idle periods.

Modbus object types

Communications 

Each device on a Modbus communication has a unique address.

The Modbus RTU works by RS-485 which is a single cable multi-drop network, only the node assigned as the Master may initiate a command. All the other devices are slaves and answer to requests and commands.

There are many modems and gateways that support Modbus, as it is a very simple and often copied protocol. Some of them were specifically designed for this protocol. Different implementations use wireline, wireless communication, such as in the ISM band, and even SMS or GPRS. One of the more common designs of wireless networks makes use of mesh networking. 

A Modbus command contains the Modbus address of the device it is intended for ( 1 to 247 ). Only the addressed device will respond and act on the command, even though other devices might receive it (an exception is specific broadcastable commands sent to node 0, which are acted on but not acknowledged). Also it is important to say that all Modbus commands contain checksum information to allow the recipient to detect transmission errors.

 

Modbus RTU format frame

 

Available functions/command codes 

Reading, writing and other operations are categorized as follows. Prominent entities within a Modbus slave are:

  • Coils: readable and writable, 1 bit ( on-off )

  • Discrete Inputs: readable, 1 bit ( on-off )

  • Inputs Registers: readable, 16 bits ( 0 to 65.535 ), essentially measurements and statuses

  • Holding Registers: readable and writable, 16 bits ( 0 to 65.535 ), essentially configuration values


Modbus function codes


Data format of requests and responses for main function codes


Here you'll find the details of data formats of most used function codes.

*Function code 1 ( read coils ) and function code 2 ( read discrete inputs ) 

Request :

  • Address of first coil/discrete input to read ( 16 - bit )

  • Number of coils/discrete inputs to read ( 16 - bit )

Normal response :

  • Number of bytes of coil/discrete input values to follow ( 8 bit ) 

  • Coil/discrete input values ( 8 coils/discrete inputs per byte )

We have to know that the value of each coil/discrete input is binary ( 0-off , 1-on ). First requested coil/discrete input is stored as least significant bit or first byte in reply. In case that the numer of coils/discrete inputs is not a multiple of 8, most significant bit of last byte will be stuffed with zeros. Because the byte count returned in the reply message is only 8 bits wide and the protocol overhead is 5 bytes, a maximum of 2008 ( 251 x 8 ) discrete inputs or coils can be read at once.


*Function code 5 ( force/write single coil ) 

Request :

  • Address of coil (16-bit)

  • Value to force/write: 0 for off and 65.280 ( FF00 in hexadecimal ) for on 


Normal response : the same as request.

*Function code 15 ( force/write multiple coils ) 

Request :

  • Address of first coil to force/write ( 16-bit )

  • Number of coils to force/write ( 16-bit)

  • Number of bytes of coil values to follow ( 8-bit)

  • Coil values ( 8 coil values per byte )

Value of each coil is binary ( 0-off,  1-on ). First requested coil is stored as least significant bit of first byte in request. If the number of coils isn't a multiple of 8, most significant bit of last byte should be stuffed with zeros. 

Normal response : 

  • Address of first coil ( 16-bit )

  • Number of coils ( 16-bit ) 

    *Function code 4 ( read input registers ) and function code 3 ( read holding registers )

Request :

  • Address of first register to read ( 16-bit )

  • Number of registers to read ( 16-bit )

Normal response : 

  • Number of bytes of register to read ( 16-bit )

  • Register values ( 16 bits per register )

Because the number of bytes for register values is 8-bit wide and maximum Modbus message size is 256 bytes, only 125 registers for Modbus RTU and 123.


*Function code 6 ( preset/write single holding register )

Request :

  • Address of holding register to preset/write ( 16-bit )

  • New value of the hlding register ( 16-bit )

Normal response : the same as request 

*Function code 16 ( preset/write multiple holding registers )

Request :

  • Address of first holding register to preset/write ( 16-bit )

  • Number of holding registers to preset/write ( 16-bit )

  • Number of bytes of register values to follow ( 8-bit )

  • New values of holding registers ( 16 bits per register )

Because register values are 2-bytes wide and only 127 bytes worth of values can be sent, only 63 holding registers can be preset/written at once.


Normal response :

  • Address of first preset/written holding register ( 16-bit )

  • Number of present/written holding registers ( 16-bit )



Main Modbus exception codes


Coil, discrete input, input register, holding register numbers and addresses

Entity numbers combine entity type and entity location within their description table.

Entity address is the starting address, as 16-bit value in the data part of the Modbus frame. As such its range goes from 0 to 65.535

In the traditional standard, numbers for those entities stard with a digit, followed by a number of 4 digits in the range 1-9.999 :
  • Coils numbers start with 0 and span from 00001 to 09999

  • Discrete input numbers start with 1 and span from 10001 to 19999

  • Input register numbers start with 3 and span from 30001 to 39999

  • Holding register numbers start with 4 and span from 40001 to 49999 

This results into addresses between 0 and 9.999 in data frames. For ex., in order to read holding registers, starting at number 40001, corresponding address in the data frame will be 0 with a function code of ( as seen above ). For holding registers starting at number 40100, address will be 99. etc...

This limits the number of addresses to 9.999 for each entity. A de facto referencing extends this to the maximum of 65.536. Bassically consists of adding one digit to the previous list:

  • Coil numbers span from 000001 to 065536

  • Discrete input numbers span from 100001 to 165536

  • Input register numbers span from 300001 to 365536

  • Holding register numbers span from 400001 to 465536

If you are using the extended referencing, all the number references must have exactly 6 digits. This avoids confusion between coils and other entities. For example, to know the difference between holding register #40001 and coil #40001 is the target, it must appear as #040001


Uses 

Some of the most common uses:

Data types :

  • IEEE floating-point number

  • 32-bit integer 

  • 8-bit data

  • Mixed data types

  • Bit fields in integers

  • Multipliers to change data to/from integer. 10, 100, 1000, 256 ...

Protocol extensions :

  • 16-bit slave addresses

  • 32-bit data size ( 1 address = 32 bits of data returned )

  • Word-swapped data 


Software

Modbus RTU Master with Arduino IDE

The Modbus RTU Master Module implements the Modbus RTU Master capabilities. 

#include <ModbusRTUMaster.h>


It is possible to use any hardware Serial Arduino stream:

  • RS-485

#include <RS485.h>

ModbusRTUMaster master(RS485);


  • RS-232

#include <RS232.h>

ModbusRTUMaster master(RS232);


Before using it, it is required to call the begin function in the setup for both the serial and the Modbus variable. It is a good practise to set the baudrate (default: 19200 bps) also in the Modbus variable to define the Modbus internal timeouts.

RS485.begin(9600, HALFDUPLEX, SERIAL_8E1);
master.begin(9600);


The functions to read and write slave values are:

readCoils(slave_address, address, quantity);
readDiscreteInputs(slave_address, address, quantity);
readHoldingRegisters(slave_address, address, quantity);
readInputRegisters(slave_address, address, quantity);
writeSingleCoil(slave_address, address, value);
writeSingleRegister(slave_address, address, value);
writeMultipleCoils(slave_address, address, values, quantity);
writeMultipleRegisters(slave_address, address, values, quantity);


Where :

  • slave_address is the Modbus RTU slave address.

  • address is the coil, digital input, holding register or input register address. Usually this address is the coil, digital input, holding register or input register number minus 1: the holding register number 40009 has the address 8.
    quantity is the number of coils, digital, holding registers or input registers to read/write.
  • value is the given value of the coil or holding registers on a write operation. Depending on the function the data type changes. A coil is represented by a bool value and a holding register is represented by a uint16_t value.

On a multiple read/write function the address argument is the first address. On a multiple write function the values argument is an array of values to write.

It is important to say that these functions are non-blocking, so they do not return the read value. They return true or  false depending on the current module state. If there is a pending Modbus request, they return false.  

// Read 5 holding registers from address 0x24 of slave with address 0x10
if (master.readHoldingRegisters(0x10, 0x24, 5)) {
	// OK, the request is being processed
} else {
	// ERROR, the master is not in an IDLE state
}

There is the function vailable()  to check for responses from the slave:

ModbusResponse response = master.available();
if (response) {
	// Process response
}


The ModbusResponse implements some functions to get the response information:

hasError();
getErrorCode();
getSlave();
getFC();
isCoilSet(offset);
isDiscreteInputSet(offset);
isDiscreteSet(offset);
getRegister(offset);
ModbusResponse response = master.available();
if (response) {
	if (response.hasError()) {
		// There is an error. You can get the error code with response.getErrorCode()
	} else {
		// Response ready: print the read holding registers
		for (int i = 0; i < 5; ++i) {
			Serial.println(response.getRegister(i);
		}
	}
}

 

The possible error codes are:

0x01 ILLEGAL FUNCTION
0x02 ILLEGAL DATA ADDRESS
0x03 ILLEGAL DATA VALUE
0x04 SERVER DEVICE FAILURE


Do you want more information?

Open Source technology allows you to develop your installations.

Just fill the form and we will contact you as soon as we can.

Send