diff --git a/Kconfig b/Kconfig index 50b48f3..4c7eed8 100644 --- a/Kconfig +++ b/Kconfig @@ -16,7 +16,7 @@ menu "Modbus configuration" config FMB_TCP_PORT_MAX_CONN int "Maximum allowed connections for TCP stack" - range 1 6 + range 1 8 default 5 depends on FMB_COMM_MODE_TCP_EN help @@ -34,6 +34,14 @@ menu "Modbus configuration" Modbus TCP connection timeout in seconds. Once expired the current connection with the client will be closed and Modbus slave will be waiting for new connection to accept. + + config FMB_TCP_UID_ENABLED + bool "Modbus TCP enable UID (Unit Identifier) support" + default n + depends on FMB_COMM_MODE_TCP_EN + help + If this option is set the Modbus stack uses UID (Unit Identifier) field in MBAP frame. + Else the UID is ignored by master and slave. config FMB_COMM_MODE_RTU_EN bool "Enable Modbus stack support for RTU mode" diff --git a/freemodbus/common/include/esp_modbus_common.h b/freemodbus/common/include/esp_modbus_common.h index f443286..757783d 100644 --- a/freemodbus/common/include/esp_modbus_common.h +++ b/freemodbus/common/include/esp_modbus_common.h @@ -143,6 +143,7 @@ typedef union { // TCP/UDP communication structure struct { mb_mode_type_t ip_mode; /*!< Modbus communication mode */ + uint8_t slave_uid; /*!< Modbus slave address field for UID */ uint16_t ip_port; /*!< Modbus port */ mb_tcp_addr_type_t ip_addr_type; /*!< Modbus address type */ void* ip_addr; /*!< Modbus address table for connection */ diff --git a/freemodbus/modbus/include/mb.h b/freemodbus/modbus/include/mb.h index 964e3e1..32a1aa2 100644 --- a/freemodbus/modbus/include/mb.h +++ b/freemodbus/modbus/include/mb.h @@ -165,6 +165,7 @@ eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, * frame processing is still disabled until eMBEnable( ) is called. * * \param usTCPPort The TCP port to listen on. + * \param ucSlaveUid The UID field for slave to listen on. * \return If the protocol stack has been initialized correctly the function * returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error * codes is returned: @@ -172,7 +173,7 @@ eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, * slave addresses are in the range 1 - 247. * - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error. */ -eMBErrorCode eMBTCPInit( USHORT usTCPPort ); +eMBErrorCode eMBTCPInit( UCHAR ucSlaveUid, USHORT usTCPPort ); /*! \ingroup modbus * \brief Release resources used by the protocol stack. diff --git a/freemodbus/modbus/include/mbconfig.h b/freemodbus/modbus/include/mbconfig.h index 268b748..eb12947 100644 --- a/freemodbus/modbus/include/mbconfig.h +++ b/freemodbus/modbus/include/mbconfig.h @@ -86,6 +86,10 @@ PR_BEGIN_EXTERN_C #endif +/*! \brief The option is required for support of RTU over TCP. + */ +#define MB_TCP_UID_ENABLED ( CONFIG_FMB_TCP_UID_ENABLED ) + /*! \brief This option defines the number of data bits per ASCII character. * * A parity bit is added before the stop bit which keeps the actual byte size at 10 bits. diff --git a/freemodbus/modbus/mb.c b/freemodbus/modbus/mb.c index 60ba538..daf9ac0 100644 --- a/freemodbus/modbus/mb.c +++ b/freemodbus/modbus/mb.c @@ -202,11 +202,16 @@ eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eM #if MB_TCP_ENABLED > 0 eMBErrorCode -eMBTCPInit( USHORT ucTCPPort ) +eMBTCPInit( UCHAR ucSlaveUid, USHORT ucTCPPort ) { eMBErrorCode eStatus = MB_ENOERR; - if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) + /* Check preconditions */ + if( ucSlaveUid > MB_ADDRESS_MAX ) + { + eStatus = MB_EINVAL; + } + else if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) { eMBState = STATE_DISABLED; } @@ -222,7 +227,7 @@ eMBTCPInit( USHORT ucTCPPort ) peMBFrameReceiveCur = eMBTCPReceive; peMBFrameSendCur = eMBTCPSend; pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL; - ucMBAddress = MB_TCP_PSEUDO_ADDRESS; + ucMBAddress = ucSlaveUid; eMBCurrentMode = MB_TCP; eMBState = STATE_DISABLED; } @@ -371,7 +376,8 @@ eMBPoll( void ) if( eStatus == MB_ENOERR ) { /* Check if the frame is for us. If not ignore the frame. */ - if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) + if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) + || ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); ESP_LOG_BUFFER_HEX_LEVEL(MB_PORT_TAG, &ucMBFrame[MB_PDU_FUNC_OFF], usLength, ESP_LOG_DEBUG); @@ -401,8 +407,8 @@ eMBPoll( void ) } /* If the request was not sent to the broadcast address we - * return a reply. */ - if( ucRcvAddress != MB_ADDRESS_BROADCAST ) + * return a reply. In case of TCP the slave answers to broadcast address. */ + if( ( ucRcvAddress != MB_ADDRESS_BROADCAST ) || ( eMBCurrentMode == MB_TCP ) ) { if( eException != MB_EX_NONE ) { diff --git a/freemodbus/modbus/tcp/mbtcp.c b/freemodbus/modbus/tcp/mbtcp.c index 3502454..a24228b 100644 --- a/freemodbus/modbus/tcp/mbtcp.c +++ b/freemodbus/modbus/tcp/mbtcp.c @@ -124,10 +124,14 @@ eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength ) *pusLength = usLength - MB_TCP_FUNC; eStatus = MB_ENOERR; - /* Modbus TCP does not use any addresses. Fake the source address such - * that the processing part deals with this frame. + /* The regular Modbus TCP does not use any addresses. Fake the MBAP UID in this case. + * The MBAP UID field support is used for RTU over TCP option if enabled. */ +#if MB_TCP_UID_ENABLED + *pucRcvAddress = pucMBTCPFrame[MB_TCP_UID]; +#else *pucRcvAddress = MB_TCP_PSEUDO_ADDRESS; +#endif } } else @@ -152,6 +156,7 @@ eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength ) */ pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U; pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF; + if( xMBTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE ) { eStatus = MB_EIO; diff --git a/freemodbus/modbus/tcp/mbtcp_m.c b/freemodbus/modbus/tcp/mbtcp_m.c index 4a9fc01..112f437 100644 --- a/freemodbus/modbus/tcp/mbtcp_m.c +++ b/freemodbus/modbus/tcp/mbtcp_m.c @@ -123,10 +123,14 @@ eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLeng *pusLength = usLength - MB_TCP_FUNC; eStatus = MB_ENOERR; - /* Modbus TCP does not use any addresses. Fake the source address such - * that the processing part deals with this frame. + /* Get MBAP UID field if its support is enabled. + * Otherwise just ignore this field. */ +#if MB_TCP_UID_ENABLED + *pucRcvAddress = pucMBTCPFrame[MB_TCP_UID]; +#else *pucRcvAddress = MB_TCP_PSEUDO_ADDRESS; +#endif } } else @@ -137,20 +141,27 @@ eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLeng } eMBErrorCode -eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength ) +eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame, USHORT usLength ) { eMBErrorCode eStatus = MB_ENOERR; UCHAR *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC; USHORT usTCPLength = usLength + MB_TCP_FUNC; - /* The MBAP header is already initialized because the caller calls this - * function with the buffer returned by the previous call. Therefore we - * only have to update the length in the header. Note that the length - * header includes the size of the Modbus PDU and the UID Byte. Therefore - * the length is usLength plus one. + /* Note that the length in the MBAP header includes the size of the Modbus PDU + * and the UID Byte. Therefore the length is usLength plus one. */ pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U; pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF; + + /* Set UID field in the MBAP if it is supported. + * If the RTU over TCP is not supported, the UID = 0 or 0xFF. + */ +#if MB_TCP_UID_ENABLED + pucMBTCPFrame[MB_TCP_UID] = ucAddress; +#else + pucMBTCPFrame[MB_TCP_UID] = 0x00; +#endif + if( xMBMasterTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE ) { eStatus = MB_EIO; diff --git a/freemodbus/modbus/tcp/mbtcp_m.h b/freemodbus/modbus/tcp/mbtcp_m.h index 901cd0e..68ca532 100644 --- a/freemodbus/modbus/tcp/mbtcp_m.h +++ b/freemodbus/modbus/tcp/mbtcp_m.h @@ -50,7 +50,7 @@ void eMBMasterTCPStart( void ); void eMBMasterTCPStop( void ); eMBErrorCode eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ); -eMBErrorCode eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame, +eMBErrorCode eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame, USHORT usLength ); BOOL xMBMasterTCPTimerExpired(void); diff --git a/freemodbus/tcp_master/port/port_tcp_master.c b/freemodbus/tcp_master/port/port_tcp_master.c index d8be1e9..49ef9af 100644 --- a/freemodbus/tcp_master/port/port_tcp_master.c +++ b/freemodbus/tcp_master/port/port_tcp_master.c @@ -989,6 +989,7 @@ xMBMasterTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength ) // Apply TID field to the frame before send pucMBTCPFrame[MB_TCP_TID] = (UCHAR)(pxInfo->usTidCnt >> 8U); pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(pxInfo->usTidCnt & 0xFF); + int xRes = xMBMasterTCPPortWritePoll(pxInfo, pucMBTCPFrame, usTCPLength, MB_TCP_SEND_TIMEOUT_MS); if (xRes < 0) { ESP_LOGE(TAG, MB_SLAVE_FMT(", send data failure, err(errno) = %d(%d)."), diff --git a/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c b/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c index 45078b4..9c47f43 100644 --- a/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c +++ b/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c @@ -72,7 +72,7 @@ static esp_err_t mbc_tcp_slave_start(void) eMBErrorCode status = MB_EIO; // Initialize Modbus stack using mbcontroller parameters - status = eMBTCPInit((USHORT)mbs_opts->mbs_comm.ip_port); + status = eMBTCPInit((UCHAR)mbs_opts->mbs_comm.slave_uid, (USHORT)mbs_opts->mbs_comm.ip_port); MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack initialization failure, eMBInit() returns (0x%x).", status); diff --git a/freemodbus/tcp_slave/port/port_tcp_slave.c b/freemodbus/tcp_slave/port/port_tcp_slave.c index c6485f4..d4929dd 100644 --- a/freemodbus/tcp_slave/port/port_tcp_slave.c +++ b/freemodbus/tcp_slave/port/port_tcp_slave.c @@ -106,8 +106,9 @@ static void* vxMBTCPPortRespQueueRecv(QueueHandle_t xRespQueueHandle) BaseType_t xStatus = xQueueReceive(xRespQueueHandle, (void*)&pvResp, pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS)); - MB_PORT_CHECK((xStatus == pdTRUE), NULL, "Could not get respond confirmation."); - MB_PORT_CHECK((pvResp), NULL, "Incorrect response processing."); + if (xStatus != pdTRUE) { + ESP_LOGD(TAG, "Could not get respond confirmation."); + } return pvResp; } @@ -599,7 +600,7 @@ static void vMBTCPPortServerTask(void *pvParameters) // Wait while response is not processed by stack by timeout UCHAR* pucSentBuffer = vxMBTCPPortRespQueueRecv(xConfig.xRespQueueHandle); if (pucSentBuffer == NULL) { - ESP_LOGE(TAG, "Response time exceeds configured %d [ms], ignore packet.", + ESP_LOGD(TAG, "Response is ignored, time exceeds configured %d [ms].", MB_TCP_RESP_TIMEOUT_MS); } else { USHORT usSentTid = MB_TCP_GET_FIELD(pucSentBuffer, MB_TCP_TID); diff --git a/idf_component.yml b/idf_component.yml index bb2e37c..082a8bc 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.8" +version: "1.0.9" description: ESP-MODBUS is the official Modbus library for Espressif SoCs. url: https://github.com/espressif/esp-modbus dependencies: diff --git a/test/tcp/mb_tcp_master/main/tcp_master.c b/test/tcp/mb_tcp_master/main/tcp_master.c index 0bbb0e4..00de8c9 100644 --- a/test/tcp/mb_tcp_master/main/tcp_master.c +++ b/test/tcp/mb_tcp_master/main/tcp_master.c @@ -73,7 +73,7 @@ static const char *TAG = "MASTER_TEST"; // Enumeration of modbus device addresses accessed by master device // Each address in the table is a index of TCP slave ip address in mb_communication_info_t::tcp_ip_addr table enum { - MB_DEVICE_ADDR1 = 1, // Slave address 1 + MB_DEVICE_ADDR1 = 1, // Slave UID = 1 MB_DEVICE_ADDR2 = 200, MB_DEVICE_ADDR3 = 35 }; @@ -750,7 +750,6 @@ void app_main(void) comm_info.ip_netif_ptr = (void*)get_example_netif(); ESP_ERROR_CHECK(master_init(&comm_info)); - vTaskDelay(50); master_operation_func(NULL); ESP_ERROR_CHECK(master_destroy()); diff --git a/test/tcp/mb_tcp_master/sdkconfig.ci.wifi b/test/tcp/mb_tcp_master/sdkconfig.ci.wifi index 169fb40..b4b5485 100644 --- a/test/tcp/mb_tcp_master/sdkconfig.ci.wifi +++ b/test/tcp/mb_tcp_master/sdkconfig.ci.wifi @@ -8,6 +8,7 @@ CONFIG_FMB_COMM_MODE_ASCII_EN=n CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 CONFIG_FMB_TIMER_PORT_ENABLED=y +CONFIG_FMB_TCP_UID_ENABLED=n CONFIG_MB_MDNS_IP_RESOLVER=n CONFIG_MB_SLAVE_IP_FROM_STDIN=y CONFIG_EXAMPLE_CONNECT_IPV6=n diff --git a/test/tcp/mb_tcp_master/sdkconfig.defaults b/test/tcp/mb_tcp_master/sdkconfig.defaults index bf2bb8d..cb2ab90 100644 --- a/test/tcp/mb_tcp_master/sdkconfig.defaults +++ b/test/tcp/mb_tcp_master/sdkconfig.defaults @@ -13,6 +13,7 @@ CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 CONFIG_FMB_TIMER_PORT_ENABLED=y CONFIG_FMB_TIMER_ISR_IN_IRAM=y CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y +CONFIG_FMB_TCP_UID_ENABLED=n CONFIG_MB_MDNS_IP_RESOLVER=n CONFIG_MB_SLAVE_IP_FROM_STDIN=y CONFIG_MB_SLAVE_ADDR=1 diff --git a/test/tcp/mb_tcp_slave/main/Kconfig.projbuild b/test/tcp/mb_tcp_slave/main/Kconfig.projbuild index 27a2979..4a7bc53 100644 --- a/test/tcp/mb_tcp_slave/main/Kconfig.projbuild +++ b/test/tcp/mb_tcp_slave/main/Kconfig.projbuild @@ -2,7 +2,8 @@ menu "Modbus Example Configuration" config MB_SLAVE_ADDR int "Modbus slave address" - range 1 255 + range 1 247 if !FMB_TCP_UID_ENABLED + range 0 247 if FMB_TCP_UID_ENABLED default 1 help This is the Modbus slave address in the network. diff --git a/test/tcp/mb_tcp_slave/main/tcp_slave.c b/test/tcp/mb_tcp_slave/main/tcp_slave.c index e372546..aced702 100644 --- a/test/tcp/mb_tcp_slave/main/tcp_slave.c +++ b/test/tcp/mb_tcp_slave/main/tcp_slave.c @@ -52,6 +52,8 @@ | MB_EVENT_COILS_WR) #define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK) +#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) + static const char *TAG = "SLAVE_TEST"; static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED; @@ -69,8 +71,6 @@ static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED; #define MB_DEVICE_ID (uint32_t)CONFIG_FMB_CONTROLLER_SLAVE_ID #endif -#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) - #define MB_MDNS_INSTANCE(pref) pref"mb_slave_tcp" // convert mac from binary format to string @@ -315,6 +315,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info) comm_info->ip_addr = NULL; // Bind to any address comm_info->ip_netif_ptr = (void*)get_example_netif(); + comm_info->slave_uid = MB_SLAVE_ADDR; // Setup communication parameters and start stack err = mbc_slave_setup((void*)comm_info); @@ -423,6 +424,7 @@ void app_main(void) ESP_ERROR_CHECK(init_services()); // Set UART log level + esp_log_level_set(TAG, ESP_LOG_INFO); mb_communication_info_t comm_info = { 0 };