Subscribe to two Spark Core boards and decided which one published data first

Hey Folks
I have two Spark Core boards publishing data on the cloud and I’m using the third Spark to subscribe to them. I would like to know how can I decide which one of the two Spark Core publish data first? Depending on that, I will concatenate the data of the first Spark first followed by the second Spark. I started my code for the subscriber and I want to see your suggestions please.

#include "Particle.h"
char Text1[64]; //Buffer for the data of Spark 1
char Text2[64]; //Buffer for the data of Spark 2
uint32_t ms1; //Time storage for Spark 1 publishing 
uint32_t ms2; //Time storage for Spark 2 publishing

void myHandler1(const char *event, const char *data) //Handler for Spark1
{
  strcpy(Text1, data); //Put the Spark1 data inside a global buffer so we can use it anywhere in the code
  Particle.publish("Spark1",Text1); // Make sure that Spark3 can receive data from Spark1 correctly
  ms1 = millis(); //put the time of the initial publishing of Spark1 in ms1
}

void myHandler2(const char *event, const char *data) //Handler for Spark2
{
  strcpy(Text2, data); //Put the Spark2 data inside a global buffer so we can use it anywhere in the code
  Particle.publish("Spark2",Text2); // Make sure that Spark3 can receive data from Spark2 correctly	
  ms2 = millis(); //put the time of the initial publishing of Spark2 in ms2
}

void setup()
{
  Serial.begin(4800); //start the serial port
  Particle.subscribe("Spark1", myHandler1); //subscribe for Spark1
  Particle.subscribe("Spark2", myHandler2); //subscribe for Spark2
if (Text1!=0 && Text2!=0) // make sure that both of the sparks have data
	{
    if (ms1-ms2 > 0) //Check if Spark1 publish the data first
{
snprintf(Text1,sizeof(Text1),Text2,sizeof(Text2)); //concatenate data of Spark1 to data of Spark2 in a form of (data1data2) and place the result in Text1
//strcat(Text1, Text2); //another way to concatenate the data
//sprintf(Text1, "Pad's 1 data is %d, Pad's 2 data is %d, Pad's 3 data is %d", Text1,Text2); //another way to concatenate the data
Particle.publish("Spark1,Spark2",Text1); // publish the concatenated data to the cloud   
Particle.publish("The sequence of data","Spark 1 publish before Spark2");
					}
            else        // Spark2 publish data before Spark1
snprintf(Text2,sizeof(Text2),Text1,sizeof(Text1)); //concatenate data of Spark2 to data of Spark1 in a form of (data2data1) and place the result in Text2
Particle.publish("Spark2,Spark1",Text2); // publish the concatenated data to the cloud   
Particle.publish("The sequence of data","Spark 2 publish before Spark1");	
}
}
void loop(){}

Can I use the millis function two times to decide which Spark publish first? Or is there any other way to do it?
@bko @Moors7 @ScruffR
Thanks in advance.
Ahmed.

I would assume that due to network latency, there is no way to know. If you want to make sure you have them in the right order, I suggest adding the event time to the data the devices publish and then using that.

1 Like

Thank for your reply @justinmy , but how can I add the event time to the data the devices publish?

what data are you publishing from your other devices?

1 Like

I’m publishing a sensor serial data in Ascii format, which is something like SAXE (the X might change between 0-F Hex).

so the format is up to but you could use JSON:

{
“published”: “[epoch time with milliseconds]”,
“data”: “SAXE”
}

Then compare the published times and merge the data.

1 Like

Thanks for your suggestion but I didn’t deal with Json before. Where I should put this line inside my code? Do I need this line inside each handler?
Thanks in advance.

instead of json, you could even go simpler:

String data = "SAXE"; // however you get your data
unsigned long now = millis();
String publishData = String(now) + "|" + data; // e.g. 692865|SAXE
Particle.publish("EVENT_NAME", publishData);

And then on the receiving side:

int separator = receivedData.indexOf("|");
unsigned long deviceEventMillis = receivedData.substring(0, separator).toInt();
String deviceEventData = recievedData.substring(separator + 1);

Couple things to note.

  1. Here’s a post on getting time with milliseconds, which currently you can’t.

  2. I am making the assumption that your two events could happen within the same second and that you need the accuracy. So the other thing I would do is that when the device start, they would publish their start time and millis so you can do the math to get the actual time.

1 Like

Thanks again @justinmy

For the first code that you posted, I understood that I need to add it for each publisher Spark (Spark1 and Spark2) to count their publish time and decide which one was activated first.

For the receiver side, I couldn’t understand how I will extract the time from the publishdata and compare between the two Sparks time?
Other question that I have are:

1- Is the receivedData is the publishData from the sender?

2- Do I need to compare between the deviceEventMillis1 (published time for Spark1) and deviceEventMillis2 (published time for Spark2)?

3- Is it easier to use different line to publish the Spark publish time in the sender side, compare between the two publish time for the two Sparks in the receiver side, and concatenate the two Sparks data then send it again to the cloud?

Thanks for your kindly help.
Ahmed.

  1. Yes
  2. Yes

Here is a general flow:

  1. Device connected to sensor starts up
  2. Device sends Startup time and mills
  3. Monitor Device receives startup event
  4. Device sends sensor data and mills
  5. Monitor Device receives sensor event
    a. subtracts startup_mills from the sensor_event_mills
    b. converts above value to decimal seconds and adds it to startup_time for true event_time
  6. If second event is sent in close proximity, the two event_times are compared
1 Like

Instead, you could do something like this:

int start_epoch = 0;
unsigned long start_millis = 0;

void setup()
{
	start_epoch = Time.now();
	start_millis = millis();
}

void sensorData(const char* data)
{
        double seconds = double(millis() - start_millis) / 1000;
        double epoch = start_epoch + seconds;
        Particle.publish("EVENT_NAME", String::format("%.4f|%s", epoch, data));
}
1 Like

@justinmy
Do you mean that I should add this code to the sender code? What about the receiver?

Hello @justinmy
For now, I'm sending this event value from the sender:

Do I need to change in the code of the receiving device to start comparing between the time for the two Sparks?

Thanks

I tried to add your suggestion’s code to the sender and receiver but I got the following errors:

error: request for member 'indexOf' in 'Text1', which is of non-class type 'char [64]'

error: request for member 'substring' in 'Text1', which is of non-class type 'char [64]'

You’re getting these errors because you’re mixing String code (from justinmy’s code suggestions) with c-string code (i.e. the char arrays Text1 and Text2). You need to use one or the other. I would suggest that since the data passed in is a char*, that you stick with c-strings, and use the strtok function to separate the two parts of your data.

1 Like

Thanks @Ric
Your explanation is correct. my code for the receiver looks like this:

 #include "Particle.h"
char Text1[64];
char Text2[64];
int separator1;
int separator2;
unsigned long deviceEventMillis1;
unsigned long deviceEventMillis2;
char deviceEventData1[64];
char deviceEventData2[64];

void myHandler3(const char *event, const char *data)
{
  strcpy(deviceEventData1, data);
  separator1 = data.indexOf("|");
  deviceEventMillis1 = data.substring(0, separator1).toInt();
  deviceEventData1 = data.substring(separator1 + 1);
  Particle.publish("Pad3",deviceEventData1);
}

void myHandler4(const char *event, const char *data)
{
  strcpy(deviceEventData2, data);
  separator2 = deviceEventData2.indexOf("|");
  deviceEventMillis2 = data.substring(0, separator2).toInt();
  deviceEventData2 = data.substring(separator2 + 1);
  Particle.publish("Pad4",deviceEventData2);
}

void setup()
{
  Serial.begin(4800); 
  Particle.subscribe("Carpet3", myHandler3);
  Particle.subscribe("Carpet4", myHandler4);
if  (deviceEventMillis1-deviceEventMillis2>0){
  strcat(deviceEventData1, deviceEventData2);
  Particle.publish("Pad3,Pad4",deviceEventData1);
}
else if  (deviceEventMillis2-deviceEventMillis1>0){
  strcat(deviceEventData2, deviceEventData1);
  Particle.publish("Pad4,Pad3",deviceEventData2);
}
}

void loop(){}

For the sender side, I’m sending this data (as @justinmy suggested):

Yes, his explanation is correct, but you code still isn't.
How do you intend to incorporate @Ric's advice?

1 Like

Hello @ScruffR
I modified my code depending on his advice as shown below:

#include "Particle.h"
unsigned long deviceEventMillis1;
unsigned long deviceEventMillis2;
char deviceEventData1[64];
char deviceEventData2[64];
void myHandler3(const char *event, const char *data)
{
  strcpy(deviceEventData1, strtok(data , "|"));
  strcpy(deviceEventMillis1, strtok(NULL, "|"));
  Particle.publish("Pad3",deviceEventData1);
}

void myHandler4(const char *event, const char *data)
{
  strcpy(deviceEventData2, strtok(data , "|"));
  strcpy(deviceEventMillis2, strtok(NULL, "|"));
  Particle.publish("Pad4",deviceEventData2);
}

void setup()
{
  Serial.begin(4800); 
  Particle.subscribe("Carpet3", myHandler3);
  Particle.subscribe("Carpet4", myHandler4);
if  (deviceEventMillis1-deviceEventMillis2>0){
  strcat(deviceEventData1, deviceEventData2);
  Particle.publish("Pad3,Pad4",deviceEventData1);
}
else if  (deviceEventMillis2-deviceEventMillis1>0){
  strcat(deviceEventData2, deviceEventData1);
  Particle.publish("Pad4,Pad3",deviceEventData2);
}
}

void loop(){}

but I still have these errors:

error: invalid conversion from 'const char*' to 'char*' [-fpermissive]

error: initializing argument 1 of 'char* strtok(char*, const char*)' [-fpermissive]
 char  *_EXFUN(strtok,(char *__restrict, const char *__restrict));

If you investigate how strtok() works - or just look at the error message - you might see why it doesn’t build.

http://www.cplusplus.com/reference/cstring/strtok/

1 Like

@ScruffR I did that and I looked in google for how the strtok function works, but I couldn’t get rid of the errors. I tried to change the type of the deviceEventData1[64]; to be:

String deviceEventData1[64];
char deviceEventData1[64];
or char *deviceEventData1[64];

but I still couldn’t get any success.