Filling a ModbusMessage
If you are not able to use one of the constructors as are described above, you can use a number of calls to add or modify the contents of a ModbusMessage
First, there are setMessage()
calls that have the same functionality as the constructors, but with one exception: these calls will return an Error
value describing what may have happened when creating the message. Error
is one of the error codes defined in ModbusTypeDefs.h
:
enum Error : uint8_t {
SUCCESS = 0x00,
ILLEGAL_FUNCTION = 0x01,
ILLEGAL_DATA_ADDRESS = 0x02,
ILLEGAL_DATA_VALUE = 0x03,
SERVER_DEVICE_FAILURE = 0x04,
ACKNOWLEDGE = 0x05,
SERVER_DEVICE_BUSY = 0x06,
NEGATIVE_ACKNOWLEDGE = 0x07,
MEMORY_PARITY_ERROR = 0x08,
GATEWAY_PATH_UNAVAIL = 0x0A,
GATEWAY_TARGET_NO_RESP = 0x0B,
TIMEOUT = 0xE0,
INVALID_SERVER = 0xE1,
CRC_ERROR = 0xE2, // only for Modbus-RTU
FC_MISMATCH = 0xE3,
SERVER_ID_MISMATCH = 0xE4,
PACKET_LENGTH_ERROR = 0xE5,
PARAMETER_COUNT_ERROR = 0xE6,
PARAMETER_LIMIT_ERROR = 0xE7,
REQUEST_QUEUE_FULL = 0xE8,
ILLEGAL_IP_OR_PORT = 0xE9,
IP_CONNECTION_FAILED = 0xEA,
TCP_HEAD_MISMATCH = 0xEB,
UNDEFINED_ERROR = 0xFF // otherwise uncovered communication error
};
If parameter checks will find a deviation from the standard, the returned Error
will tell you what it was.
For completeness, here are the setMessage()
variants:
-
no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11)
Error setMessage(uint8_t serverID, uint8_t functionCode);
-
one uint16_t parameter (FC 0x18)
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1);
-
two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2);
-
three uint16_t parameters (FC 0x16)
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint16_t p3);
-
two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10)
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint16_t *arrayOfWords);
-
two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f)
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2, uint8_t count, uint8_t *arrayOfBytes);
-
generic method for preformatted data ==> count is counting bytes!
Error setMessage(uint8_t serverID, uint8_t functionCode, uint16_t count, uint8_t *arrayOfBytes);
There is in fact an eighth function, that is not available as a constructor. It will set the ModbusMessage
to be an error response, signalling the given Error
:
- Error response method
Error setError(uint8_t serverID, uint8_t functionCode, Error errorCode);
Note
a call to one of the set...()
methods will erase the previous contents of the ModbusMessage
before filling it!
Adding data to a message
If a message has been set up - for instance with server ID and function code only, more data can be added to it.
uint16_t add(T value);
add()
takes the integral value
of type T
and appends it MSB-first to the existing message. This applies to uint8_t
, uint16_t
etc. as well as int
, unsigned int
etc. uint8_t b = 9; msg.add(b);
for instance will append one single byte 09
to the message, whereas uint32_t i = 122316; msg.add(i);
will append four bytes: 00 01 DD CC
(the hexadecimal representation of 122316, MSB-first!).
The add()
method will return the number of bytes in the message after the addition.
Warning
expressions like a ? 1 : 2
are converted to int
by the compiler. So please be sure to cast expressions if you would like to have a shorter representation. msg.add(a ? 1 : 2);
most likely will add 00 00 00 01
or 00 00 00 02
to your message, while msg.add((uint8_t)(a ? 1 : 2));
will append 01
or 02
- which may be what you wanted instead.
uint16_t add(T v, ...);
add()
may also be used with more than one integral value and will add the given values one by one. The returned size is that after adding all of the values in a row.
uint16_t add(float v);
and uint16_t add(double v);
These two variants are extending the range of “add-able” data types to float
and double
. The values are added to the message in “pure IEEE754” MSB-first sequence, that is opposite of the order the ESP32 is using internally.
User-defined float
and double
byte order
To cope with Modbus devices not using the IEEE754 byte sequence to communicate float
or double
values, both add()
functions for these data types are supporting an optional second parameter defining the byte order. This parameter is constructed as an ORed combination of these four values:
SWAP_BYTES
SWAP_REGISTERS
SWAP_WORDS
(double
only)SWAP_NIBBLES
Given the normalized byte order of “0, 1, 2, 3” (“0, 1, 2, 3, 4, 5, 6, 7” for a double
), the result of these values is as follows:
SWAP_BYTES
: “1, 0, 3, 2” (“1, 0, 3, 2, 5, 4, 7, 6”)SWAP_REGISTERS
: “2, 3, 0, 1” (“2, 3, 0, 1, 6, 7, 4, 5”)SWAP_WORDS
: only valid fordouble
- “4, 5, 6, 7, 0, 1, 2, 3,”SWAP_NIBBLES
will change the order of the two 4-bit nibbles in a byte. “0xAB” will be “0xBA”, “0x12” gets “0x21” and so on.
A combination of values will yield the combined effect. SWAP_BYTES|SWAP_REGISTERS
for instance will turn a “0, 1, 2, 3” float
into “3, 2, 1, 0”.
uint16_t add(uint8_t *data, uint16_t dataLen);
This version of add()
will append the given buffer of dataLen
bytes pointed to by data
. It also does return the final size of the message.
void append(ModbusMessage& m);
and
void append(std::vector<uint8_t>& m):
With append()
you may add in another ModbusMessage
’s content or that of a std::vector
of bytes.
void push_back(const uint8_t& value);
Like the std::vector
method of the same name, push_back()
will append the given byte to the end of the existing message.