Making A Telnet Session Persist

Tried this in the Programming forum, and got no replies....

There is clearly something I don't understand about using Telnet with Arduino.... I have an application that can use either a Serial port, or telnet as its "console". I want the Telnet session to take priority when it is connected, otherwise default to the Serial port. This works, but only up to a point. On reset, the Serial port is active, and works fine. If I then open a Telnet session, the appplication connects only when something is typed on the Telnet terminal, then disconnects almost immeidately. This means that most, if not all, of the response to the command entered on the Telnet terminal goes to the Serial port instead.

This is the code I'm using to detect an active Telnet session, and do the switching. What am I missing? How do I reliably detect that a Telnet client is available, and when it disconnects?

// Globals
boolean TelnetConnected = false;
EthernetServer *pTelnetServer = NULL;
EthernetClient TelnetClient;
Stream *pConsole;


// Switching code in loop()
    if (TelnetConnected && !TelnetClient.connected())
    {
        if (pTelnetServer)
        {
            pTelnetServer->begin();
            pConsole = (Stream *)&Serial1;
        }
        TelnetConnected = false;
        LCDMsg(1, 1, "Telnet Disconnected ");
    }
    else if (!TelnetConnected && pTelnetServer->available())
    {
        TelnetConnected = true;
        TelnetClient = pTelnetServer->available();
        pConsole = (Stream *)&TelnetClient;
        LCDMsg(1, 1, "Telnet Connected    ");
    }

I'm using PuTTY for the Telnet client, if that matters....

Regards, Ray L.

This is your question in the Programming section : http://forum.arduino.cc/index.php?topic=391428.0

You don't show a complete sketch, so we can not test it. Where did you find an example for telnet ? The 'EthernetServer' is not created, you have only a pointer to it, but it points to what ? You don't even say which Arduino board you use.

The problem is, that you want to do something that is not easy and switching between Serial and Telnet is not often done, perhaps you are the only one, and there might be problems in the library for disconnecting. Because of that little piece of code, we have no idea what you are doing.

Koepel: This is your question in the Programming section : http://forum.arduino.cc/index.php?topic=391428.0

You don't show a complete sketch, so we can not test it. Where did you find an example for telnet ? The 'EthernetServer' is not created, you have only a pointer to it, but it points to what ? You don't even say which Arduino board you use.

The problem is, that you want to do something that is not easy and switching between Serial and Telnet is not often done, perhaps you are the only one, and there might be problems in the library for disconnecting. Because of that little piece of code, we have no idea what you are doing.

The problem is, this is part of a huge program - over 250K of object code - and the behavior is inconsistent. At times, it works as I want, at other times it doesn't, with NO changes to the hardware or code. So, I have no simple example that is guaranteed to exhibit the problem.

The basic question is: How/When/Why is the connection terminated? The Telnet client on the PC never goes away, so why does TelnetClient.connected() return false? What triggers that? I've looked at the EthernetServer source code, but don't know enough about Ethernet to make heads or tails out of what it's doing. On other systems (unix/Linux) I've written simple Telnet clients that were very straight-forward, and worked as I wanted - the client opens a socket, and it stays open until either the client explicitly closes it, or the cable is unplugged. But it looks to me like the Arduino EthernetServer is closing the connection when it no longer sees any incoming data - incoming data causes TelnetServer.connected() to return true for only long enough for the data to be received, then it returns false. Well, at least sometimes. Is there a timeout at work here? Can it be changed?

Regards, Ray L.

One thing comes to mind: I am running both a web server and telnet server, so I create two EthernetServer objects, one on port 80, one on port 23. Is this ok? Or does the Arduion only support running a single EthernetServer at a time?

Is it ok, or required, to run TelnetServer->begin() each time the connection is dropped? Or is that either useless, or counter-productive?

The Ethernet examples are so simple, and there is so little explanation of how it all works, it's very hard to know what is the right way to handle these conditions. I would think the answers to these questions would be pretty easy for someone who understands Ethernet, and the Arduino Ethernet libraries.

Regards, Ray L.

Since you are not saying which Arduino board you use, I'm guessing that you have a Arduino Mega 2560 with an Ethernet Shield and W5100 chip. Not a Arduino Yun or a ESP8266. The Arduino Mega has a lot of flash size, but the ram is still limited for a large sketch. If it sometimes works and sometimes not, that is also an indication of a ram overflow problem. Could you check runtime the available ram ? Since you don't give a link to an example of a telnet server, I think you wrote it yourself. I still don't know where that pTelnetServer is pointing to, and you don't want to say it. That's a problem.

When memory if overwritten, or there is a stack overflow, or a not-assigned pointer it used, the resulting code might still run. That means there might be a bug in your code all the time, and it just happens to pop to the surface now.

As far as I know, it is possible to have up to four servers running for a W5100, and the library should support it. http://subethasoftware.com/2013/04/08/arduino-ethernet-and-multiple-socket-server-connections/

The server.begin() is ment to be called just once. If you have a Arduino Mega 2560 board, I could look into the code.

I have a Mega + Ethernet, but I have not added a telnet server yet. It seems pretty straightforward: https://www.arduino.cc/en/Reference/ServerBegin Or this: https://github.com/josejamilena/ArduinoTelnetServer/blob/master/ArduinoTelnetServer/ArduinoTelnetServer.ino The version by SurferTim is a little different : http://playground.arduino.cc/Code/Telnet

If you have a test sketch, I could test the timeouts.

Koepel:
Since you are not saying which Arduino board you use, I’m guessing that you have a Arduino Mega 2560 with an Ethernet Shield and W5100 chip. Not a Arduino Yun or a ESP8266.
The Arduino Mega has a lot of flash size, but the ram is still limited for a large sketch. If it sometimes works and sometimes not, that is also an indication of a ram overflow problem. Could you check runtime the available ram ?
Since you don’t give a link to an example of a telnet server, I think you wrote it yourself.
I still don’t know where that pTelnetServer is pointing to, and you don’t want to say it. That’s a problem.

When memory if overwritten, or there is a stack overflow, or a not-assigned pointer it used, the resulting code might still run. That means there might be a bug in your code all the time, and it just happens to pop to the surface now.

As far as I know, it is possible to have up to four servers running for a W5100, and the library should support it.
Arduino Ethernet and multiple socket server connections | Sub-Etha Software

The server.begin() is ment to be called just once. If you have a Arduino Mega 2560 board, I could look into the code.

I have a Mega + Ethernet, but I have not added a telnet server yet. It seems pretty straightforward:
Arduino - ServerBegin
Or this: ArduinoTelnetServer/ArduinoTelnetServer.ino at master · josejamilena/ArduinoTelnetServer · GitHub
The version by SurferTim is a little different : http://playground.arduino.cc/Code/Telnet

If you have a test sketch, I could test the timeouts.

Sorry. It’s a Due, with roughly half the FLASH, and at least 32K of RAM free. All working perfectly in all other respects.

Here is a sample program, with only the telnet functionality implemented. It simply echos back whatever it receives. It should build and work just as well on a Mega2560 as on a Due, using a W5100 Ethernet shield.

Keep in mind, this example will not necessarily exhibit the problem behavior, since that seems to come and go for reasons I have been unable to determine. But, again, the point is, the desired behavior is it should indicate Client.connected() unless and until the client closes the port.

#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include "utility/w5100.h"
#include "utility/socket.h"
#include <Client.h>
#include <EthernetServer.h>
#include <EthernetClient.h>

#define ETH_CS          10
#define SD_CS         4

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEB };  // Actual value is derived from CPU unique ID
IPAddress ip(10, 1, 1, 23);     // Fixed IP address to use if DHCP is disabled.

EthernetServer *pTelnetServer = NULL;
EthernetClient TelnetClient;
boolean TelnetConnected = false;
Stream *pConsole = NULL;


void setup()
{
  Serial.begin(115200);
  pConsole = &Serial;

  // Disable SD card CS, so it doesn't bugger Ethernet
  pinMode(SD_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
  pinMode(ETH_CS, OUTPUT);
  digitalWrite(ETH_CS, LOW);
  pinMode(87, INPUT_PULLUP);    // KLUDGE to make Ethernet work on Due!
  pinMode(77, INPUT_PULLUP);

  Ethernet.begin(mac, ip);

  pTelnetServer = new EthernetServer(23);
  pTelnetServer->begin();
  TelnetConnected = false;
}

void loop()
{
  if (TelnetConnected && !TelnetClient.connected())
  {
    if (pTelnetServer)
    {
      pTelnetServer->begin();
      pConsole = (Stream *)&Serial;
    }
    Serial.print("Telnet disconnected\n");
    TelnetConnected = false;
  }
  else if (!TelnetConnected && pTelnetServer->available())
  {
    TelnetConnected = true;
    TelnetClient = pTelnetServer->available();
    pConsole = (Stream *)&TelnetClient;
    Serial.print("Telnet connected\n");
  }
  if (pConsole)
  {
    if (pConsole->available())
      pConsole->print(pConsole->read());
  }
}

Regards,
Ray L.

The links to the two telnet server examples you provided look helpful. I'll have to study and try them. Interesting that the second one seems to talk directly to the W5100 driver to create a persistent "connect" status. Could be just what I'm missing...

Regards, Ray L.

You have an Arduino Due, and I hope your Ethernet Shield is a 'R3' version ?

I think the same Ethernet library source code is used for the Due and the Mega board. https://github.com/arduino/Arduino/tree/master/libraries/Ethernet/src

I'm adding a telnet to my Mega + Ethernet at the moment, and I encounter a few problems. Some telnet clients on my computer do not close the connection, or did not close it well. My webserver has already the checkSockStatus() addition : http://playground.arduino.cc/Code/WebServerST And it encounters open unused sockets, because I'm trying a few things with telnet clients in linux.

I'm also using DHCP with Ethernet.maintain().

I did not test your example: Don't set the chip select for the W5100 low, because that is active. Your SPI bus will be blocked at that moment and the W5100 chip is confused. Make every chip select high and wait a few milliseconds. The Ethernet library takes care of the chip select of pin 10. The first code in the loop() uses "TelnetClient.connected()", but TelnetClient is not assigned yet. https://www.arduino.cc/en/Reference/ServerAvailable Is server.available() the only function that returns a client for a telnet ? I think it is.

Can the client still be used after server.available() returns false ? I don't know. If that is not possible, then it is not possible to detect a disconnected client with the normal Ethernet functions. Can a client from a previous loop() be used to check if it is connected or should a new server.available() be called ? Normally a client.connected() is used when the Arduino is the client and something else on the internet is the server. Now the Arduino wants to know if one its own clients is still connected. That should be a function of the EthernetServer object.

I think I will make a command interpreter that returns its answer to a buffer. So I can use the same command interpreter for the Serial and the telnet, and keep them both active all the time.

My conclusion: I am testing a few things at the moment, but those open sockets when I close the telnet client in a bad way, that can be a problem. It think that I can say that the client.connected() does have problems with a telnet server. I don't even know if it can be used when the Arduino is the server and the Arduino wants to check its own clients. The solution seems to be to check the sockets, as in the example of SurferTim : http://playground.arduino.cc/Code/Telnet At this moment, that might be the only good solution.

If you need help with my persistent telnet code, I'm back now. I've been offline for a while due to a medical problem, but feeling much better now.

edit: The problem I had with any of the client functions is the inability to determine if there were new clients that had not sent anything, or current clients still connected. I had to resort to the low level functions you see in my code.

Note my code will send a "Hello" to the client without the client sending anything. Can't do that with the client functions.

Koepel:
You have an Arduino Due, and I hope your Ethernet Shield is a ‘R3’ version ?

Yes, it is R3, and works perfectly other than this one problem.

Koepel:
Don’t set the chip select for the W5100 low, because that is active. Your SPI bus will be blocked at that moment and the W5100 chip is confused. Make every chip select high and wait a few milliseconds. The Ethernet library takes care of the chip select of pin 10.

Fixed, though it doesn’t seem to make any difference, since the call to Ethernet.begin() will set it properly.

Koepel:
The first code in the loop() uses “TelnetClient.connected()”, but TelnetClient is not assigned yet.
Arduino - ServerAvailable

Actually, TelnetClient is assigned:

EthernetClient TelnetClient;

Regards,
Ray L.

SurferTim: If you need help with my persistent telnet code, I'm back now. I've been offline for a while due to a medical problem, but feeling much better now.

edit: The problem I had with any of the client functions is the inability to determine if there were new clients that had not sent anything, or current clients still connected. I had to resort to the low level functions you see in my code.

Note my code will send a "Hello" to the client without the client sending anything. Can't do that with the client functions.

it may be a few days before I can try to make use of your code, as I've got things torn apart for other reasons. But I will give it a try and report back.

Regards, Ray L.

Let me know. The only thing my sample code in the playground is missing is a timeout if the client doesn't communicate with the telnet server for a while. If you connect and walk away, it stays connected until you close or disconnect the client software.

Koepel: Don't set the chip select for the W5100 low, because that is active. Your SPI bus will be blocked at that moment and the W5100 chip is confused. Make every chip select high and wait a few milliseconds. The Ethernet library takes care of the chip select of pin 10.

FWIW - This seems demonstrably incorrect. I changed my initialization to set ETH_CS HIGH, and the W5100 went out to lunch. Set it back to LOW, and all is well again. Is it getting inverted somewhere along the way?

Regards, Ray L.

What do you mean by "out to lunch"?

Koepel is correct. The ethernet CS should be D10, and setting it as OUTPUT and HIGH should disable the w5100 and the ethernet library should deal with it from then on.

The only exception to this is the Due. If you set D10 as OUTPUT, the ethernet shield/module will fail.

SurferTim: Let me know. The only thing my sample code in the playground is missing is a timeout if the client doesn't communicate with the telnet server for a while. If you connect and walk away, it stays connected until you close or disconnect the client software.

What I want is for it to stay connected until the client application is closed, so it should be fine.

Regards, Ray L.

SurferTim:
What do you mean by “out to lunch”?

Koepel is correct. The ethernet CS should be D10, and setting it as OUTPUT and HIGH should disable the w5100 and the ethernet library should deal with it from then on.

The only exception to this is the Due. If you set D10 as OUTPUT, the ethernet shield/module will fail.

I mean the W5100 never comes up - no blinking lights. I AM on a Due, but setting D10 as OUTPUT, then setting it LOW works fine. What is the alleged problem with setting it as an output? It HAS to be set as an output to work…

Regards,
Ray L.

Are you guys perhaps cunfusing the W5100 CS with the SD CS? I know the SD CS MUST be set high for the shield to work at all. I learned that one the hard way…

Regards,
Ray L.

It HAS to be set as an output to work....

There are 2 pins from the Due processor to the D10 pin on the board. One is the SPI SS pin, and the other is the digital D10 pin. If you set D10 to OUTPUT, the SS and D10 pins fight for control, and the SS sometimes loses. http://www.arduino.cc/en/uploads/Main/arduino-Due-schematic.pdf Look at PA28 and PC29 on the processor. Both processor pins are connected to D10 pin on the board.

edit: You want to set D10 to HIGH only. That will activate the weak pullup on D10, but not to OUTPUT. The SPI SS pin can easily overcome the weak pullup, and the weak pullup is enough to keep the w5100 SPI disabled until the SPI bus is initialized..

How will I remember that if I replace my Mega with a Due some day ? :(

I found the Uno, Mega, and Due will all use the same code. Don't set D10 to OUTPUT on any of them, just set it to HIGH. The weak pullup is enough to disable the w5100 SPI on all of them.