MODBUS TCP with different slave ID

A

Thread Starter

asd

for client server communication and it is working fine. now req is my slave device should respond to the diff slave addresses polled by master/client. Can I just change slave id and establish connection? or I need to close previous connection and again establish new one?
 
L

Lynn August Linse

You should be able to continue using the SAME TCP socket with any number of Unit Id (I assume the slave address you mention). For example, if you have a TCP-to-RS485 bridge with slaves 1-4 on the RS-485, you can use a single socket and poll for units 1 to 4 in the same socket. (There are advantages to using 4 sockets ... but that is a different discussion).
 
> You should be able to continue using the SAME TCP socket with any number of
> Unit Id (I assume the slave address you mention). For example, if you have a
> TCP-to-RS485 bridge with slaves 1-4 on the RS-485, you can use a single socket
> and poll for units 1 to 4 in the same socket. (There are advantages to using 4
> sockets ... but that is a different discussion).

I'm working on win ce 6 modbus tcp client server. application is developed for a client server communication and it is working fine. now required is my slave device should respond to the diff slave addresses polled by master/client. Can I just change slave id and establish connection or I need to close previous connection and again establish new one?

below is the code, which is working fine for one node. if I polled with other node ID then it gives exception. what change it req to communicate with other node simultaneously? My device should be able to communicate with 32 diff nodes on modbus tcp. Shall I create individual threads for each node but how they will communicate on same port? before establishing connection with other node shall I close previous node?
 
L

Lynn August Linse

> below is the code, which is working fine for one node. if I polled with
> other node ID then it gives exception.

I think you are seeing a difference between what is POSSIBLE, and what this particular device implemented.

The Modbus/TCP spec implies one could create a half-duplex design, where it is an error to poll a second device before the first answers.

I think in your case, you will need to open one socket for each slave because that looks like the only way this device is designed to work.
 
> below is the code, which is working fine for one node. if I polled with other node
> ID then it gives exception. what change it req to communicate with other node simultaneously?
> My device should be able to communicate with 32 diff nodes on modbus tcp. Shall I
> create individual threads for each node but how they will communicate on same port?
> before establishing connection with other node shall I close previous node?<pre>
startupServer(int slaveAddr, const TCHAR * const hostName)
{

int result;
int tcpOption;
struct sockaddr_in hostAddress;

if (isStarted())
return (FTALK_ILLEGAL_STATE_ERROR);

// Note: For TCP we allow 0 as slave address, -1 means ignore slave adr
if ((slaveAddr < -1) || (slaveAddr > 255))
return (FTALK_ILLEGAL_ARGUMENT_ERROR);
this->slaveAddr = slaveAddr;

//
// Special treatment for the Win32 platform, needs to load WinSock DLL
//
#ifdef _WINSOCKAPI_
WSADATA wsaData;

result = WSAStartup(0x0101, &wsaData);
if (result != 0)
return (FTALK_SOCKET_LIB_ERROR);
#endif

//
// Open socket
//
listenSocket = socket(PF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
shutdownServer();
return (FTALK_OPEN_ERR);
}

//
// Configure listen socket options (we ignore errors here)
//
#ifdef SO_REUSEADDR
tcpOption = 1; // Enable option
setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR,
(char *) &tcpOption, sizeof (tcpOption));
#endif

//
// Binding the listen socket to the port
//
hostAddress.sin_family = AF_INET;
if ((hostName == NULL) || (hostName[0] == '\0'))
hostAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
hostAddress.sin_addr.s_addr = inet_addr((char *) hostName);
#if !defined(__VXWORKS__) // We don't support host name resolving with VxWorks
if (hostAddress.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *hostInfo;

hostInfo = gethostbyname((char *) hostName);

if (hostInfo == NULL)
return (FTALK_TCPIP_CONNECT_ERR);
hostAddress.sin_addr = *(struct in_addr *) hostInfo->h_addr;
}
#endif
}
hostAddress.sin_port = htons(portNo);
result = bind(listenSocket, (struct sockaddr *) &hostAddress,
sizeof (hostAddress));
if (result == SOCKET_ERROR)
{
shutdownServer();
switch (socketErrno)
{
#ifdef _WINSOCKAPI_
case WSAEACCES:
return (FTALK_PORT_NO_ACCESS);
case WSAEADDRINUSE:
return (FTALK_PORT_ALREADY_BOUND);
case WSAEADDRNOTAVAIL:
default:
return (FTALK_PORT_NOT_AVAIL);
#else
case ENOTCONN: // Linux 7.2 reports this error no if no root privilege
case EACCES:
return (FTALK_PORT_NO_ACCESS);
case EADDRINUSE:
return (FTALK_PORT_ALREADY_BOUND);
case EADDRNOTAVAIL:
default:
return (FTALK_PORT_NOT_AVAIL);
#endif
}
}

//
// Start listening to incoming connections
//
result = listen(listenSocket,
((MAX_CONNECTIONS < SOMAXCONN) ? MAX_CONNECTIONS : SOMAXCONN));
if (result == SOCKET_ERROR)
{
shutdownServer();
return (FTALK_LISTEN_FAILED);
}
return (FTALK_SUCCESS);
}

serverLoop()
{
int iReturnCode = (FTALK_SUCCESS);
int result;
int sockIdx;
int recvResult;
int sendResult;
fd_set fdSet;
timeval timeVal;
SOCKET maxFileDes;
int replyCnt;
int tcpOption;

if (!isStarted())
return (FTALK_ILLEGAL_STATE_ERROR);

//
// Prepare file descriptor set for select call
//
FD_ZERO (&fdSet);
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127)
#endif
FD_SET (listenSocket, &fdSet);
#ifdef _MSC_VER
# pragma warning(pop)
#endif
maxFileDes = listenSocket;
for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127)
#endif
FD_SET (connectionSocketArr[sockIdx], &fdSet);
#ifdef _MSC_VER
# pragma warning(pop)
#endif
if (connectionSocketArr[sockIdx] > maxFileDes)
maxFileDes = connectionSocketArr[sockIdx];
}

//
// Block until accept request or received data or time-out
//
timeVal.tv_sec = (long) timeOut / 1000L;
timeVal.tv_usec = ((long) timeOut % 1000L) * 1000L;
if (timeOut == 0)
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, NULL);
else
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, &timeVal);
if (result == SOCKET_ERROR)
return (FTALK_FILEDES_EXCEEDED);

//
// Check for time-out
//
if (result == 0)
{
TRACELOG1("Slave poll time-out!\n");
dataTablePtr->timeOutHandler();

iReturnCode = (FTALK_REPLY_TIMEOUT_ERROR);
}

//
// Connection accept request
//
if (FD_ISSET (listenSocket, &fdSet))
{
// Search a free socket
for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] == INVALID_SOCKET)
{
struct sockaddr_in peerAddr;
SOCK_LEN_TYPE peerAddrLen = sizeof(peerAddr);

// Yes, socket is free, try to accept a connection on it
connectionSocketArr[sockIdx] = accept(listenSocket,
(struct sockaddr *) &peerAddr,
&peerAddrLen);
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
{
//
// Check id connection shall be accepted
//
if (!dataTablePtr->validateMasterIpAddr(inet_ntoa(peerAddr.sin_addr)))
{
shutdown(connectionSocketArr[sockIdx], SD_BOTH);
closesocket(connectionSocketArr[sockIdx]);
connectionSocketArr[sockIdx] = INVALID_SOCKET;
TRACELOG2("Connection rejected on slot %d\n", sockIdx);
}

//
// Set socket options (we ignore errors here, not critical)
//
#ifdef TCP_NODELAY
tcpOption = 1; // Enable option
setsockopt(connectionSocketArr[sockIdx],
IPPROTO_TCP, TCP_NODELAY,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_SNDBUF
tcpOption = MAX_MSG_SIZE;
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_SNDBUF,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_RCVBUF
tcpOption = MAX_MSG_SIZE;
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_RCVBUF,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_LINGER
tcpOption = 0; // Disable option = discard unsent data when closing
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_LINGER,
(char *) &tcpOption, sizeof (tcpOption));
#endif
TRACELOG2("Connection accepted on slot %d\n", sockIdx);
}
break; // Leave for loop
}
}
}

//
// Data received on socket
//

for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
{
if (FD_ISSET (connectionSocketArr[sockIdx], &fdSet))
{
recvResult = recv (connectionSocketArr[sockIdx],
(char *) bufferArr, sizeof (bufferArr), 0);
sendResult = 0;
replyCnt = 0;

//
// Process client message
//
if (recvResult >= PREFIX_LEN) // Process only minimum message sizes
{
short dataLen;

dataLen = (short) ((bufferArr[4] << 8) | (bufferArr[5] & 0xFF));
// Validate length before processing message
if ((dataLen + PREFIX_LEN) == recvResult)
{
replyCnt = processMessage(&bufferArr[PREFIX_LEN],
recvResult - PREFIX_LEN);

// The first two bytes (msg id) are returned untouched
bufferArr[2] = 0; // protocol identifier
bufferArr[3] = 0; // protocol identifier
bufferArr[4] = (char) ((replyCnt) >> 8);
bufferArr[5] = (char) ((replyCnt) & 0xFF);
sendResult = send(connectionSocketArr[sockIdx],
(char *) bufferArr,
replyCnt + PREFIX_LEN, 0);
}
}
//
// Check for disconnection and errors
//
if ((recvResult < PREFIX_LEN) ||
(sendResult != replyCnt + PREFIX_LEN))
{
//
// Free socket
//
shutdown(connectionSocketArr[sockIdx], SD_BOTH);
closesocket(connectionSocketArr[sockIdx]);
connectionSocketArr[sockIdx] = INVALID_SOCKET;
if (recvResult == 0)
TRACELOG2("Disconnected slot %d nicely by other peer.\n",
sockIdx);
else
TRACELOG2("Forced disconnection on slot %d!\n", sockIdx);
}
}
}
}
return iReturnCode;
}</pre>
 
Top