In the field of industrial control, MODBUS-RTU is one of the most universal and time-tested communication protocols. It acts like the “Mandarin” among devices, allowing instruments, PLCs, and controllers from different manufacturers to communicate smoothly.
Today, I will set aside complex theories and directly guide you through the four most commonly used function codes:01, 03, 05, 06, combined with practical code examples on the PIC microcontroller, to help you thoroughly grasp the essence of MODBUS-RTU.
1. Simple Introduction to MODBUS-RTU Protocol
You can think of MODBUS communication as an ordered question-and-answer:
Master (Master) asks: “Slave 1, what is the status of your coil?”
Slave (Slave) replies: “Slave 1 replies, my coil status is XXXX.”
Protocol Frame Format (Key Point): Both questions and answers follow the structure below, which is fundamental for analyzing and debugging all issues.
|
Slave Address |
Function Code |
Data |
CRC Checksum |
|
1 Byte |
1 Byte |
N Bytes |
2 Bytes |
Slave Address: 0 is the broadcast address, 1-255 are device addresses.
Function Code: Indicates what action the slave should perform.
Data: The specific content of the request or reply.
CRC Checksum: Ensures that data is not corrupted during transmission (low 8 bits first, high 8 bits last).
2. Detailed Explanation of the Four Core Function Codes
1. Function Code 01 (Read Coils) – Read Coil Status
Function: Reads the status of a group of digital outputs (DO), such as the activation or deactivation of a relay.
Master Request Frame (e.g., read from Slave 1, starting at coil address 0x0000 for 4 coil statuses)
|
Address |
Function Code |
Start Address High8 Bits |
Start Address Low8 Bits |
Quantity High8 Bits |
Quantity Low8 Bits |
CRC16 |
|
0x01 |
0x01 |
0x00 |
0x00 |
0x00 |
0x04 |
0x31CA |
Slave Normal Reply (assuming the statuses of 4 coils are: ON, OFF, ON, OFF)
|
Address |
Function Code |
Byte Count |
Data (Coil Status) |
CRC16 |
|
0x01 |
0x01 |
0x01 |
0x05 (Binary 0000 0101) |
0xE1C8 |
Note: The binary of data 0x05 is 0000 0101, which corresponds to coils 1ON, 2OFF, 3ON, 4OFF. If the number of returned coils is not a multiple of 8, the remaining bits in the last data byte are filled with zeros, and the byte count indicates the total number of data bytes.
2. Function Code 05 (Write Single Coil) – Force Single Coil
Function: Forcefully changes the state of a single digital output, commonly used to activate or deactivate a relay.
Master Request Frame (forcefully setting Slave 1’s coil at address 0x0002 to ON)
|
Address |
Function Code |
Output Address High8 Bits |
Output Address Low8 Bits |
Output Value High8 Bits |
Output Value Low8 Bits |
CRC16 |
|
0x01 |
0x05 |
0x00 |
0x02 |
0xFF |
0x00 |
0x8C3A |
Note: Forcefully ON is fixed at 0xFF00, and forcefully OFF is fixed at 0x0000.
Slave Normal Reply: Replies with the same data sent by the master, indicating successful execution.
|
Address |
Function Code |
Output Address High8 Bits |
Output Address Low8 Bits |
Output Value High8 Bits |
Output Value Low8 Bits |
CRC16 |
|
0x01 |
0x05 |
0x00 |
0x02 |
0xFF |
0x00 |
0x8C3A |
3. Function Code 03 (Read Holding Registers) – Read Holding Registers
Function: Reads device parameters, collected data, etc., and is one of the most frequently used function codes. For example, reading temperature, pressure, flow, etc.
Master Request Frame (reading from Slave 1, starting at address 0x0000 for 2 registers)
|
Address |
Function Code |
Start Address High8 Bits |
Start Address Low8 Bits |
Quantity High8 Bits |
Quantity Low8 Bits |
CRC16 |
|
0x01 |
0x03 |
0x00 |
0x00 |
0x00 |
0x02 |
0xC40B |
Slave Normal Reply (assuming the values of the two registers are 0x1234, 0x5678)
|
Address |
Function Code |
Byte Count |
Data1 High8 Bits |
Data1 Low8 Bits |
Data2 High8 Bits |
Data2 Low8 Bits |
CRC16 |
|
0x01 |
0x03 |
0x04 |
0x12 |
0x34 |
0x56 |
0x78 |
0x1FE1 |
4. Function Code 06 (Write Single Register) – Preset Single Register
Function: Modifies a parameter of the device, such as setting target temperature, pressure limits, etc.
Master Request Frame (changing the value of the register at address 0x0001 of Slave 1 to 0x55AA)
|
Address |
Function Code |
Register Address High8 Bits |
Register Address Low8 Bits |
Data High8 Bits |
Data Low8 Bits |
CRC16 |
|
0x01 |
0x06 |
0x00 |
0x01 |
0x55 |
0xAA |
0x12E4 |
Slave Normal Reply: Similarly, replies with the same command from the master, indicating successful modification.
3. PIC Microcontroller MODBUS-RTU Code
Below is part of the main program:
#include<pic16f1947.h> // Taking PIC16F1947 as an example
unsigned char address_485=1;// Default slave address is 1
unsigned char recive[100]; // Slave receive data buffer of 100 bytes
unsigned int crc_hl; //16 bit two-byte checksum
void calccrc(uchar crcbuf) // Checksum algorithm subroutine
{
uchar i;
crc_hl=crc_hl^crcbuf;
for(i=0;i<8;i++)
{
uchar TT;
TT=crc_hl&1;
crc_hl=crc_hl>>1;
crc_hl=crc_hl&0x7fff;
if(TT==1)
crc_hl=crc_hl^0xa001;
crc_hl=crc_hl&0xffff;
}
}
void main()
{
unsigned char i,j;
// Initialization
while(1)
{
if(x) // x is the condition to determine successful communication reception of a data packet
{
crc_hl=0xffff;
for(i=0; i<6; i++)
{
calccrc(recive[i]);
}
if((recive[0]== address_485)&&(recive[1]==0x03)&&(recive[6]==(crc_hl&0xff))&&(recive[7]==(crc_hl>>8))) // Check address, function code, and checksum
{
//1 Determine register address and fill in the data to reply
//2 Checksum calculation
//3 Send the data to reply and checksum to the master
// For function codes 05, 06, directly send the data to the master without needing 2 these two steps
}
}
}
MODBUS-RTU physical layer typically uses RS-485. A stable and reliable RS-485 circuit is essential for communication.
The following is the RS485 interface circuit diagram I have been using:
For detailed introduction, please refer to my other article: In-Depth Analysis of RS-232, RS-485, RS422 Bus and Practical Protection Circuit
By mastering the four core function codes: 01, 03, 05, 06, you can solve over 80% of MODBUS-RTU application scenarios. The protocol itself is not difficult; the key lies in rigorous implementation and protection against field interference.
If you are interested in this article or have any questions, feel free to leave a comment.
More valuable content is coming, let’s progress together in the world of microcontrollers.