Problems With Multiple Ethernet Clients

Hello all,

I have been troubleshooting an odd problem for the past couple days, and have found out what causes it but I am not sure why.

I have a Mega with Ethernet Shield attached. I connect all serial ports from the Mega to my computer, as well as the Ethernet connection from the shield to my computer.

I wrote a C# form app to test the connections. The purpose is to send commands/messages to the Arduino.

When I send out Ethernet 1 or 2, I write the message to the Arduino which will then write the message out Serial 1 or 2, respectively, and be read on my computer's Serial in the form app (and vice versa for Serial -> Ethernet). I did this to check if all ports were working properly.

When using the app, I would send out Eth1, receive in Serial1. I would send out Eth2, receive Serial2. I would send out Serial1, receive in Eth1. However, when I sent out Serial2, I would not receive in Eth2.

I'm going to bypass a lot of troubleshooting here. If needed, I can go into detail. Long story short, I determined my C# code was fine, and that the problem was in my Arduino. I also determined that the Arduino was receiving the serial data, but would lose the Tcp2 connection. There was no difference in code with what I was doing between Serial1 and Serial2, or Tcp1 and Tcp2 code in my Arduino. So what gives?

Well, I determined that the problem was the order in which I initiated the EthernetClient objects:

EthernetClient m_TcpClientCommands;
EthernetClient m_TcpClientSerial1;
EthernetClient m_TcpClientSerial2;

I will lose connection to whatever the last client is, in this case Client2. If I did the following:

EthernetClient m_TcpClientSerial2;
EthernetClient m_TcpClientSerial1;

I'd lose connection to Client1.

As a way around, I added TcpClientSerial3, even though it doesn't do anything else in my code.

EthernetClient m_TcpClientCommands;
EthernetClient m_TcpClientSerial1;
EthernetClient m_TcpClientSerial2;
EthernetClient m_TcpClientSerial3;

Everything now works fine.

Why?

Why?

Based on just three snippets? I'd guess that there is something wrong with the code you didn't post.

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask

EthernetServer m_ServerCommands(8400); //server port
EthernetServer m_ServerSerial1(8401);
EthernetServer m_ServerSerial2(8402);

EthernetClient m_TcpClientCommands;
EthernetClient m_TcpClientSerial1;
EthernetClient m_TcpClientSerial2;
EthernetClient m_TcpClientSerial3;


//Command Parsing Variables
bool m_bMessageStartFound;
String m_sCurrentCommand;

//Struct for holding different command types
typedef void(*functionType)(String);

//LED Position Variables
int LED_1 = 26;
int LED_2 = 27;
int LED_3 = 28;

struct CommandFunction
{
  String CommandString;
  functionType Function;
};
CommandFunction Commands[6];
/*
CommandFunction Commands[] = {
  {"ON", LED_On}
  , {"OFF", LED_Off}
  , {"Flash", LED_Flash}
  , {"Status", Status}
  , {"OPEN", SerialOpen}
  , {"CLOSE", SerialClose}
};
*/
//Variables used for flashing LED
unsigned long previousMillis = 0;
const long interval = 1000;
int ledState1 = LOW;
int ledState2 = LOW;
int ledState3 = LOW;
bool baFlashArray[3];

void setup()
{
  //init digital pins as an output
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(LED_3, OUTPUT);

  Commands[0] = {"ON", LED_On};
  Commands[1] = {"OFF", LED_Off};
  Commands[2] = {"Flash", LED_Flash};
  Commands[3] = {"GetStatus", Status};
  Commands[4] = {"OPEN", SerialOpen};
  Commands[5] = {"CLOSE", SerialClose};
  Commands[6] = {"Test", Test};

  Ethernet.begin(mac, ip, gateway, subnet);
  //m_ServerCommands.begin();
  m_ServerSerial1.begin();
  m_ServerSerial2.begin();

  //enable serial data print
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial.println("server test");
}

void loop()
{
  FlashingFunction(baFlashArray[0], baFlashArray[1], baFlashArray[2]);
  //TCPStatus("Start of Checks\t");
  if(!m_TcpClientCommands.connected())
  {
    m_TcpClientCommands = m_ServerCommands.available();
  }
  if(!m_TcpClientSerial1.connected())
  {
    m_TcpClientSerial1 = m_ServerSerial1.available();
  }
  if(!m_TcpClientSerial2.connected())
  {
    m_TcpClientSerial2 = m_ServerSerial2.available();
  }
  //TCPStatus("End of Checks \t");

  CheckTcpClientCommand();
  CheckTcpClientSerials();
  CheckSerials();
  //Keeps IP address for DHCP
  Ethernet.maintain();
}

void TCPStatus(String message)
{
  /*
  Serial.print("Function: ");
  Serial.print(message);
  Serial.print("TCP Status ");
  Serial.print(m_TcpClientCommands.connected());
  Serial.print(m_TcpClientSerial1.connected());
  Serial.print(m_TcpClientSerial2.connected());
  Serial.println();
  */
}

void CheckTcpClientCommand()
{
  TCPStatus("TCPCommandClient \t");
  if (m_TcpClientCommands.connected() && m_TcpClientCommands.available())
  {
    while (m_TcpClientCommands.available())
    {
      char temp = (char)m_TcpClientCommands.read();
      //Check for Commands Here
      CheckForCommand(temp);
    }
  }
  
}

void CheckTcpClientSerials()
{
  TCPStatus("TCPClientSerials \t");
  //Serial1
  if (m_TcpClientSerial1.available())
  {
    Serial.println("TcpClient1 Data Available");
    while (m_TcpClientSerial1.available())
    {
      Serial1.write((byte)m_TcpClientSerial1.read());
    }
  }
  //Serial2
  if (m_TcpClientSerial2.available())
  {
    Serial.println("TcpClient2 Data Available");
    while (m_TcpClientSerial2.available())
    {
      Serial2.write((byte)m_TcpClientSerial2.read());
    }
  }
}

void CheckSerials()
{
  TCPStatus("Serials \t\t");
  while (Serial1.available())
  {
    byte Serial1Byte = Serial1.read();
    Serial.println("Serial1 Data Available");
    if(m_TcpClientSerial1.connected())
    {
      m_TcpClientSerial1.write(Serial1Byte);

    }
  }
  while (Serial2.available())
  {
    byte Serial2Byte = Serial2.read();
    Serial.println("Serial2 Data Available");
    if(m_TcpClientSerial2.connected())
    {
      Serial.println("Serial2 TCP Send Byte");
      m_TcpClientSerial2.write(Serial2Byte);
    }
  }
}

void CheckForCommand(char inputChar)
{
  bool bFoundCommand = false;
  switch (inputChar)
  {
    case '<':
      m_bMessageStartFound = true;
      m_sCurrentCommand = "";
      break;

    case '>':
      if (m_bMessageStartFound == true)
      {
        ProcessCommand(m_sCurrentCommand);
      }
      m_bMessageStartFound = false;
      m_sCurrentCommand = "";
      break;

    default:
      if (m_bMessageStartFound == true)
      {
        // add inputChar to m_sCurrentCommand
        m_sCurrentCommand += inputChar;
      }
      break;
  }
}

void ProcessCommand(String userInput)
{
  int firstComma = userInput.indexOf(',');
  String sCommandString = userInput.substring(0, firstComma);
  userInput.remove(0, firstComma + 1);
  bool bCommandFound = false;
  for (int i = 0; i < sizeof(Commands) / sizeof(CommandFunction); i++)
  {
    if (sCommandString == Commands[i].CommandString)
    {
      bCommandFound = true;
      Serial.print("Function: Process Command(");
      Serial.print(sCommandString);
      Serial.println(")");
      Commands[i].Function(userInput);
    }
  }
  if (bCommandFound == false)
  {
    Serial.println("That is an invalid command");
  }
}

void Status(String Variables)
{
  Serial.println("Status Message Sent");
    //TODO update this!
    //m_TcpClientCommands.println("Status");
}

void LED_On(String LED)
{
  switch (LED.toInt()) {
    case 1:
      {
        baFlashArray[0] = false;
        digitalWrite(LED_1, HIGH);
        Serial.println("LED_1 ON");
      }
      break;
    case 2:
      {
        baFlashArray[1] = false;
        digitalWrite(LED_2, HIGH);
        Serial.println("LED_2 ON");
      }
      break;
    case 3:
      {
        baFlashArray[2] = false;
        digitalWrite(LED_3, HIGH);
        Serial.println("LED_3 ON");
      }
      break;
  }
}

void LED_Off(String LED)
{
  switch (LED.toInt()) {
    case 1:
      {
        baFlashArray[0] = false;
        digitalWrite(LED_1, LOW);
        Serial.println("LED_1 OFF");
      }
      break;
    case 2:
      {
        baFlashArray[1] = false;
        digitalWrite(LED_2, LOW);
        Serial.println("LED_2 OFF");
      }
      break;
    case 3:
      {
        baFlashArray[2] = false;
        digitalWrite(LED_3, LOW);
        Serial.println("LED_3 OFF");
      }
      break;
  }
}

void LED_Flash(String LED)
{
  switch (LED.toInt()) {
    case 1:
      {
        baFlashArray[0] = true;
        Serial.println("LED_1 FLASH");
      }
      break;
    case 2:
      {
        baFlashArray[1] = true;
        Serial.println("LED_2 FLASH");
      }
      break;
    case 3:
      {
        baFlashArray[2] = true;
        Serial.println("LED_3 FLASH");
      }
      break;
  }
}
//Struct for holding different command types
typedef void(*functionType)(String);

Does that look ANYTHING like a struct to you?

  Commands[0] = {"ON", LED_On};
  Commands[1] = {"OFF", LED_Off};
  Commands[2] = {"Flash", LED_Flash};
  Commands[3] = {"GetStatus", Status};
  Commands[4] = {"OPEN", SerialOpen};
  Commands[5] = {"CLOSE", SerialClose};
  Commands[6] = {"Test", Test};

Does this actually work? I KNOW that writing 7 elements in a 6 element array does NOT.

void SerialOpen(String Message)
{
  int firstComma = Message.indexOf(',');
  String sPort = Message.substring(0, firstComma);
  String sBaud;
  int nBaud;
  if (sPort != "1" && sPort != "2")
  {
    Serial.print("Invalid Serial Command");
  }
  else
  {
    Message.remove(0, firstComma + 1);
    sBaud = Message;
    nBaud = sBaud.toInt();
    sPort == "1" ? Serial1.begin(nBaud) : Serial2.begin(nBaud); 
    sPort == "1" ? Serial.println("sPort = 1, sBaud = " + sBaud) : Serial.println("sPort = 2, sBaud = " + sBaud);
  }
}

void SerialClose(String Message)
{
  int firstComma = Message.indexOf(',');
  String sPort = Message.substring(0, firstComma);
  String sBaud;
  int nBaud;
  if (sPort != "1" && sPort != "2")
  {
    Serial.print("Invalid Serial Command");
  }
  else
  {
    sPort == "1" ? Serial1.end() : Serial2.end();
    sPort == "1" ? Serial.println("sPort = 1 closed") : Serial.println("sPort = 2 closed");
  }
}


void FlashingFunction(bool LED_Flash1, bool LED_Flash2, bool LED_Flash3)
{
  unsigned long currentMillis = millis();
  for (int i = 1 ; i <= 3 ; i++)
  {
    if (currentMillis - previousMillis >= interval)
    {
      // save the last time you blinked the LED
      previousMillis = currentMillis;
      if (LED_Flash1 == true)
      {
        ledState1 == LOW ? ledState1 = HIGH : ledState1 = LOW;
        digitalWrite(LED_1, ledState1);
      }
      if (LED_Flash2 == true)
      {
        ledState2 == LOW ? ledState2 = HIGH : ledState2 = LOW;
        digitalWrite(LED_2, ledState2);
      }
      if (LED_Flash3 == true)
      {
        ledState3 == LOW ? ledState3 = HIGH : ledState3 = LOW;
        digitalWrite(LED_3, ledState3);
      }
    }
  }
}

void Test(String s_test)
{
  Serial1.print(s_test);
}

Paul-

Thanks for the reply. No, it doesn't look like a struct. It is a defined type that goes into a struct, and my comment got separated by the LED's. This should be more readable:

//Struct for holding different command types
typedef void(*functionType)(String);
struct CommandFunction
{
  String CommandString;
  functionType Function;
};

Also, the oversized Commans struct does compile actually, I'm pretty surprised. The test command was used for a test, I don't use it anymore and should remove.

Paul-

I corrected the size of the array and everything works as intended. I feel like an IDIOT but at least I'm out of my MISERY.

Thanks

I feel like an IDIOT

Don't. There are certain problems that occur often enough that the first thing I look for is array bounds issues. You are less likely to spot them because you don't look at dozens of programs a day.

I'm glad you are working now.