Resources
modbus.org Modbus Application Protocol
Describes function codes
modbus.org Modbus over Serial Line Specification and Implementation Guide
Register Addressing
The Modbus register number should really be thought of as an offset rather than an address, as different vendors can use different register addressing schemes. Firstly registers may start from 0 for some devices and 1 for others. The register addressing always starts from 0 in the Modbus protocol, but for some devices such as PLC’s it may refer to address 1. Modbus also uses tables to hold different values and the modbus address sent is an offset within the particular table:
Coil/Register Numbers Data Addresses Type Table Name
1-9999 0000 to 270E Read-Write Discrete Output Coils
10001-19999 0000 to 270E Read-Only Discrete Input Contacts
30001-39999 0000 to 270E Read-Only Analog Input Registers
40001-49999 0000 to 270E Read-Write Analog Output Holding Registers
So, using command which accesses Analog Output Holding Register 40001 is accomplished by specifying register number 0 in the modbus register number field.
A good way to specify register numbers to avoid potential for confusion:
Register 99, Address 40100
Function Codes / Message Type
Message Type Action Table Name
0x01 Read Discrete Output Coils
0x02 Read Discrete Input Contacts
0x03 Read Analog Output Holding Registers
0x04 Read Analog Input Registers
0x05 Write single Discrete Output Coil
0x06 Write single Analog Output Holding Register
0x10 Write multiple Analog Output Holding Registers
0x0F Write multiple Discrete Output Coils
Data Types
A register is 16 bits and can store the following:
A 16-bit unsigned integer
A 16-bit signed integer
A two character ASCII string
A discrete on/off value
To store larger values, for instance 32bit values, float values, ASCII strings, etc simply use successive registers.
Byte Ordering
The Modbus specification doesn’t define exactly how the data is stored in the registers. Some manufacturers use HighByte|LowByte and others use LowByte|HighByte.
Similarly when registers are combined to represent 32-bit data types some devices store the high 16 bits in the first register and the remaining low word in the second while others do the opposite.
CRC (Cyclic Redundancy Check)
If a slave receives a packet with an invalid CRC no response should be sent.
How to calculate the CRC:
const UINT16 CRCTable[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };
UINT16 ModbusCalculateCrc (BYTE *pTxBuffer, WORD Length)
{
unsigned char ucTemp;
UINT16 CrcWord = 0xFFFF;
while (Length--)
{
ucTemp = (*pTxBuffer++) ^ (BYTE)(CrcWord & 0x00ff);
CrcWord >>= 8;
CrcWord ^= CRCTable[ucTemp];
}
return(CrcWord);
}
Modbus RTU Exception Error Response
The function Code byte value is returned with bit 7 set to indicate exception.
Following this a exception code is sent:
0x01 Illegal Function (The function code received in the query is not an allowable action for the slave)
0x02 Illegal Data Address (The data address received in the query is not an allowable address for the slave)
0x03 Illegal Data Value (A value contained in the query data field is not an allowable value for the slave)
0x04 Slave Device Failure (An unrecoverable error occurred while the slave was attempting to perform the requested action)
0x05 Acknowledge
0x06 Slave Device Busy
0x07 Negative Acknowledge (the slave cannot perform the program function received in the query)
0x08 Memory Parity Error
0x0A Gateway Path Unavailable
0x0B Gateway Target Device Failed to Respond
For example for a failed read of a word (16 bits) from a register the following is sent:
Byte 1 Modbus Device Address (1 to 247)
Byte 2 Function Code / Message Type (0x03)
Byte 3 Register Number (high byte)
Byte 4 Register Number (low byte)
Byte 5 Data Length (high byte, specified in Words)
Byte 6 Data Length (low byte)
Byte 7 CRC of bytes 1 to 6 (high byte)
Byte 8 CRC of bytes 1 to 6 (low byte)
And this is returned
Byte 1 Modbus Device Address
Byte 2 Function Code / Message Type (0x83)
Byte 3 The Exception Code
Byte 4 CRC of bytes 1 to 5 (high byte)
Byte 5 CRC of bytes 1 to 5 (low byte)