TCPClient Panic when in SEMI_AUTOMATIC wireless mode

If you have chance to make it, I’d still be happy to look over a SSCCE for this - ideally there should not be any SOS’s for regular, well-behaved code.

I just double-checked the docs too. In semi-automatic mode, Spark.process() shouldn’t be needed unless your code blocks for more than 20 seconds, and even then it shouldn’t SOS!

No, I definitely don’t block for even close to that long, and the SOS was happening immediately at the first call to TCP read before any time passed. I need to get my codebase on github soon. Maybe I can try and do that sooner than later since I’m getting close to release. Would be great having some more eyes to audit all the coding I’ve done. :stuck_out_tongue:

Might take me a few weeks for an SSCCE sadly…a few more days before a few weeks out of town. Let’s not forget about this issue though, and if I can slap up my codebase more quickly, I’ll at least let you know in case you want to take a peak! At the risk of you cringing from a lot of my other code of course. :wink:

1 Like

Hey @choosatron,

I opened an issue for that here:

Thanks,
David

Awesome! Thanks David. :slight_smile:

Hi @choosatron! I’ve been working on this issue today along with another similar one, and trying to reproduce your failure case with the latest master… I’m working on a test app to reproduce your failure. I’ll report back when I get something going.

Ok here’s my test app… so far I haven’t had the need to run Spark.process() while starting in SEMI_AUTOMATIC mode. This TCP client code doesn’t always seem to want to grab data though, but I don’t see a hard fault when it doesn’t. When it powers up, type ‘W’ or ‘C’ to get a connection… then type ‘T’ to load the data. If the client doesn’t connect, press ‘t’ to stop the client and it will typically 100% work with ‘T’ the next time.

#include "application.h"

SYSTEM_MODE(SEMI_AUTOMATIC);

void tcp_connect();

TCPClient client;

void setup()
{
  // Make sure your Serial Terminal app is closed before powering your Core
  Serial.begin(9600);

  // Now open your Serial Terminal, and hit any key to continue!
  while(!Serial.available()) SPARK_WLAN_Loop();
}

void loop()
{
  if (Serial.available()) {
    int c = Serial.read();
    switch (c) {
      case 'C': Spark.connect(); break;
      case 'c': Spark.disconnect(); break;
      case 'W': WiFi.connect(); break;
      case 'w': WiFi.disconnect(); break;
      case 'T': tcp_connect(); break;
      case 't': client.stop(); break;
      case 'O': WiFi.on(); break;
      case 'o': WiFi.off(); break;
      case 'L': WiFi.listen(); break;
    }
  }

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

void tcp_connect() {
  Serial.println("connecting...");

  if (client.connect("www.textfiles.com", 80))
  {
    Serial.println("connected");
    client.println("GET /100/ad.txt HTTP/1.0");
    client.println("Host: www.textfiles.com");
    client.println("Content-Length: 0");
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
}
2 Likes

Thanks @BDub for this example.
Please I’d like to ask you if I can see the client’t input on the server side by using server.write(client.read()); or Serial.print(client.read());? I mean that I used your code in one of my core for the client side as shown below:

#include "application.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
void tcp_connect();
byte server[] = { 192, 168, 1, 104 }; // Server
TCPClient client;
void setup()
{
  // Make sure your Serial Terminal app is closed before powering your Core
  Serial.begin(9600);

  // Now open your Serial Terminal, and hit any key to continue!
  while(!Serial.available()) SPARK_WLAN_Loop();
}

void loop()
{
  if (Serial.available()) {
    int c = Serial.read();
    switch (c) {
      case 'C': Spark.connect(); break;
      case 'c': Spark.disconnect(); break;
      case 'W': WiFi.connect(); break;
      case 'w': WiFi.disconnect(); break;
      case 'T': tcp_connect(); break;
      case 't': client.stop(); break;
      case 'O': WiFi.on(); break;
      case 'o': WiFi.off(); break;
      case 'L': WiFi.listen(); break;
    }
  }

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

void tcp_connect() {
  Serial.println("connecting...");

  if (client.connect(server, 8080))
  {
    Serial.println("connected");
  }
  else
  {
    Serial.println("connection failed");
  }
}

and I used the TCPServer example in the docs on the second core for the server side as below:

TCPServer server = TCPServer(8080);
TCPClient client;

void setup()
{
  // start listening for clients
  server.begin();

  // Make sure your Serial Terminal app is closed before powering your device
  Serial.begin(9600);
  // Now open your Serial Terminal, and hit any key to continue!
  while(!Serial.available()) SPARK_WLAN_Loop();

  Serial.println(WiFi.localIP());
  Serial.println(WiFi.subnetMask());
  Serial.println(WiFi.gatewayIP());
  Serial.println(WiFi.SSID());
}

void loop()
{
  if (client.connected()) {
    // echo all available bytes back to the client
    while (client.available()) {
      server.write(client.read());
      Serial.print(client.read());
    }
  } else {
    // if no client is yet connected, check for a new connection
    client = server.available();
  }
}

Now, why I couldn’t see the char that I’m entering on the client side console (COM6) on the server side console (COM5)?

Thanks in advance.
@Dave @bko @mdma

After you connect to the server from your TCP Client side test app, you need to write the Serial USB characters to the TCP client: if(client.connected()) client.write(c);. You’ll have to figure out how to not collide with the other switch cases though… perhaps avoiding those altogether until you type a very special key like [`], or else it just echoes your keystrokes all to the client.

Serial.print(client.read()); looks correct in on the Server side.

1 Like

Thanks@BDub
I modified the Client\Server codes send the data from the client to the server to display it on the server side, but why I should press on the T char for two times to get the response on the Server serial console?
Here are my codes for Client, and Server:
For the Server

#include "application.h"
SYSTEM_MODE(SEMI_AUTOMATIC);

TCPServer server = TCPServer(9999);
TCPClient client;
void tcp_connect();
void setup()
{
  Serial.begin(9600);
  while(!Serial.available()) SPARK_WLAN_Loop();
  Serial.println("Please Enter Your Character Input:");
  }

void loop()
{
   if (Serial.available()) {
    int c = Serial.read();
    switch (c) {
      case 'W': 
      {
            WiFi.connect();
            delay(500);
            Serial.println("The server is connected to the router");
            server.begin();
            delay(500);
            Serial.println("The server is starting");  
            break;
            }
            case 'T': 
      {
            tcp_connect();
            break;
      }
    case 'C':
      {
       Spark.connect();   
       delay(500);
       Serial.println();
       Serial.println("The server is connected to the Spark Cloud");
       break;
        }
    }
  }
}

void tcp_connect() {
  if (client.connected()) 
  {
  while (client.available()) {
      char stringOne = client.read();
      Serial.println(stringOne);
    }
  }
  else
  {
      client = server.available();
  }
}

For the Client

#include "application.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
void tcp_connect();
byte server[] = { 192, 168, 1, 104 }; // Server
TCPClient client;
void setup()
{
  Serial.begin(9600);
  while(!Serial.available()) SPARK_WLAN_Loop();
  Serial.println("Please Enter Your Character Input:");
 }
void loop()
{
  if (Serial.available()) {
    int c = Serial.read();
      switch (c) {
            case 'W': 
      {
          WiFi.connect();
          delay(500);
          Serial.println("The client is connected to the router");
          break;
      }
      case 'T': tcp_connect(); break;
      case 'C':
      {
       Spark.connect();
       delay(500);
       Serial.println("The client is connected to the Spark Cloud");
       break;
       }
  }
  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }
}
}
void tcp_connect() {
  Serial.println("connecting...");
  if (client.connect(server, 9999))
  {
    Serial.println("connected to Server 192.168.1.104");
    Serial.println("The below data is sent from the client to the server");
    Serial.println("S");
    client.write("S");
    Serial.println("P");
    client.write("P");
    Serial.println("A");
    client.write("A");
    Serial.println("R");
    client.write("R");
    Serial.println("K");
    client.write("K");
     }
  else
  {
    Serial.println("connection failed");
  }
}

Any suggestion please? @Dave
Thanks in advance

Hi @Ahmedsa1983,

client.read() is destructive, it’ll pull the char off the buffer and return it, so doing it twice in a row won’t give you the outcome you want, try this instead:

char c = client.read();
server.write(c);
Serial.print(c);

Thanks!
David

2 Likes

Thanks for your reply @Dave
Actually, I’m using client.write(); to send the data from client to the server, and ai can see this data on the server side. I think that server.write(); will do the opposite scenario, am I correct? I mean I can use the server.write to send the data from the server to all the connected clients. My question is related with the server code that I posted in my previous post, which is why I should on T char for two times to be able of receiving the data from the client and display it on the server side on serial console (T char will execute the tcp_connect function)? Also my second question is that can send the timestamp with the client’s data to display it on the server?
Thanks a lot.

Hi @Ahmedsa1983,

Sorry if I’m not understanding your question. If you want the serial (on the server), and the client to receive the same data, you should only call client.read() once for each unique piece of data you want to pull off the buffer.

You can send whatever you like over the TCP / UDP sockets, but it’s up to you to decide how you want that data to be formatted. For example you could do something like:

2 bytes message length;message;timestamp;

// PSEUDOCODE warning!  I didn't test this to see if it would compile / work
// this is just an example :)  
int length = strlen(some_string) + 2 + strlen(some_time_string);
byte lengthBytes[2];
lengthBytes[0] = length & 255 ;
lengthBytes[1] = length << 8;

client.write(lengthBytes, 2);
client.write(some_string);
client.write(";")
client.write(some_time_string);
client.write(";")

This code is not tested, just pseudocode / not super efficient / just an example, etc. On the otherside, you could read the length prefix, read the remaining bytes into your buffer, and tokenize on the semicolon “;”, and parse appropriately, etc, etc. :slight_smile:

Thanks,
David

1 Like

Thanks @Dave
I’m sorry if I’m not able to make my question clear enough. What I need to do is sending data (Coming from sensors through Serial2 from the Server to client to display it on the Client’s serial. In addition, I wanted to embed the Timestamp with each frame of these data to display it on the receiver side (Either Client or Server). For the code that I posted before this post, I just sent data (SPARK) from the client to server, and I print the data on the client’s serial to show what this data looks like.
I appreciated your help.


Edit: Here is my code that I’m working on for the Server side:

// Thanks for @Scruff  and @BDub
#include "application.h"
#include "time.h"
#include "Serial2/Serial2.h"

SYSTEM_MODE(SEMI_AUTOMATIC);
void tcp_connect();
TCPServer server = TCPServer(9999);
TCPClient client;
const int wakePin = D0;  // use Serial2 RX pin as wake pin
char szReceive[64] = { '\0' };
int idx = 0;
uint32_t ms;
uint32_t msPublish;
uint32_t msLastSerial;
bool frameStart = false;

void setup()
{
  Serial.begin(9600);
  Serial2.begin(19200);
  while(!Serial.available()) SPARK_WLAN_Loop();
  msLastSerial = millis();
}

void loop()
{
    if (Serial.available()) {
    int d = Serial.read();
    switch (d) {
      case 'W': 
      {
            WiFi.connect();
            delay(500);
            break;
      }
      case 'C':
      {
           Spark.connect();   
            break;
       }
    case 'T': 
      {
            tcp_connect();
            break;
      }
    }
  }
    ms = millis();
    Serial2.flush();
    idx = 0;
    while (idx < 38 && millis() - ms < 1000 )
    {
if(Serial2.available())
        {
            msLastSerial = millis();
            char c = Serial2.read();
            Serial.write(c);
            if (c == 'S')
            { 
                frameStart = TRUE;
                idx = 0;
            }

            if (frameStart)
            {
                szReceive[idx++] = c;
                szReceive[idx] = '\0';
            }
        }
    }
    if (idx >= 38 && millis() - msPublish > 1000)
    {
        Serial.println(szReceive);
        if (Spark.connected())
        {
        Spark.publish("Carpet1",szReceive,PRIVATE);
        }
        msPublish = millis();
        idx = 0;
        frameStart = FALSE;
    }
    if (millis() - msLastSerial > 5000)   // stay awake 5 sec after last serial byte
    Spark.sleep(wakePin, RISING);
}

void tcp_connect() {
  if (client.connected()) 
  {
  while (client.available()) {
         server.write(szReceive);
    }
  }
  else
  {
      client = server.available();
  }}

For the client side:

// Thanks for @BDub
#include "application.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
void tcp_connect();
byte server[] = { 192, 168, 1, 104 }; // Server
TCPClient client;
void setup()
{
  Serial.begin(9600);
  while(!Serial.available()) SPARK_WLAN_Loop();
  Serial.println("Please Enter Your Character Input:");
 }
void loop()
{
  if (Serial.available()) {
    int c = Serial.read();
      switch (c) {
            case 'W': 
      {
          WiFi.connect();
          delay(500);
          Serial.println("The client is connected to the router");
          break;
      }
      case 'T': tcp_connect(); break;
      case 'C':
      {
       Spark.connect();
       delay(500);
       Serial.println("The client is connected to the Spark Cloud");
       break;
       }
  case 'X':
      {
            char x = client.read();
            Serial.print(x);
            break;
      }
  }
  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }
}
}
void tcp_connect() {
  Serial.println("connecting...");
  if (client.connect(server, 9999))
  {
    Serial.println("connected to Server 192.168.1.104");
    Serial.println("The below data is sent from the client to the server");
    char x = client.read();
            Serial.print(x);
     }
  else
  {
    Serial.println("connection failed");
  }
}

So, the szReceive is the incoming data (ASCII) from the serial2, and each frame (38 Byte in length) in this stream of data looks like this(SA00000000B00000000C00000000D00000000E). What I need is to send this stream of data to the client, and display it on the client’s serial display. The issue here that I can see the data on the Server Serial, but I couldn’t see it on the client serial display.
Thanks in advance
Ahmed

1 Like

Hi @Ahmedsa1983,

Ahh, that makes more sense, thanks for clarifying!

OK, so it sounds like the client app isn’t receiving / displaying data, the biggest issue that jumps out at me right away is that your if (client.available...) check is inside your Serial.available() check. This means it’ll only check the TCP socket for incoming data if there is also data on the serial buffer at the same time. Try moving that check to the top of your loop like this:


void loop()
{

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

  if (Serial.available()) {
    int c = Serial.read();
      switch (c) {
            case 'W': 
      {
          WiFi.connect();
          delay(500);
          Serial.println("The client is connected to the router");
          break;
      }
      case 'T': tcp_connect(); break;
      case 'C':
      {
       Spark.connect();
       delay(500);
       Serial.println("The client is connected to the Spark Cloud");
       break;
       }
  case 'X':
      {
            char x = client.read();
            Serial.print(x);
            break;
      }
  }   //end of switch
 }  //end of serial check
}

Thanks,
David

1 Like

@Dave
Actually, I changed the code to what you suggested me to do, but I still couldn’t receive any output on the client side. I can see an empty input ( ) when I press X on the client serial display. Any other suggestions please?
Thanks in advance.

Hi @Ahmedsa1983,

I’m guessing your firmware just needs some clarification on when the server is reading / writing, and when you’re reading / writing to serial. Try starting with a minimal working example, and going from there.

Good luck!

Thanks,
David

1 Like

Thanks for your advice @Dave


Edit: Actually I did what you told me to start with a simple code, and I do see @digixx code in this thread:


and it worked like a charm for me, so I could see the “Hello” output on the client’s serial output. Then I modified my server code to be like this:

// Thanks for @Scruff and @BDub
#include "application.h"
#include "Serial2/Serial2.h"

SYSTEM_MODE(SEMI_AUTOMATIC);
TCPServer server = TCPServer(9999);
TCPClient client;
const int wakePin = D0;  // use Serial2 RX pin as wake pin
char szReceive[64] = { '\0' };
int idx = 0;
uint32_t ms;
uint32_t msPublish;
uint32_t msLastSerial;
bool frameStart = false;

void setup()
{
  server.begin();
  Serial.begin(19200);
  Serial2.begin(19200);
  while(!Serial.available()) SPARK_WLAN_Loop();
  msLastSerial = millis();
}

void loop()
{
    int d = Serial.read();
    switch (d) {
      case 'W': 
      {
            WiFi.connect();
            delay(500);
            break;
      }
      case 'C':
      {
           Spark.connect();   
            break;
      }
    } // end of switch
    ms = millis();
    Serial2.flush();
    idx = 0;
    while (idx < 38 && millis() - ms < 1000 )
    {
if(Serial2.available())
        {
            msLastSerial = millis();
            char c = Serial2.read();
            Serial.write(c);
            if (c == 'S')
            { 
                frameStart = TRUE;
                idx = 0;
            } // end of if (c=='S')

            if (frameStart)
            {
                szReceive[idx++] = c;
                szReceive[idx] = '\0';
            } // end of if (frameStart)
        } // end of if(Serial2.available())
    } // end of while
    if (idx >= 38 && millis() - msPublish > 1000)
    {
        Serial.println(szReceive);
        if (client.connected()) 
   {
   server.write(szReceive);
       while (client.available()) 
         {
            Serial.print("a"); // ooh, the client has send some data
            server.write(client.read()); // just send back whats received
         }
    } //end of if (client.connected())
    else    
            {
        // if no client is yet connected, check for a new connection
            client = server.available();
            } //end of else
        if (Spark.connected())
        {
        Spark.publish("Carpet1",szReceive,PRIVATE);
        } // end of if (Spark)
        msPublish = millis();
        idx = 0;
        frameStart = FALSE;
    } // end of if (idx)
    if (millis() - msLastSerial > 5000)   // stay awake 5 sec after last serial byte
    Spark.sleep(wakePin, RISING);
} // end of void

And the client code to be like this:

// Thanks for  @BDub
#include "application.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
void tcp_connect();
byte server[] = { 192, 168, 1, 104 }; // Server
TCPClient client;
void setup()
{
  Serial.begin(19200);
  while(!Serial.available()) SPARK_WLAN_Loop();
  Serial.println("Please Enter Your Character Input:");
 }

void loop()
{
   if (client.connected())
  {
char x = client.read();
        Serial.print(x);
            }
  if (Serial.available()) {
    int c = Serial.read();
      switch (c) {
            case 'W': 
      {
          WiFi.connect();
          delay(500);
          Serial.println("The client is connected to the router");
          break;
      }
      case 'T': tcp_connect(); break;
      case 'C':
      {
       Spark.connect();
       delay(500);
       Serial.println("The client is connected to the Spark Cloud");
       break;
       }
  }   //end of switch
 }  //end of serial check

}
void tcp_connect() {
  Serial.println("connecting...");
  if (client.connect(server, 9999))
  {
    Serial.println("connected to Server 192.168.1.104");
    Serial.println("The below data is sent from the client to the server");
     }
  else
  {
    Serial.println("connection failed");
  }
}

For now, I can see the output on the Server serial side as show below:

but on the Client serial side it looked like this:

Please @Dave, @ScruffR, @BDub do you know why I’m seeing these spaces at the client serial side? Also, if I want to capture the output at the client serial, I’m seeing these weird characters with the actual data:

Does this weird character (or spaces) is because I’m doing Serial2.flush();? Or it is error in somewhere else in the firmware?
Thanks in advance.

Reread Dave’s code and look where you have not done what he suggested.

Once you lockated the error, think why this made the difference you saw (BTW: Screenshot 2 and 3 do tell you the same story).

1 Like

Aha, I think you mean that I should move the if (client.available()); outside the (serial.available());, since it will only check the TCP socket for the incoming data if there is also data on the serial buffer at the same time. That’s what you mean right? I’ll try to change that in the client’s code, and see if this will solve the issue. Thanks for your notice @ScruffR