Modbus is an industrial communication protocol used for exchanging data between devices in automation systems, offering simplicity and reliability for connecting and controlling industrial devices.
There are three main types of Modbus, Modbus RTU, Modbus TCP and Modbus ASCII.
Modbus RTU is characterised by it use of a RS-232 or RS-485 connection, which is a type of serial communication.
Modbus TCP uses Ethernet as the underlying communication medium, instead of the serial communication Modbus RTU uses. It's essentially a Modbus RTU packet wrapped in a TCP wrapper, so I will explain more towards the end.
Modbus ASCII is essentially Modbus RTU but every character of the Modbus RTU request and response is converted into the characters ASCII code. It's incredibly rare, so I won't mention it from now.
During the rest of the explanation I will mostly talk about Modbus RTU.
Everything in Modbus starts with the frame. It's split up into two parts, a Application Data Unit (ADU), which includes the other part, a Protocol Data Unit (PDU). I will come back to this later.
A Modbus frame looks like this (in Hexadecimal) - 11 06 0001 0003 9A9B
Let's examine each part of the frame in sections.
As mentioned before, Modbus works on a Master/Slave relationship. Each slave is given a unique unit address from 1 to 247 to identify itself. When the master requests data, the first byte it sends is the Slave address. This way each slave knows after the first byte whether or not to ignore the message.
In the shown frame, 11 is given (17 in decimal). This address is in 8 bits, meaning that the address can run from 00 to FF (0 to 255 in decimal). This means that the Master is sending a message to the Slave with ID 17.
This is the meat 🥩 and potatos 🥔 of Modbus.
Within the Slave, data is stored in four different tables. Two tables store on/off discrete values (coils) and two store numerical values (registers). The coils and registers each have a read-only table and read-write table.
Each of the different tables have different names.
We've established the different data types, but how do we actually read and write to those values? The second byte sent by the Master (see the previous frame) is the Function code. This number tells the slave which table to access and whether to read from or write to the table.
Code | Action | Table Name |
01 (01 hex) | Read | Discrete Output Coils |
05 (05 hex) | Write Single | Discrete Output Coil |
15 (0F hex) | Write multiple | Discrete Output Coils |
02 (02 hex) | Read | Discrete Input Contacts |
04 (04 hex) | Read | Analog Input Registers |
03 (03 hex) | Read | Analog Output Holding Registers |
06 (06 hex) | Write Single | Analog Output Holding Register |
16 (10 hex) | Write Multiple | Analog Output Holding Registers |
This is the last part of the frame. It is two bytes added to the end of every modbus message for error detection. Every byte in the message is used to calculate the CRC. The receiving device also calculates the CRC and compares it to the CRC from the sending device.
If even one bit in the message is received incorrectly, the CRCs will be different and an error will result.
Let's finish this section off by revisiting the frame. I mentioned before that a Modbus frame looks like this (in Hexadecimal) - 11 06 0001 0003 9A9B
This is the Write a Single Register command (FC 06). Let's go through the entire frame, now that we know what is what.
Okay, now that we've sent the command, what now? The normal response is an echo of the query from the slave, returned after the register contents have been written. It's as followed (each response is different for each function code).
Obviously I can't show you the request/response of every function code, so have a look here. You can read about each function code and it's associated requests/responses.
Okay we've sent the command and we've just realised we did something we shouldn't of. Will I write to the wrong address with the wrong function code, causing everything to break? No! Modbus will only let us do something that is allowed by the protocol.
Let's say the request is received without a communication error, but cannot be processed by the slave for another reason.
The slave replies with an exception response.
There a lots of these, you can read all about them here.
Some of the fun ones are -
Instead of the usual response mentioned above, you'll get something like this -
We've pretty much covered everything, but one thing I wanted to cover is RTU vs TCP. Simply put, Modbus TCP is a Modbus RTU message transmitted with a TCP/IP wrapper and sent over Ethernet. The Slaves does not have a SlaveID since it uses an IP Address instead (there are some technicalities around this but I won't get into it).
A new 7-byte header called the MBAP header (Modbus Application Header) is added to the start of the message. This header has the following data:
The Slave ID and CRC are removed from the packet.