I switched from AUTOMATIC wireless mode to SEMI_AUTOMATIC. Basically on startup I check for a certain key press, and if it is present, I DON’T setup wireless, otherwise I do. I can see that it starts up, connects to WiFi correctly, and my state machine goes into its waiting state for an action to occur.
At this point, I call the core via a spark cloud function already setup, I send over TCP connection info, an IP and port, and on next loop the core attempts to make a TCP Client connection to my server. It does so successfully, sees data on the pipe to read, and goes into a HARD FAULT.
If I change back to AUTOMATIC with NO other changes, there are no problems. The fact that it is able to connect without issue but dies on a read confuses me. Any thoughts? Am I failing to understand SEMI_AUTOMATIC and failing to do something required? I will reread the posts on that just in case, but I’d love some help.
Sorry to hear of these problems. If there’s any way you can prune the code down to the smallest test case that I or someone else could run and debug that would be a great help!
Ah! It appears you do have to call Spark.process(); in SEMI_AUTOMATIC mode. Maybe I’m crazy, I thought I had read the docs closely to verify that very thing…in any case, with process, things go back to fully functional! Yay!
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.
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.
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");
}
}
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.
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");
}
}
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 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.
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.
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
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
}
@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.
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.