The PDU and the code that handles it comprise the core of the Modbus Application Protocol Specification. This specification defines the format of the PDU, the various data concepts used by the protocol, the use of function codes to access that data, and the specific implementation and restrictions of each function code.
The Modbus PDU format is defined as a function code followed by an associated set of data. The size and contents of this data are defined by the function code, and the entire PDU (function code and data) cannot exceed 253 bytes in size. Every function code has a specific behavior that slaves can flexibly implement based on their desired application behavior. The PDU specification defines core concepts for data access and manipulation; however, a slave may handle data in a way that is not explicitly defined in the specification.
Accessing Data in Modbus and the Modbus Data Model
Modbus-accessible data is stored, in general, in one of four data banks or address ranges: coils, discrete inputs, holding registers, and input registers. As with much of the specification, the names may vary depending on the industry or application. For example, holding registers may be referred to as output registers, and coils may be referred to as digital or discrete outputs. These data banks define the type and access rights of the contained data. Slave devices have direct access to this data, which is hosted locally on the devices. The Modbus-accessible data is generally a subset of the device’s main memory. In contrast, Modbus masters must request access to this data through various function codes. The behavior of each block is described in Table 1.
Table 1. Modbus Data Model Blocks
These blocks give you the ability to restrict or permit access to different data elements and also to provide simplified mechanisms at the application layer to access different data types.
The blocks are completely conceptual. They may exist as separate memory addresses in a given system, but they may also overlap. For example, coil one may exist in the same location in memory as the first bit of the word represented by holding register one. The addressing scheme is entirely defined by the slave device, and its interpretation of each memory block is an important part of the device’s data model.
Data Model Addressing
The specification defines each block as containing an address space of as many as 65,536 (216) elements. Within the definition of the PDU, Modbus defines the address of each data element as ranging from 0 to 65,535. However, each data element is numbered from 1 to n, where n has a maximum value of 65,536. That is, coil 1 is in the coil block at address 0, while holding register 54 is at address 53 in the section of memory that the slave has defined as holding registers.
The full ranges allowed by the specification are not required to be implemented by a given device. For example, a device may choose not to implement coils, discrete inputs, or input registers and instead only use holding registers 150 through 175 and 200 through 225. This is perfectly acceptable, and invalid access attempts would be handled through exceptions.
Data Addressing Ranges
Although the specification defines different data types as existing in different blocks and assigns a local address range to each type, this does not necessarily translate into an intuitive addressing scheme for the purposes of documentation or understanding a given device’s Modbus-accessible memory. To simplify the discussion of memory block locations, a numbering scheme was introduced, which added prefixes to the address of the data in question.
For example, rather than referring to an item as holding register 14 at address 13, a device manual would refer to a data item at address 4,014, 40,014, or 400,014. In each case, the first number specified is 4 to represent holding registers, and the address is specified using the remaining numbers. The difference between 4XXX, 4XXXX, and 4XXXXX depends on the address space used by the device. If all 65,536 registers are in use, 4XXXXX notation should be used, as it allows for a range from 400,001 to 465,536. If only a few registers are used, a common practice is to use the range 4,001 through 4,999.
In this addressing scheme, each data type is assigned a prefix as shown in Table 2.
Table 2. Data Range Prefixes
Coils exist with a prefix of 0. This means that a reference of 4001 could refer to either holding register one or coil 4001. For this reason, all new implementations are recommended to use 6-digit addressing with leading zeros, and to note this in the documentation. Thus, holding register one is referenced as 400,001 and coil 4001 is referenced as 004,001.
Data Address Start Values
The difference between memory addresses and reference numbers is further complicated by the indexing selected by a given application. As mentioned previously, holding register one is at address zero. Typically, reference numbers are one-indexed, meaning that the start value of a given range is one. Thus, 400,001 translates literally to holding register 00001, which is at address 0. Some implementations choose to start their ranges at zero, meaning that 400,000 translates to the holding register at address zero. Table 3 demonstrates this concept.
||Number (1-indexing, standard)
||Number (0-indexing, alternative)
Table 3. Register Indexing Schemes
One-indexed ranges are common and strongly recommended. In either case, the start value for each range should be noted in documentation.
Large Data Types
The Modbus standard supplies a relatively simplistic data model that does not include additional data types outside of an unsigned word and bit value. Although this is sufficient for some systems, where the bit values correspond to solenoids and relays and the word values correspond to unscaled ADC values, it is insufficient for more advanced systems. As a result, many Modbus implementations include data types that cross register boundaries. The NI LabVIEW Datalogging and Supervisory Control (DSC) Module and KEPServerEX both define a number of reference types. For example, strings stored in a holding register follow the standard form (400,001) but are followed by a decimal, the length, and the byte ordering of the string (400,001.2H, a two character string in holding register 1 where the high byte corresponds to the first character of the string). This is required because each request has finite size, and so a Modbus master must know the exact bounds of the string rather than searching for a length or delimiter like NULL.
In addition to allowing access to data that crosses a register boundary, some Modbus masters support references to individual bits within a register. This is beneficial as is allows devices to combine data of every type in the same memory range without having to split binary data into the coil and discrete input ranges. This is usually referenced using a decimal point and the bit index or number, depending on the implementation. That is, the first bit in the first register may be 400,001.00 or 400,001.01. It is recommended that any documentation specify the indexing scheme used.
Multiregister data, like single-precision floating point value, can be easily transferred in Modbus by splitting the data across two registers. Because this is not defined by the standard, the endianness (or byte order) of this split is not defined. Although each unsigned word must be sent in network (big-endian) byte order to satisfy the standard, many devices reverse the byte order for multibyte data. Figure 2 shows an unusual but valid example of this.
Figure 2. Byte Order Swap for Multiword Data
It is up to the master to understand how the slave is storing information in memory and to decode it properly. It is recommended that documentation reflect the word order used by the system. Endianness can also be added as a system configuration option, with underlying encode and decode functions, if flexibility in implementation is required.
Strings can be easily stored in Modbus registers. For simplicity, some implementations require that string lengths be multiples of two, with any additional space filled with null values. Byte order is also a variable in string interactions. String format may or may not include a NULL as the final value. As an example of this variability, some devices may store data as shown in Figure 3.
Figure 3. Byte Order Reversal in Modbus Strings
Understanding Function Codes
In contrast to the data model that can vary significantly from device to device, function codes and their data are defined explicitly by the standard. Each function follows a pattern. First, the slave validates inputs like function code, data address, and data range. Then, it executes the requested action and sends a response appropriate to the code. If any step in this process fails, an exception is returned to the requestor. The data transport for these requests is the PDU.
The Modbus PDU
The PDU consists of a one-byte function code followed by up to 252 bytes of function-specific data.
Figure 4. The Modbus PDU
The function code is the first item to be validated. If the function code is not recognized by the device receiving the request, it responds with an exception. Should the function code be accepted, the slave device begins decomposing the data according to the function definition.
Because the packet size is limited to 253 bytes, devices are constrained on the amount of data that can be transferred. The most common function codes can transfer between 240 and 250 bytes of actual data from the slave data model, depending on the code.
Slave Function Execution
As defined by the data model, different functions are defined to access different conceptual blocks of data. A common implementation is to have codes access static memory locations, but other behaviors are available. For example, function code 1 (read coils) and 3 (read holding registers) may access the same physical location in memory. In contrast, function code 3 (read holding registers) and 16 (write holding registers) may access completely different locations in memory. Thus, the execution of each function code is best considered as part of the slave data model definition.
Regardless of the actual behavior performed, all slave devices are expected to follow a simple state diagram for each request. Figure 5 shows an example of this for code 1, read coils.
Figure 5. Read Coils State Diagram From the Modbus Protocol Specification
Each slave must validate the function code, the number of inputs, the starting address, the total range, and the execution of the slave-defined function that actually performs the read.
Although static address ranges are shown in the state diagram above, the needs of real-world systems can cause these to vary somewhat from the defined numbers. In some cases, slave devices cannot transfer the maximum number of bytes defined by the protocol. That is, rather than allowing a master to request 0x07D0 inputs, it can only respond with 0x0400. Similarly, a slave data model may define the range of acceptable coil values as address 0 through 500. If a master makes a request for 125 starting at address 0, this is OK, but if a master makes the same request starting at address 400, the final coil will be at address 525, which is out of range for this device and would result in exception 02 as defined by the state diagram.
Standard Function Codes
The definition of each standard function code is in the specification. Even for the most common function codes, there are inevitable mismatches between the functions enabled on the master and what the slave can handle. To address this, early versions of the Modbus TCP specification defined three conformance classes. The official Modbus Conformance Test Specification does not reference these classes and instead defines conformance on a per-function basis; however, they can still be convenient for understanding. It is recommended that any documentation follow the test specification and define their conformance by which codes they support, rather than by the legacy classifications.
Class 0 Codes
Class 0 codes are generally considered the bare minimum for a useful Modbus device, as they give a master the ability to read from or write to the data model.
||Read Multiple Registers
||Write Multiple Registers
Table 4. Conformance Class 0 Codes
Class 1 Codes
Class 1 function codes consist of the other codes necessary to access all of the types of the data model. In the original definition, this list included function code 7 (read exception). However, this code is defined by the current specification as a serial-only code.
||Read Discrete Inputs
||Read Input Registers
||Write Single Coil
||Write Single Register
||Read Exception Status (serial-only)
Table 5. Conformance Class 1 Codes
Class 2 Codes
Class 2 function codes are more specialized functions that are less commonly implemented. For example, Read/Write Multiple Registers may help reduce the total number of request-response cycles, but the behavior can still be implemented with class 0 codes.
||Write Multiple Coils
||Read File Record
||Write File Record
||Mask Write Register
||Read/Write Multiple Registers
Table 6. Conformance Class 2 Codes
Modbus Encapsulated Interface
The Modbus encapsulated interface (MEI) code, function 43, is used to encapsulate other data within a Modbus packet. At present, two MEI numbers are available, 13 (CANopen) and 14 (Device Identification).
Function 43/14 (Device Identification) is useful in that it allows for the transfer of up to 256 unique objects. Some of these objects are predefined and reserved, such as vendor name and product code, but applications can define other objects to transfer as generic data sets.
This code is not commonly implemented.
Slaves use exceptions to indicate a number of bad conditions, from a malformed request to incorrect inputs. However, exceptions can also be generated as an application-level response to an invalid request. Slaves do not respond to requests issued with an exception. Instead, the slave ignores incomplete or corrupted requests and begins waiting for a new incoming message.
Exceptions are reported in a defined packet format. First, a function code is returned to the requesting master equal to the original function code, except with its most significant bit set. This is equivalent to adding 0x80 to the value of the original function code. In lieu of the normal data associated with a given function response, exception responses include a single exception code.
Within the standard, the four most common exception codes are 01, 02, 03, and 04. These are shown in Table 7 with standard meanings by each function.
||The received function code is not supported. To confirm the original function code, subtract 0x80 from the returned value.
||The request attempted to access an invalid address. In the standard, this can happen only if the starting address and the requested number of values exceeds 216. However, some devices may restrict this address space in their data model.
||The request had incorrect data. In some cases, this means that there was a parameter mismatch, for example between the number of registers sent and the “byte count” field. More commonly, the master requested more data than either the slave or protocol allows. For example, a master may read only 125 holding registers at a time, and resource-limited devices may restrict this value to even fewer registers.
||An unrecoverable error occurred while attempting to process the request. This is a catchall exception code that indicates the request was valid, but the slave could not execute it.
Table 7. Common Modbus Exception Codes
The state diagram for every function code should cover at least exception code 01 and usually includes exception code 04, 02, 03, and any other defined exception codes are optional.