So close, yet so far… I took your code, turned it into a class which inherits from Stream, and built a test program, which works fine. I plumbed it into my big application, and as soon as I open a single telnet session, things go sideways. I figured maybe the fact that my application also runs an EthernetServer on port 80 for the web server could have something to do with the problem, so I added that to the test program, and sure enough it failed. I added a line of code to make the telnet code ignore any sockets on port 80, and the test program works fine again. Put that change into my big app, and I can now open a single telnet session, which works fine. When I either open a second PuTTY instance, or close the first one, something is losing its mind, and the app crashes or at least stops communicating with the SerialMonitor.
I have to believe this still has something to do with the other EthernetServer on port 80, as that is the only other code that is even aware the Ethernet exists. But I don’t know why it’s going wonky when the telnet session is disconnected. Since the console stops functioning at the point where things go wonky, I don’t yet know what’s going on.
Here’s the code for my TelnetServer class:
#ifndef TELNETSERVER_H
#define TELNETSERVER_H
#include <Ethernet.h>
#include <EthernetServer.h>
#include <utility/w5100.h>
#include <utility/socket.h>
class TelnetServer : Stream
{
public:
TelnetServer();
TelnetServer(byte port);
~TelnetServer();
void begin(void);
boolean update(void);
boolean connected(void) { return (activeSocket >= 0); }
// Virtual methods required by Stream:
int available();
int read();
int peek();
void flush();
// Virtual methods required by Print:
size_t write(uint8_t);
size_t write(const uint8_t *buf, size_t size);
private:
static const uint8_t SOCK_CLOSED = 0x00;
static const uint8_t SOCK_ARP1 = 0x11;
static const uint8_t SOCK_INIT = 0x13;
static const uint8_t SOCK_LISTEN = 0x14;
static const uint8_t SOCK_SYNSENT = 0x15;
static const uint8_t SOCK_ESTABLISHED = 0x17;
static const uint8_t SOCK_FIN_WAIT = 0x18;
static const uint8_t SOCK_CLOSING = 0x1a;
static const uint8_t SOCK_TIME_WAIT = 0x1b;
static const uint8_t SOCK_CLOSE_WAIT = 0x1c;
static const uint8_t SOCK_LAST_ACK = 0x1d;
static const uint8_t SOCK_ARP2 = 0x21;
static const uint8_t SOCK_UDP = 0x22;
static const uint8_t SOCK_ARP3 = 0x31;
static const uint8_t SOCK_IPRAW = 0x32;
static const uint8_t SOCK_MACRAW = 0x42;
static const uint8_t SOCK_PPOE = 0x5f;
static const uint8_t RX_BUF_LEN = 255;
void SendReceive(void);
uint8_t RxBufFree(void) { return RX_BUF_LEN - RxCnt; }
inline uint32_t saveIRQState(void) { uint32_t pmask = __get_PRIMASK() & 1; __set_PRIMASK(1); return pmask; }
inline void restoreIRQState(uint32_t pmask) { __set_PRIMASK(pmask); }
byte socketStat[MAX_SOCK_NUM];
boolean connectStatus[MAX_SOCK_NUM];
uint16_t Port;
EthernetServer *pServer = NULL;
int8_t activeSocket = -1;
uint8_t RxBuf[RX_BUF_LEN];
uint8_t RxGet = 0;
uint8_t RxPut = 0;
uint8_t RxCnt = 0;
};
#endif
#include "TelnetServer.h"
TelnetServer::TelnetServer()
{
}
TelnetServer::TelnetServer(byte port)
{
Port = port;
pServer = new EthernetServer(port);
for (int i = 0; i < MAX_SOCK_NUM; i++)
{
socketStat[i] = 0;
connectStatus[i] = 0;
}
}
TelnetServer::~TelnetServer()
{
}
void TelnetServer::begin(void)
{
pServer->begin();
}
boolean TelnetServer::update(void)
{
boolean listening = false;
Serial.printf("\n\n");
for (int i = 0; i < MAX_SOCK_NUM; i++)
{
uint16_t socketport = W5100.readSnPORT(i);
if (socketport == 80)
continue;
uint8_t s = W5100.readSnSR(i);
socketStat[i] = s;
Serial.printf("Socket %d = %02x\n", i, s);
if (s == SOCK_CLOSE_WAIT)
{
Serial.printf("Closing socket %d\n", i);
close(i);
connectStatus[i] = false;
if (activeSocket == i)
activeSocket = -1;
}
if (s == SOCK_LISTEN)
listening = true;
if (s == SOCK_ESTABLISHED && connectStatus[i] == 0)
{
// Found a new connection, so close any old ones
Serial.printf("Connecting on socket %d\n", i);
if (activeSocket >= 0)
{
disconnect(activeSocket);
//while (W5100.readSnSR(i) == SOCK_ESTABLISHED)
// ;
connectStatus[activeSocket] = false;
}
activeSocket = i;
connectStatus[i] = true;
}
}
if (!listening)
{
for (int i = 0; i < MAX_SOCK_NUM; i++)
{
if (socketStat[i] == 0)
{
socket(i, SnMR::TCP, Port, 0);
listen(i);
break;
}
}
}
SendReceive();
return (activeSocket >= 0);
}
void TelnetServer::SendReceive(void)
{
if (activeSocket == -1)
return;
while (W5100.getRXReceivedSize(activeSocket) && RxBufFree())
{
// Read as much as we can
int16_t cnt = recv(activeSocket, &RxBuf[RxPut], RxBufFree());
if (cnt == 0)
break;
RxPut += cnt;
uint32_t pmask = saveIRQState();
RxCnt += cnt;
restoreIRQState(pmask);
}
}
// Virtual methods required by Stream
int TelnetServer::available()
{
return (int)RxCnt;
}
int TelnetServer::read()
{
int ret = -1;
if (RxCnt)
{
ret = RxBuf[RxGet];
RxGet++;
uint32_t pmask = saveIRQState();
RxCnt--;
restoreIRQState(pmask);
}
return ret;
}
int TelnetServer::peek()
{
int ret = -1;
if (RxCnt)
{
ret = RxBuf[RxGet];
}
return ret;
}
void TelnetServer::flush()
{
RxCnt = RxGet = RxPut = 0;
}
// Virtual methods required by Print:
size_t TelnetServer::write(uint8_t c)
{
uint16_t ret = 0;
if (activeSocket >= 0)
ret = send(activeSocket, &c, 1);
return ret;
}
size_t TelnetServer::write(const uint8_t *buf, size_t size)
{
uint16_t ret = 0;
if (activeSocket >= 0)
ret = send(activeSocket, buf, size);
return ret;
}
The following is called from loop() to keep the telnet status up-to-date, and to do the switching of the Stream *pConsole pointer that the rest of the app uses to read/write the console.
void UpdateTelnet(void)
{
if (pTelnetServer->update())
{
if (pConsole != (Stream *)pTelnetServer)
{
pConsole->printf("\nDisconnecting...\n");
delay(10);
pConsole = (Stream *)pTelnetServer;
pConsole->flush();
pConsole->printf("\nConnected\n");
}
}
else if (pConsole == (Stream *)pTelnetServer)
{
pConsole->printf("\nDisconnecting...\n");
delay(10);
pConsole = (Stream *)&Serial;
pConsole->flush();
pConsole->printf("\nConnected\n");
}
}
Regards,
Ray L.