Managing Ethernet Connections

I've been tearing my hair out for months trying to get a reliable telnet connection to a Due. Most of the time, it works ok, but every once in a while, the connection gets dropped, due to noise in the system, and there seems to be no way to re-connect without resetting the Due, and re-initializing everything.

Here is the code I've been using for a while, but it's just one of countless things I've tried:

EthernetServer *pTelnetServer;
boolean TelnetConnected;
EthernetClient TelnetClient;
Stream *pConsole = &Serial1;

....

setup(void)
{
pTelnetServer = new EthernetServer(23);
TelnetConnected = false;
}


loop()
{
    ....
	if (TelnetConnected && !TelnetClient.connected())
	{
		if (pTelnetServer)
		{
			pConsole = (Stream *)&Serial1;
		}
		TelnetConnected = false;
	}
	else if (!TelnetConnected)
	{
		TelnetClient = pTelnetServer->available();
		if (TelnetClient)
		{
			TelnetConnected = true;
			pConsole = (Stream *)&TelnetClient;
		}
		else
		{
			TelnetConnected = false;
			pConsole = (Stream *)&Serial1;
		}
	}
}

This code ALWAYS works correctly following a re-boot. It will sometimes work all day long. But, once the connection is dropped, usually due to noise in the system, it cannot be re-connected without a reboot.

Regards,
Ray L.

Add this stuff to your sketch. I use the serial port to check the sockets.

#include <utility/w5100.h>

byte socketStat[MAX_SOCK_NUM];

void ShowSockStatus()
{
  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    Serial.print(F("Socket#"));
    Serial.print(i);
    uint8_t s = W5100.readSnSR(i);
    socketStat[i] = s;
    Serial.print(F(":0x"));
    Serial.print(s,16);
    Serial.print(F(" "));
    Serial.print(W5100.readSnPORT(i));
    Serial.print(F(" D:"));
    uint8_t dip[4];
    W5100.readSnDIPR(i, dip);
    for (int j=0; j<4; j++) {
      Serial.print(dip[j],10);
      if (j<3) Serial.print(".");
    }
    Serial.print(F("("));
    Serial.print(W5100.readSnDPORT(i));
    Serial.println(F(")"));
  }
}

// this is what I use to check the socket status
  if(Serial.available()) {
    char ch = Serial.read();
    
    if(ch == 's') ShowSockStatus();    
  }

A status list:
0x0 = available
0x14 = waiting for a connection
0x17 = connected
0x1C = connected waiting for close
0x22 = UDP

Tim,

Thanks!

Instead of using available(), and expecting it to return a valid client (as I'm doing now), would it be appropriate to scan the sockets, and take the first one I find that indicates status 0x17? Once I find a socket that is connected, how do I get the client object associated with that socket?

My assumption, obviously, was that available() would return the first socket that had data waiting (there will never be more than one at any given time). That seems not to be the case, however.

I will, at a minimum, do a test program like your example, to get a better understanding of how it actually works.

Regards,
Ray L.

I have a telnet-like TCP server in the playground. Maybe you can take a look and see if anything there will help. Borrow what you want. There is no timeout on the clients. I'll see if I can find my code that has a timeout.http://playground.arduino.cc/Code/Telnet

Some progress, I guess.... I've used the code you linked to, and modified my code to look at the status. All appears to be working reasonably well - I can open multiple telnet windows, and they all work as they should, even when I open and close them. But, noise in the system (solenoids and solenoid valves) seems to be whacking the Ethernet. At some point, all the telnet connections drop, and cannot be re-established. At this point, the Ethernet appears to be working (based on watching the LEDs on the Ethernet board), but I can no longer even ping to the board. The stat shows all sockets either available or waiting for connection. It does, at least, correctly hand-off the "console" duties to the Serial device, which is an improvement. But I need to get to the bottom of the noise problem and, ideally, figure out some way to detect when the Ethernet has likely stopped working, and then figure out how to re-initialize it. It appears to be either the Ethernet device that ceases functioning, as the web server goes out to lunch at the same time as the telnet server. So, perhaps I can do a timeout - if no device is connected for perhaps 10 seconds, then re-do Ethernet.begin(), delete the existing telnet and web servers, and create new ones?

Regards,
Ray L.

Implementing a timeout (when no connections are present), then re-doing the Ethernet.begin() does allow me to recover from the "crash". I have also found the cause of the glitch that is causing the crash. So, I should have a good belt and suspenders work-around for this problem.

I do wish I had a better understanding of how this all works....

Regards,
Ray L.

Here is what I’ve come up with that seems to mostly work…

unsigned long telnetTick = 0;

....

        // Check telnet connection every second
	if (millis() - telnetTick > 1000L)
	{
		telnetTick = millis();
		telnetActive = false;
		for (int socket = 0; socket < MAX_SOCK_NUM; socket++)
		{
			uint8_t s = W5100.readSnSR(socket);
			socketStat[socket] = s;
			Serial.printf("socket[%d]=%02x/%s\n", socket, s,
				s==0x00 ? "available" :
				s==0x14 ? "waiting connection" :
				s==0x17 ? "connected" :
				s==0x1c ? "waiting close" :
				s==0x22 ? "UDP" :
				"??");
			if (s == 0x1c)
			{
				Serial.printf("Closing socket %d\n", socket);
				close(socket);
			}
			else if (s == 0x17)
			{
				telnetActive = true;
			}
		}
		if (!telnetActive)
		{
			pConsole = (Stream *)&Serial;
			TelnetConnected = false;
			if (++EthernetResetCnt == 10)
			{
                                // Reset Ethernet if no connection for 10 seconds
				Ethernet.begin(mac, ip);
				EthernetResetCnt = 0;
			}
		}
		else if (telnetActive && !TelnetConnected)
		{
			EthernetClient client = pTelnetServer->available();
			if (client)
			{
				TelnetClient = pTelnetServer->available();
				pConsole = (Stream *)&TelnetClient;
				TelnetConnected = true;
				EthernetResetCnt = 0;
			}
		}
		else if (telnetActive)
		{
			EthernetClient client = pTelnetServer->available();
			if (client)
			{
				TelnetClient = pTelnetServer->available();
				pConsole = (Stream *)&TelnetClient;
			}
			EthernetResetCnt = 0;
		}
	}

So far, it seems to work as it should, except that sometimes, on boot-up, the W5100 will never show a connection. Reset the board (again!), and it generally works correctly. There seems to be some instability/unreliability in the power-up initialization.

OK, so I'm still fighting this problem... It sorta, kinda works, but does not reliably make the connection. Once a connection is made, it works fine, unless the connection is lost. At that point, it may, or may not, be possible to reconnect.

So, I believe the problem has to do with the way I'm updating my TelnetClient, by periodically doing a pTelnetServer->available(), checking the return value for NULL, and if not, setting TelnetClient to the returned client. available() returns a new client object each time it's called, but that client may be bound to a different port and on the previous call. So, I'm thinking perhaps what I need to do is:

  1. If no connection is present, revert TelnetClient to Serial.
  2. When a connection is established, AND available return non-NULL, the set TelnetClient to the retrurned client.
  3. Do NOT change TelnetClient unless and until the connection is lost or otherwise closed.

Does that sound correct? That obviously means there can be only one active connection at a time (which is fine), and any additional connections will simply be ignored until the first one is closed.

Regards,
Ray L.

But, noise in the system (solenoids and solenoid valves) seems to be whacking the Ethernet.

What level of isolation and noise reduction (suppression) do you have with these? Are they controlled with mechanical relays? How close to the electronics are the solenoids and solenoid control circuit?

dlloyd:
What level of isolation and noise reduction (suppression) do you have with these? Are they controlled with mechanical relays? How close to the electronics are the solenoids and solenoid control circuit?

The original noise problem has been resolved. So, I'm down to just the mechanics of cleanly opening and closing connections. The Ethernet connection itself is fine, as the web server always works, even when Telnet is being fussy.

Regards,
Ray L.

It seems to me it would make sense to do the following:

  1. When no connection is active, scan all sockets, until one indicates connected.
  2. Create my own client object for that socket, using pTelnetClient = new EthernetClient(socket);
  3. Remember which socket we're using, and poll status for ONLY that socket until the connection is closed.
  4. Reset pTelnetClient = &Serial, and return to #1 above.

Regards,
Ray L.

There is clearly something very fundamental I’m missing here… Here is my current code:

int telnetSocket = -1;
byte socketState[MAX_SOCK_NUM];


loop()
{
	if (telnetSocket < 0)
	{
		for (int socket = 0; socket < MAX_SOCK_NUM; socket++)
		{
			uint8_t s = W5100.readSnSR(socket);
			if (socketStat[socket] != s)
			{
				Serial.printf("0: socket[%d]=%02x/%s\n", socket, s,
					s == 0x00 ? "available" :
					s == 0x14 ? "wait connection" :
					s == 0x17 ? "connected" :
					s == 0x1c ? "wait close" :
					s == 0x22 ? "UDP" :
					"??");
			}
			socketStat[socket] = s;
			if (s == 0x17)
			{
				// We have a connection
				telnetSocket = socket;
				EthernetClient *client = new EthernetClient(socket);
				pConsole->printf("connecting on socket %d\n", socket);
				pConsole = (Stream *)client;
				pConsole->printf("connected\n");
				break;
			}
		}
	}
	else
	{
		uint8_t s = W5100.readSnSR(telnetSocket);
		if (socketStat[telnetSocket] != s)
		{
			Serial.printf("1: socket[%d]=%02x/%s\n", telnetSocket, s,
				s == 0x00 ? "available" :
				s == 0x14 ? "wait connection" :
				s == 0x17 ? "connected" :
				s == 0x1c ? "wait close" :
				s == 0x22 ? "UDP" :
				"??");
		}
		socketStat[telnetSocket] = s;
		if (s != 0x17)
		{
			// Close connection
			Serial.printf("disconnected\n");
			close(telnetSocket);
			telnetSocket = -1;
		}
	}
}

This code appears to be 100% reliable in making the FIRST telnet connection. When I close the telnet client (I’m using PuTTY), the connection appears to close properly. Here is the output I get from the above code on opening and closing PuTTY:

0: socket[0]=14/wait connection
0: socket[1]=14/wait connection
0: socket[0]=16/??
0: socket[0]=17/connected
connecting on socket 0
1: socket[0]=1c/wait close
disconnected
0: socket[0]=00/available

However, after this, I see NO more output from the above code. It is still running (the rest of the system is still operating normally), but I see no more socket state changes occur, and even opening a browser window does not establish a connection.

If I disable the “if (s == 0x17)” in the above code, and re-boot, so the telnet connection is never recognized by my code, then I can open and close the browser as many times as I like, and it ALWAYS connects properly, and I see all the expected socket state changes.

So, it seems there is something I simply am not understanding about how to properly terminate a telnet session, and whatever it is I am doing wrong causes the W5100 to go out to lunch and stop responding to all connection requests.

Another issue: While the above code works properly if I establish the telnet connection first-thing after a re-boot, if I open the browser first, the above code sees the browser connection as a telnet connection. How do I distinguish between the two, so this code responds only to actual telnet sessions, and ignores browser sessions?

Regards,
Ray L.

Progress! This code, so far, seems to work very well. The trick to being able to re-connect is to repeat the server.begin() when a connection is closed. The trick it “filtering” telnet sessions from browser sessions is to look at the port number associated with the socket. The way I’m doing that right now is, I’m sure, sub-optimal, but does work. I’ll spend some time understanding it all better, and cleaning it up, when I have more time.

	if (telnetSocket < 0)
	{
		for (int socket = 0; socket < MAX_SOCK_NUM; socket++)
		{
			uint8_t s = W5100.readSnSR(socket);
			if (socketStat[socket] != s)
			{
				ShowSockStat(socket, s);  // Display socket status on Serial console
			}
			socketStat[socket] = s;
			if ((W5100.readSnPORT(socket) == 23) && (s == 0x17))
			{
				// We have a telnet connection
				telnetSocket = socket;
				pTelnetClient = new EthernetClient(socket);
				pConsole->printf("connecting on socket %d\n", socket);
				pConsole = (Stream *)pTelnetClient;
				pConsole->printf("connected\n");
				break;
			}
		}
	}
	else
	{
		uint8_t s;
		for (int socket = 0; socket < MAX_SOCK_NUM; socket++)
		{
			s = W5100.readSnSR(socket);
			if (socketStat[socket] != s)
			{
				ShowSockStat(socket, s);
			}
			socketStat[socket] = s;
		}
		if (socketStat[telnetSocket] != 0x17)
		{
			// Close connection
			pConsole->printf("disconnecting on socket %d\n", telnetSocket);
			disconnect(telnetSocket);
			close(telnetSocket);
			delete pTelnetClient;
			pConsole = &Serial;
			pConsole->printf("disconnected from socket %d\n", telnetSocket);
			telnetSocket = -1;
			pTelnetServer->begin();
		}
	}

Regards,
Ray L.