Using SEND_KEEP with W5100 Ethernet Shield

Hello All

This is my first proper post to the Arduino forums so please be gentle!

I'm using an Arduino Uno and the W5100 Ethernet Shield to do some hardware control using TELNET. I've written the server code using Arduino V1.0 and I'm using the standard Ethernet library as shipped with the IDE. The concept works fine - type commands into a TELNET server and things happen, with responses fed back up the wire to the client - but I need to do some form of link loss detection and make-safe a couple of seconds after the link fails.

What I was hoping to do was to send a TCP KEEP_ALIVE function to the client and check for an ACK response. As a proof of concept I added some code to the EthernetServer and W5100 libraries to send the KEEP_ALIVE command but I'm struggling when it comes to confirming that an ACK was received by the W5100.

I've done a search through the forums and can't find anything that relates to what I'm trying to do, so I'd be grateful for any help that you can offer!!

I've been through the W5100 datasheet and it seems that when I write a SEND_KEEP to SnCR (Socket n Command Register), the W5100 should send a TCP KEEP_ALIVE packet and await receipt of the ACK response. According to the datasheet, failure to receive an ACK within a given timeout should cause the KEEP_ALIVE packet to be re-transmitted until a resend limit is reached, after which the TIMEOUT bit of SnIR (Socket n Interrupt Register) to be set. The re-transmit interval is set in the RTR register and the resend limit is set by RCR. In my case, RTR is set to 0x07D0 (200mS) and RCR is set to 0x08 - I've written a function to read these registers and can confirm that that's the values they hold.

My hacked code checks to see whether there's an active connection, then writes SEND_KEEP to SnCR and reads SnCR until it clears (which should indicate successful receipt of an ACK). Whilst waiting, a do/while loop reads SnIR to see if the TIMEOUT bit is set and, if so, returns an error. Assuming that SEND_KEEP completes normally, which should indicate that the ACK packet was received, the function also returns normally. I have the Arduino Shield and a couple of PCs on a Network Hub so I can monitor the traffic to see what's going on (one PC is running the TELNET client, the other is the Wireshark sniffer).

So, my main sketch polls a send_keep_alive function every 750mS to check for link integrity and when the link is up, the KEEP_ALIVE and ACK packets are sent and received normally. When I pull the link to the TELNET client PC, the W5100 firstly sends 3 KEEP_ALIVE packets at 750mS intervals (from the sketch) then the time period expands and the W5100 starts retransmitting bytes of old (stale) data at 5 second intervals for the next 25 seconds or so, after which the W5100 pulls the plug and closes the socket. During this, SnCR/SEND_KEEP completes normally and the SnIR/TIMEOUT bit is not set until the socket is closed! The stale data is information previously sent from Arduino to the TELNET client and can be either information type in, or the initial TELNET/IAC (setup) commands if nothing has been typed.

I've thrown loads of debug strings in to write status details to the Serial port, and it seems like SEND_KEEP returns OK and the TIMEOUT bit is not set, regardless of whether an ACK was received. I only seem to get TIMEOUT is when the socket is forcefully closed, around 30 seconds after the link fails.

I don't think I'm doing anything stupid in my code and I even put a message an Wiznet's Q&A to which they replied, confirming that the information in the datasheet is correct.

I'm now at a loss and I was wondering if anyone else has used SEND_KEEP to check/confirm link integrity and, if so, how did you get it to work?!

I can post the code I've used but I'm new to the Arduino forum and didn't want to put to much up on my first real post..... :roll_eyes:

Regards

Derek.

This user may have the answer for you:

Claims at least 2 simultaneous telnet (persistent) connections without confusion. I am relatively familiar with the w5100 C++ code, and I have been waiting to see the code that does that. :slight_smile:

Hi SurferTim

Thanks for the reply.

That post concerns multiple (Telnet) clients, which can be done by creating a different Client instance for each connection. In my case, once I get this working I'll be restricting the clients to one as I would only want one connection to control the hardware at any one time (to avoid any conflicts..!). I'm only using Telnet as a test and once my skills are up to it, I intend to write a PC-based client to control the Arduino server properly. The client can include a handshaking/watchdog feature using the TCP/IP application layer, but I was hoping to use something on the transport layer.

What I'm trying to do is prove that a client is still connected and/or that the link is intact. I tacked the following code snippets to the end of w5100.cpp and EthernetServer, and amended the header files to suit.

As you're familiar with the W5100 C++ code, perhaps you could tell me where I've gone wrong :~

<<w5100.cpp SNIPPET BEGIN>>
//Experimental DEBUG Code
uint8_t W5100Class::send_keep_alive(SOCKET s) {
// Send KEEP_ALIVE packet to socket
writeSnCR(s, Sock_SEND_KEEP);
// Wait for command to complete
do {
if ((W5100.readSnIR(s) & SnIR::TIMEOUT) == SnIR::TIMEOUT){
Serial2.print("w5100.cpp: TIMEOUT bit set\r\n"); //DEBUG CODE
W5100.writeSnIR(s, SnIR::TIMEOUT); //Write bit-clear to SnIR
return(1); //FAIL - Timeout bit set
}
}
while (readSnCR(s)); //End of command_complete loop
return(0); //Success - ACK received
} //END OF DEBUG
<>

<>
//Experimental DEBUG Code
size_t EthernetServer::keepalive()
{
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
client.status() == SnSR::ESTABLISHED) { //If we have a TCP connection
uint8_t ret=(W5100.send_keep_alive(sock));
return(ret);
} //End of SnSR::ESTABLISHED
return(2); //Return with no socket connected
} //End of sock loop
return(3); //Unknown error has occurred
}
//END OF DEBUG
<>

I call server.keepalive(); from my sketch which in turn calls the w5100.send_keep_alive function - the value returned determines whether the connection is live or dead or if the client has bailed. A "Successful" return simply loops awaiting input, whilst a returned "fail" value would initiate client.stop() and make safe my I/O to avoid a runaway condition.

As you've probably guessed I'm still fairly new to programming (and TCP/IP generally) and I was expecting that when the client response to a KEEP_ALIVE failed, the SnCR register would remain high as no ACK was received. This would keep us in the command_complete do/while loop, testing for a TIMEOUT condition in the SnIR. With RTR at 200mS and RCR at 8 I was expecting to see a further 8 KEEP_ALIVE packets on the wire at 200mS intervals, followed by the SnIR/TIMEOUT bit set approximately 1800mS after the first ACK fail.

This isn't what I'm getting though and the TCP_RETRANSMIT of stale data is confusing me!

Any help would be appreciated!!

Kind Regards

Derek

I am using w5100 to implement a tcp serial gateway and wanted to implement the same feature like you. I also tried to issue a timeout error as soon as the timeout bit in S2_IR was set , but i could'nt succeed. Maybe some guy in the w5100 team can help ? Maybe some information in w5100 manual is not correct?

At this time i just decided to have my board shut down the session after 24 seconds of unacknowledged messages. :frowning:

Stefano

The WizNet5100 datasheet is not clear about when the SEND_KEEP command ends. It could be easily interpreted as the command is finished when the keep alive packet has been sent.

I agree that the retries should occur faster than you experienced them but I cannot find the part of your code where you're setting these registers. Are you just trusting that the defaults of the datasheet (for RCR and RTR) are really set that way (I agree you should be able to trust but it's always better to set it explicitly)?

With RTR at 200mS and RCR at 8 I was expecting to see a further 8 KEEP_ALIVE packets on the wire at 200mS intervals, followed by the SnIR/TIMEOUT bit set approximately 1800mS after the first ACK fail.

Me too, but are the registers set to these values?