Modbus data is oriented around the concept of tags, or variables. These data elements—holding registers, input registers, coils, and discrete inputs—are called by different names within the protocol, but the idea is the same. These tags are data elements that can be written to or read from at any time, but provide only their current value. This makes sense for many applications; however, implementing concepts like events, messages, or first-in-first-out memory buffers (FIFOs) can be difficult.
Modbus defines four banks of data—the four types mentioned above—that store the vast majority of the information transported by the protocol. Each request from a master can perform a single operation on a single bank of data. That is, a single request can read from the coils bank or write to the coils bank, but not both. There are exceptions, however. For example, function code 23 allows a master to write holding registers and read holding registers in a single request/response cycle. However, this is not a commonly implemented function code. Both master and slave device documentation should be checked to verify whether this function code is available.
It is also critical to understand that data in a given bank can only be accessed sequentially, and up to the packet size limit of the Modbus protocol. This limit is typically about 240 to 250 bytes of payload data, but depends on the specific function used. This makes it extremely important to carefully design the register banks.
For example, a given application may require the transfer of significant amounts of data at a fast rate. In this case, minimizing the number of requests required is desirable. Because data must be accessed sequentially, it would make sense for this application to concentrate as much data as possible in a single bank of registers and to pack data together with the minimum number of unused data elements.
Another application may have a more complex slave that allows for some local control. In this situation, there may be some data that is read at a fast rate and some data that is monitored periodically. In this case, data should be grouped by transfer rate. The quickly updating data should be concentrated in one place, while slowly updating data should be organized into different blocks to make it easier for a single request to read or update a particular data set.
To demonstrate, consider a set of five quickly updating data values (F1-F5) and five slowly updating values (S1-S5). In addition, S1, S2, and S3 correspond to one code module while S4 and S5 correspond to another code module—that is, they may be updated at different times. In Figure 3, the ideal organization for this case gives the master the ability to read the quickly updating data in a single request and then, as needed, read the other slowly updating data sets.
Figure 3. Well-Organized Data
If care is not taken, a data set could easily be organized as shown in Figure 4. In this case, it’s moved from a little over 20 requests per second to over 80 requests per second. It is actually more efficient for a small data set to read everything in a single block. However, these approaches quickly become unscalable for a complex system.
Figure 4. Poor Data Organization
Although internally, data in each Modbus bank is accessed using an address value from 0 to 65,535, it is more common for documentation to refer to data elements using a common addressing scheme. In such a scheme, the address of the data element is prefixed by a number that defines the bank to be accessed. Table 1 shows these prefixes.
Table 1. Data Access Prefixes
Depending on the particular documentation being referenced, addressing starts at either zero or one and uses a fixed width of between four and six values. That is, different documents could all refer to the same integer holding register as 4005, 40004, and 400005. The documentation should define the particular addressing scheme used, and any new system development should publish this information.