Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Modbus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,3 +811,10 @@ Modbus::StatusCode ModbusInterface::readFIFOQueue(uint8_t /*unit*/, uint16_t /*f
return Modbus::Status_BadIllegalFunction;
}
#endif

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
Modbus::StatusCode ModbusInterface::readDeviceIdentification(uint8_t /*unit*/, uint8_t /*readDevId*/, uint8_t /*objectId*/, uint8_t * /*data*/, uint8_t * /*dataSize*/)
{
return Modbus::Status_BadIllegalFunction;
}
#endif
16 changes: 16 additions & 0 deletions src/Modbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class ModbusServerPort;
* 22 (0x16) - `MASK_WRITE_REGISTER`
* 23 (0x17) - `READ_WRITE_MULTIPLE_REGISTERS`
* 24 (0x18) - `READ_FIFO_QUEUE`
* 43 (0x2B) - `ENCAPSULATED_INTERFACE_TRANSPORT` (Read Device Identification, MEI type 0x0E)

Default implementation of every Modbus function returns `Modbus::Status_BadIllegalFunction`.
*/
Expand Down Expand Up @@ -213,6 +214,21 @@ class MODBUS_EXPORT ModbusInterface
virtual Modbus::StatusCode readFIFOQueue(uint8_t unit, uint16_t fifoadr, uint16_t *count, uint16_t *values);
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
/// \details Function for Read Device Identification (FC43, MEI type 0x0E).
/// Reads identity objects (vendor name, product code, revision, etc.) from a remote device.
/// The response is returned as raw bytes — the caller is responsible for parsing the
/// TLV (Type-Length-Value) object list from the response payload.
/// \param[in] unit Address of the remote Modbus device.
/// \param[in] readDevId Read Device ID code: 1=Basic, 2=Regular, 3=Extended, 4=Specific.
/// \param[in] objectId Starting object ID to read from (0x00-0xFF).
/// \param[out] data Pointer to output buffer for raw MEI response data.
/// Buffer must be at least MB_VALUE_BUFF_SZ bytes.
/// \param[out] dataSize Number of bytes written into data buffer.
/// \return The result `Modbus::StatusCode` of the operation. Default implementation returns `Status_BadIllegalFunction`.
virtual Modbus::StatusCode readDeviceIdentification(uint8_t unit, uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize);
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

};

// --------------------------------------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions src/ModbusClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ StatusCode ModbusClient::readFIFOQueue(uint16_t fifoadr, uint16_t *count, uint16
}
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
StatusCode ModbusClient::readDeviceIdentification(uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize)
{
ModbusClientPrivate *d = d_cast(d_ptr);
return d->port->readDeviceIdentification(this, d->unit, readDevId, objectId, data, dataSize);
}
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

#ifndef MBF_READ_COILS_DISABLE
StatusCode ModbusClient::readCoilsAsBoolArray(uint16_t offset, uint16_t count, bool *values)
{
Expand Down
9 changes: 9 additions & 0 deletions src/ModbusClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ class MODBUS_EXPORT ModbusClient : public ModbusObject
Modbus::StatusCode readFIFOQueue(uint16_t fifoadr, uint16_t *count, uint16_t *values);
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
/// \details Read Device Identification (FC43/MEI 0x0E) from the bound device.
/// \param[in] readDevId Read Device ID code: 1=Basic, 2=Regular, 3=Extended, 4=Specific.
/// \param[in] objectId Starting object ID to read from (0x00-0xFF).
/// \param[out] data Pointer to output buffer for raw MEI response data.
/// \param[out] dataSize Number of bytes written into data buffer.
Modbus::StatusCode readDeviceIdentification(uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize);
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

#ifndef MBF_READ_COILS_DISABLE
/// \details Same as `ModbusClientPort::readCoilsAsBoolArray(uint8_t unit, uint16_t offset, uint16_t count, bool *values)`, but the `unit` address of the remote Modbus device is missing. It is preset in the constructor.
Modbus::StatusCode readCoilsAsBoolArray(uint16_t offset, uint16_t count, bool *values);
Expand Down
64 changes: 64 additions & 0 deletions src/ModbusClientPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,63 @@ Modbus::StatusCode ModbusClientPort::readFIFOQueue(ModbusObject *client, uint8_t
}
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
Modbus::StatusCode ModbusClientPort::readDeviceIdentification(ModbusObject *client, uint8_t unit, uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize)
{
ModbusClientPortPrivate *d = d_cast(d_ptr);

const uint16_t szBuff = 300;

uint8_t buff[szBuff];
Modbus::StatusCode r;
uint16_t szOutBuff;

ModbusClientPort::RequestStatus status = this->getRequestStatus(client);
switch (status)
{
case ModbusClientPort::Enable:
// Validate Read Device ID code (1=Basic, 2=Regular, 3=Extended, 4=Specific)
if (readDevId < MB_READ_DEVICE_ID_BASIC || readDevId > MB_READ_DEVICE_ID_SPECIFIC)
{
this->cancelRequest(client);
RAISE_ERROR_COMPLETED(Status_BadNotCorrectRequest,
StringLiteral("FC43. Invalid Read Device ID code"));
}
// Pack 3-byte MEI request: [MEI Type, Read Dev ID Code, Object ID]
buff[0] = MB_MEI_TYPE_READ_DEVICE_ID; // MEI Type = 0x0E (Read Device Identification)
buff[1] = readDevId; // Read Device ID code
buff[2] = objectId; // Object ID to start reading from
// no need break
case ModbusClientPort::Process:
r = this->request(unit, // unit ID
MBF_ENCAPSULATED_INTERFACE_TRANSPORT, // modbus function number (0x2B)
buff, // in-out buffer
3, // count of input data bytes
szBuff, // maximum size of buffer
&szOutBuff); // count of output data bytes
if (StatusIsProcessing(r))
return r;
if (!StatusIsGood(r) || d->isBroadcast())
RAISE_COMPLETED(r);
// Minimum valid response: MEI Type(1) + Read Dev ID(1) + Conformity(1) +
// More Follows(1) + Next Object ID(1) + Num Objects(1) = 6 bytes
if (szOutBuff < 6)
RAISE_ERROR_COMPLETED(Status_BadNotCorrectResponse,
StringLiteral("FC43. Incorrect received data size"));
// Verify MEI type in response matches our request
if (buff[0] != MB_MEI_TYPE_READ_DEVICE_ID)
RAISE_ERROR_COMPLETED(Status_BadNotCorrectResponse,
StringLiteral("FC43. MEI Type mismatch in response"));
// Copy raw response to caller — caller is responsible for parsing TLV objects
*dataSize = static_cast<uint8_t>(szOutBuff);
memcpy(data, buff, szOutBuff);
RAISE_COMPLETED(Modbus::Status_Good);
default:
return Status_Processing;
}
}
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

#ifndef MBF_READ_COILS_DISABLE
Modbus::StatusCode ModbusClientPort::readCoilsAsBoolArray(ModbusObject *client, uint8_t unit, uint16_t offset, uint16_t count, bool *values)
{
Expand Down Expand Up @@ -1046,6 +1103,13 @@ Modbus::StatusCode ModbusClientPort::readFIFOQueue(uint8_t unit, uint16_t fifoad
}
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
Modbus::StatusCode ModbusClientPort::readDeviceIdentification(uint8_t unit, uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize)
{
return readDeviceIdentification(this, unit, readDevId, objectId, data, dataSize);
}
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

ModbusPort *ModbusClientPort::port() const
{
return d_cast(d_ptr)->port;
Expand Down
16 changes: 16 additions & 0 deletions src/ModbusClientPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ class MODBUS_EXPORT ModbusClientPort : public ModbusObject, public ModbusInterfa
Modbus::StatusCode readFIFOQueue(ModbusObject *client, uint8_t unit, uint16_t fifoadr, uint16_t *count, uint16_t *values);
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
/// \details Read Device Identification (FC43/MEI 0x0E).
/// Has `client` as first parameter to seize current `ModbusClientPort` resource.
/// \param[in] client Pointer to client object for port resource arbitration.
/// \param[in] unit Address of the remote Modbus device.
/// \param[in] readDevId Read Device ID code: 1=Basic, 2=Regular, 3=Extended, 4=Specific.
/// \param[in] objectId Starting object ID to read from (0x00-0xFF).
/// \param[out] data Pointer to output buffer for raw MEI response data.
/// \param[out] dataSize Number of bytes written into data buffer.
Modbus::StatusCode readDeviceIdentification(ModbusObject *client, uint8_t unit, uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize);
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

#ifndef MBF_READ_COILS_DISABLE
/// \details Same as `ModbusClientPort::readCoilsAsBoolArray(uint8_t unit, uint16_t offset, uint16_t count, bool *values)` but has `client` as first parameter to seize current `ModbusClientPort` resource.
Modbus::StatusCode readCoilsAsBoolArray(ModbusObject *client, uint8_t unit, uint16_t offset, uint16_t count, bool *values);
Expand Down Expand Up @@ -318,6 +330,10 @@ class MODBUS_EXPORT ModbusClientPort : public ModbusObject, public ModbusInterfa
Modbus::StatusCode readFIFOQueue(uint8_t unit, uint16_t fifoadr, uint16_t *count, uint16_t *values) override;
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
Modbus::StatusCode readDeviceIdentification(uint8_t unit, uint8_t readDevId, uint8_t objectId, uint8_t *data, uint8_t *dataSize) override;
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

#ifndef MBF_READ_COILS_DISABLE
/// \details Same as `ModbusClientPort::readCoils(uint8_t unit, uint16_t offset, uint16_t count, void *values)`, but the output buffer of values `values` is an array, where each discrete value is located in a separate element of the array of type `bool`.
inline Modbus::StatusCode readCoilsAsBoolArray(uint8_t unit, uint16_t offset, uint16_t count, bool *values) { return readCoilsAsBoolArray(this, unit, offset, count, values); }
Expand Down
41 changes: 39 additions & 2 deletions src/ModbusGlobal.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,46 @@
/// \brief Encapsulated interface transport (function code 43)
#define MBF_ENCAPSULATED_INTERFACE_TRANSPORT 43
/// \brief Illegal function code indicator
#define MBF_ILLEGAL_FUNCTION 73
#define MBF_ILLEGAL_FUNCTION 73
/// \brief Exception response flag (bit 7 set)
#define MBF_EXCEPTION 128
#define MBF_EXCEPTION 128
///\}

/// \name MEI (Modbus Encapsulated Interface) Constants
/// Constants for FC43 Encapsulated Interface Transport
///\{

/// \brief MEI Type for Read Device Identification (sub-function of FC43)
#define MB_MEI_TYPE_READ_DEVICE_ID 0x0E

/// \brief Read Device ID Code: Basic identification (objects 0x00-0x02)
#define MB_READ_DEVICE_ID_BASIC 0x01
/// \brief Read Device ID Code: Regular identification (objects 0x00-0x06)
#define MB_READ_DEVICE_ID_REGULAR 0x02
/// \brief Read Device ID Code: Extended identification (objects 0x00-0xFF)
#define MB_READ_DEVICE_ID_EXTENDED 0x03
/// \brief Read Device ID Code: Specific identification (one individual object)
#define MB_READ_DEVICE_ID_SPECIFIC 0x04

/// \brief Device ID Object: Vendor Name (mandatory, basic)
#define MB_DEVICE_ID_VENDOR_NAME 0x00
/// \brief Device ID Object: Product Code (mandatory, basic)
#define MB_DEVICE_ID_PRODUCT_CODE 0x01
/// \brief Device ID Object: Major Minor Revision (mandatory, basic)
#define MB_DEVICE_ID_MAJOR_MINOR_REVISION 0x02
/// \brief Device ID Object: Vendor URL (optional, regular)
#define MB_DEVICE_ID_VENDOR_URL 0x03
/// \brief Device ID Object: Product Name (optional, regular)
#define MB_DEVICE_ID_PRODUCT_NAME 0x04
/// \brief Device ID Object: Model Name (optional, regular)
#define MB_DEVICE_ID_MODEL_NAME 0x05
/// \brief Device ID Object: User Application Name (optional, regular)
#define MB_DEVICE_ID_USER_APPLICATION_NAME 0x06

/// \brief More Follows flag: additional objects available
#define MB_MORE_FOLLOWS 0xFF
/// \brief More Follows flag: no more objects
#define MB_NO_MORE_FOLLOWS 0x00
///\}


Expand Down
38 changes: 38 additions & 0 deletions src/ModbusServerResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,29 @@ StatusCode ModbusServerResource::processInputData(const uint8_t *buff, uint16_t
break;
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
case MBF_ENCAPSULATED_INTERFACE_TRANSPORT:
// FC43 request must have at least 3 bytes: MEI Type + Read Dev ID Code + Object ID
if (sz < 3)
{
const size_t len = 100;
Char errbuff[len];
snprintf(errbuff, len, StringLiteral("FC%02hhu. Incorrect received data size"), d->func);
return d->setError(Status_BadNotCorrectRequest, errbuff);
}
// Verify MEI type is Read Device Identification (0x0E)
if (buff[0] != MB_MEI_TYPE_READ_DEVICE_ID)
{
const size_t len = 100;
Char errbuff[len];
snprintf(errbuff, len, StringLiteral("FC%02hhu. Unsupported MEI Type 0x%02hhX"), d->func, buff[0]);
return d->setError(Status_BadIllegalFunction, errbuff);
}
d->subfunc = buff[1]; // Read Device ID code (1-4)
d->valueBuff[0] = buff[2]; // Object ID to start from
break;
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

default:
{
const size_t len = 100;
Expand Down Expand Up @@ -680,6 +703,13 @@ StatusCode ModbusServerResource::processDevice()
break;
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
case MBF_ENCAPSULATED_INTERFACE_TRANSPORT:
// subfunc = Read Device ID code, valueBuff[0] = starting Object ID
r = d->device->readDeviceIdentification(d->unit, d->subfunc, d->valueBuff[0], d->valueBuff, &d->outByteCount);
break;
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

default:
return d->setError(Status_BadIllegalFunction, StringLiteral("Unsupported function"));
}
Expand Down Expand Up @@ -844,6 +874,14 @@ StatusCode ModbusServerResource::processOutputData(uint8_t *buff, uint16_t &sz)
break;
#endif // MBF_READ_FIFO_QUEUE_DISABLE

#ifndef MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE
case MBF_ENCAPSULATED_INTERFACE_TRANSPORT:
// Copy raw response from device handler directly into wire buffer
memcpy(buff, d->valueBuff, d->outByteCount);
sz = d->outByteCount;
break;
#endif // MBF_ENCAPSULATED_INTERFACE_TRANSPORT_DISABLE

}
return Status_Good;
}
2 changes: 1 addition & 1 deletion src/Modbus_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#define MODBUSLIB_VERSION_MINOR 4
#define MODBUSLIB_VERSION_PATCH 9

#define MB_DYNAMIC_LINKING
/* #undef MB_DYNAMIC_LINKING */

/* #undef MB_CLIENT_DISABLE */
/* #undef MB_SERVER_DISABLE */
Expand Down