diff --git a/src/Modbus.cpp b/src/Modbus.cpp index ea8b782..0ade6af 100644 --- a/src/Modbus.cpp +++ b/src/Modbus.cpp @@ -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 diff --git a/src/Modbus.h b/src/Modbus.h index ae2f667..eeb1e16 100644 --- a/src/Modbus.h +++ b/src/Modbus.h @@ -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`. */ @@ -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 + }; // -------------------------------------------------------------------------------------------------------- diff --git a/src/ModbusClient.cpp b/src/ModbusClient.cpp index 3436bbb..7d666b7 100644 --- a/src/ModbusClient.cpp +++ b/src/ModbusClient.cpp @@ -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) { diff --git a/src/ModbusClient.h b/src/ModbusClient.h index 16dfd5e..3b3b78b 100644 --- a/src/ModbusClient.h +++ b/src/ModbusClient.h @@ -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); diff --git a/src/ModbusClientPort.cpp b/src/ModbusClientPort.cpp index f752e5d..0b22f78 100644 --- a/src/ModbusClientPort.cpp +++ b/src/ModbusClientPort.cpp @@ -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(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) { @@ -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; diff --git a/src/ModbusClientPort.h b/src/ModbusClientPort.h index e25d299..a07847d 100644 --- a/src/ModbusClientPort.h +++ b/src/ModbusClientPort.h @@ -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); @@ -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); } diff --git a/src/ModbusGlobal.h b/src/ModbusGlobal.h index c0eaca0..17f4790 100644 --- a/src/ModbusGlobal.h +++ b/src/ModbusGlobal.h @@ -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 ///\} diff --git a/src/ModbusServerResource.cpp b/src/ModbusServerResource.cpp index 634e924..b9a9388 100644 --- a/src/ModbusServerResource.cpp +++ b/src/ModbusServerResource.cpp @@ -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; @@ -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")); } @@ -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; } diff --git a/src/Modbus_config.h b/src/Modbus_config.h index ae97183..2bb4bde 100644 --- a/src/Modbus_config.h +++ b/src/Modbus_config.h @@ -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 */