Proper Implementation of slave devices is crucial to operating properly
Reading a holding register can be done using the dynamic_modbus_master::slave::SlaveDevice::readHolding()
method.
The simplest implementation can look like this:
However the above implementation doesn't handle errors in an appropriate manner so it can be extended to be:
In cases where values are in a different byte order or have a different endian-ness this can also be adjusted here.
The above-described mechanism works for all datatypes that can be represented in a natural number of Modbus-Registers. Therefore, it is also possible to bulk-read a group of registers using a custom datatype:
The bulk-read mechanism only works as long as the registers that are to be read are all in one block. Any unused registers must also be read and then discarded or the block to be read may be split into different units:
Register | Description | Large Block | Small Blocks |
---|---|---|---|
0 | Temp hundredths | X | X |
1 | Temp Units | X | X |
2 | Error Count Low | X | X |
3 | Error Count High | X | X |
4 | Unused | X | |
5 | Unused | X | |
6 | Voltage Low | X | X |
7 | Voltage High | X | X |
8 | Current Low | X | X |
9 | Current High | X | X |
Even in the relatively simple example device above, splitting the read into two smaller blocks may be beneficial, since this would spread the read into two and use 8-Bytes instead of 10. Additionally, this would allow to read the registers only when they are needed.
The mechanisms described for reading and writing Holding Registers work for Coil Registers as well with one exception: You cannot attempt to read a single boolean Value from multiple Coil Registers!
So SlaveReturn<bool> readCoil = SlaveDevice(1,1).readCoils<bool>(1,1);
would work correctly and SlaveReturn.data
would contain a correct value, however attempting SlaveReturn<bool> readCoil = SlaveDevice(1,1).readCoils<bool>(1,2);
would not work correctly and SlaveReturn.error
would contain a ModbusError::INVALID_ARG
since a read of multiple Coil Registers into a single Boolean was attempted. The same goes for writing Coil Registers.
An example device reading coils could look like this:
And a device that writes Coils could look like this:
Usage of the Input Registers follows the same pattern as the Coil and Holding Registers, with the caveat that they are READ-ONLY, there are no functions that would allow the user to write to an Input Register since the underlying registers cannot be written to in the first place.
If the device's response indicates an Exception the driver automatically attempts to identify which one occurred and returns the corresponding dynamic_modbus_master::ModbusError which can then be handled appropriately by the user.
For certain use cases the above API might not be sufficient, it is however possible to achieve similar functionality by implementing the dynamic_modbus_master::slave::SlaveDeviceIfc Interface. As long as this interface is implemented by a custom class, usage of Slave Devices should be the same from an application perspective.
For an example on how to implement this interface see dynamic_modbus_master::slave::SlaveDevice.