Ethernet server won't receive unless sending

Hi,

I'm trying to build a very simple serial-to-Ethernet server (code below). But I can only receive over Ethernet when I'm sending something. If I don't send anything, then the characters get stuck somewhere until I send them.

Does anyone have any ideas as to why Ethernet sending only happens when I'm receiving characters?

/*
 Serial to ethernet
 */

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xE1, 0xAB };
IPAddress ip(192, 168, 0, 200);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);


// telnet defaults to port 23
EthernetServer server(23);

void setup() {
  // Disable SD card
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);
  // start listening for clients
  server.begin();
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // wait for a new client:
  EthernetClient client = server.available();

  if (client) {
    if (Serial.available() > 0) {
      char thisChar = Serial.read();
      if (client.connected()) {
        client.print(thisChar);
      }
    }

    if (client.connected() && client.available()) {
      // read the bytes incoming from the client:
      char thisChar = client.read();
      Serial.print(thisChar);
    }
  }
}

Thanks,

Ag Primatic

I don't understand this.

But I can only receive over Ethernet when I'm sending something. If I don't send anything, then the characters get stuck somewhere until I send them.

It sounds like you are saying that the Arduino doesn't receive anything until you send it something. That is what I would expect.

Let me try to be clearer.

If I send data from my telnet window to serial, everything works as expected. When I type a character on the telnet input, it shows up on the serial port output.

But when I type a character (or a line) on the serial port input, nothing comes out on the telnet output. However, if I then type on the telnet input, one character comes out on the telnet output for every character typed on the telnet input. It's like the characters are buffered in the Arduino, just waiting for Ethernet receive before allowing any characters out.

I included the code so anyone with an Ethernet shield could try my experiment and (hopefully) get the same behavior.

I've tried it with a Windows telnet client as well as MacOS, and it behaves the same way. I ran Wireshark and it shows that the Ethernet packets are not sent (with my serial port characters) until Ethernet packets are received.

Am I missing something in my code to allow the transmission of Ethernet characters asynchronously to receiving them?

This is the basic code I use for telnet. You might have to modify it to meet your requirements.
http://playground.arduino.cc/Code/Telnet

That code checks every 5 seconds due to the delay in the loop function so you can see what is happening. You can reduce or remove the delay, but it really rolls along if you remove it.

First, thank you for the code.

I tried Arduino Playground - Telnet, and it works as expected. But, when I added these lines to the bottom of the first for loop in ShowSockStatus() so I can send data from the serial port to the Ethernet telnet session:

      if (Serial.available() > 0) {
        char thisChar = Serial.read();
        send(i, (unsigned char*)&thisChar, 1);
      }

it behaves in the exact (incorrect) way that my few lines of code do--it only sends the character when Ethernet characters are received.

So my question remains: why can't I send an Ethernet message until an Ethernet message is received?

Post your entire sketch that you modified.

edit: How do you specify which socket gets that message when you enter it on the serial monitor?

edit2: Here is code that works. It sends the text to all telnet clients that are connected.

#include <SPI.h>
#include <Ethernet.h>
#include <utility/w5100.h>
#include <utility/socket.h>

#define port 80

byte socketStat[MAX_SOCK_NUM];
byte connectStatus[MAX_SOCK_NUM];

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEC };

// change to your network settings
IPAddress ip( 192,168,2,2 );
IPAddress gateway( 192,168,2,1 );
IPAddress subnet( 255,255,255,0 );

EthernetServer server(port);

void setup()
{
  Serial.begin(115200);

  // disable SD
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  Serial.println(Ethernet.localIP());

  delay(2000);
  server.begin();
}

void loop() {
  ShowSockStatus();
  delay(5000);  
}

byte serialCount = 0;
char serialBuffer[32];

void ShowSockStatus()
{
  byte listening = 0;

  while((Serial.available() > 0) && (serialCount < 31))
  {
    serialBuffer[serialCount] = Serial.read();
    serialCount++;
    serialBuffer[serialCount] = 0;
  }
  
  Serial.println();
  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;

    if(s == 0x1C) {
      close(i);
      connectStatus[i] = 0;
    }

    if(s == 0x14) listening = 1;      

    if(s == 0x17 && connectStatus[i] == 0) {
      connectStatus[i] = 1;
// comment out this send to stop initial "Hello"
      send(i,(unsigned char*)"Hello\r\n",7);
    }

    if(s == 0x17 && strlen(serialBuffer) > 0)
    {
      send(i,(unsigned char*)serialBuffer,strlen(serialBuffer));
    }
    
    Serial.print(F(":0x"));
    if(s < 16) Serial.print(F("0"));
    Serial.print(s,HEX);
    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.print(F(") "));
    Serial.print(F("RX:"));
    unsigned int rxCount = W5100.getRXReceivedSize(i);        

    Serial.print(rxCount);
    Serial.print(F("  "));

    if(rxCount > 32) rxCount = 32;

    if(rxCount > 0) {
      unsigned char rxBuffer[33];

      recv(i,rxBuffer,rxCount);

      rxBuffer[rxCount] = 0;

      Serial.print((char*)rxBuffer);
      send(i,(unsigned char*)"ok ",3);
      send(i,(unsigned char*)rxBuffer,strlen((char*)rxBuffer));

      if(strncmp((char*)rxBuffer,"quit",4) == 0) {
        disconnect(i);

        unsigned long start = millis();

        while(W5100.readSnSR(i) != SnSR::CLOSED && millis() - start < 1000)
           delay(1);

        if(W5100.readSnSR(i) != SnSR::CLOSED) close(i); 
        connectStatus[i] = 0;
      }
    }

    Serial.println();

  }

  if(!listening) {
    Serial.println(F("Not listening"));

    for(int i = 0;i < MAX_SOCK_NUM; i++) {
      if(socketStat[i] == 0) {
        socket(i,SnMR::TCP,port,0);
        listen(i);
        break;      
      }  
    }    
  }
  serialCount = 0;
  serialBuffer[0] = 0;
}

Thanks for your help. Your comment of how to specify the socket that gets the message pointed me to the error in my serial Rx -> Ethernet Tx code.

After winnowing down your code into just the basics that I originally wanted, here's a working example of bidirectional communication that's based on your original code:

/*
 Serial to ethernet
 */

#include <SPI.h>
#include <Ethernet.h>
#include <utility/w5100.h>
#include <utility/socket.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xE1, 0xAB };
IPAddress ip(192, 168, 0, 200);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);

#define PORT 23

byte connectStatus;

EthernetServer server(PORT);

void setup() {
  // Disable SD card
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);
  // start listening for clients
  server.begin();
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
}

void loop() {
  char thisChar;
  uint8_t s = W5100.readSnSR(0);
  
  if((s == 0x17) && (connectStatus == 0)) {
    connectStatus = 1;
  }
    
  if(s == 0x1C) {
    close(0);
    connectStatus = 0;
  }

  if (Serial.available() > 0) {
    thisChar = Serial.read();
    if (connectStatus) {
      send(0, (unsigned char *)&thisChar, 1);
    }
  }

 unsigned int rxCount = W5100.getRXReceivedSize(0);        

  if (rxCount > 0) {
    recv(0, (unsigned char*)&thisChar, 1);
    Serial.print(thisChar);
  }
}

So, now my challenge is to compare this code to my original code (which didn't use the low-level W5100 or socket calls) and see what's different.

Thanks again for your help, SurferTim!

Ag

The server.available() call returns non-zero only when the client sends something. If the client just connects without sending something, your code won't know it.

Yes, that was the answer. I had my Ethernet transmit inside the if (client) {} statement.

Here is the high-level working Ethernet bidirectional communication code. It works for both Tx and Rx now.

/*
 Serial to ethernet
 */

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xE1, 0xAB };
IPAddress ip(192, 168, 0, 200);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);


// telnet defaults to port 23
EthernetServer server(23);

void setup() {
  // Disable SD card
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);
  // start listening for clients
  server.begin();
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
}

void loop() {
  EthernetClient client = server.available();

  if (client) {
      char thisChar = client.read();
      Serial.print(thisChar);
  }

  if (Serial.available() > 0) {
    char thisChar = Serial.read();
    server.print(thisChar);
  }
}

Thanks again for all of your help, SurferTim!

Ag

That code is ok, but you can't single out one particular client with your code. It sends anything to every connected client.

edit: Connect with more than one instance of telnet on your PC and you will see what I mean.