SOS status after using the Software Timer on Spark Core

Hey Guys
I got SOS status after I used the Software Timer in my code. I tried two Spark Cores with different firmware versions (0.4.9 , 0.5.0) without success. I'm flashed my code using the Web Build Particle. Anyone faced this issue before? Any suggestion please? @will @ScruffR @kennethlimcp

I found similar issue on Electron but not in Spark Core.

Here is my code

#include "freertos4core/freertos4core.h"
#include "Particle.h" //header for using of Particle.publish
char deviceEventData1[64]; //the buffer for sensor data Spark1
char deviceEventData2[64]; //the buffer for sensor data Spark2
char deviceEventData3[64]; //the buffer for sensor data Spark3
char deviceEventMillis1[64];
char deviceEventMillis2[64];
char deviceEventMillis3[64];
char string1[65] = "";
char string2[65] = "";
int idx1= 1; 
int idx2= 1; 
double pubTime1; // publish time from Spark1
double pubTime2; // publish time from Spark2
double pubTime3; // publish time from Spark3
char recdata1[64];
char recdata2[64];
char recdata3[64];
int numberOfDataSets=0;

Timer t1(1000, compareAndPublish, true); // need to use "true" here to make the timer one-shot

void setup()
{ 
  Serial.begin(9600); // define the serial port
  Particle.subscribe("Carpet1", myHandler1); //listen to Spark1 event
  Particle.subscribe("Carpet2", myHandler2); //listen to Spark2 event
  Particle.subscribe("Carpet3", myHandler2); //listen to Spark3 event
}  

void loop(){} 

void myHandler1(const char *event, const char *data) //handler function for Spark1
{
  strcpy(recdata1,data);
  strcpy(deviceEventMillis1, strtok(recdata1 , "|")); //seperate the millis from the sensor data (put the millis in deviceEventMillis1)
  strcpy(deviceEventData1, strtok(NULL, "|")); //seperate the millis from the sensor data (put the sensor data in deviceEventData1)
  pubTime1=strtod(deviceEventMillis1,NULL); 
  Particle.publish("pubTime1", String(pubTime1));
  checkDataSet();
}

void myHandler2(const char *event, const char *data) //handler function for Spark2
{ 
  strcpy(recdata2,data);
  strcpy(deviceEventMillis2, strtok(recdata2 , "|")); //seperate the millis from the sensor data (put the millis in deviceEventMillis1)
  strcpy(deviceEventData2, strtok(NULL, "|")); //seperate the millis from the sensor data (put the sensor data in deviceEventData1)
  pubTime2=strtod(deviceEventMillis2,NULL);   
  Particle.publish("pubTime2", String(pubTime2));
  checkDataSet();
}

void myHandler3(const char *event, const char *data) //handler function for Spark2
{ 
  strcpy(recdata3,data);
  strcpy(deviceEventMillis3, strtok(recdata3 , "|")); //seperate the millis from the sensor data (put the millis in deviceEventMillis1)
  strcpy(deviceEventData3, strtok(NULL, "|")); //seperate the millis from the sensor data (put the sensor data in deviceEventData1)
  pubTime3=strtod(deviceEventMillis3,NULL); 
  Particle.publish("pubTime3", String(pubTime3));
  checkDataSet();
}

void checkDataSet() {
    numberOfDataSets++;
    if (numberOfDataSets==2)
    {
        t1.start();
    }
}

void compareAndPublish() {
    
    if (deviceEventData1[0] != 0 && deviceEventData2[0] != 0 && deviceEventData3[0] != 0){
         
       if ((pubTime1 - pubTime2< 0) && (pubTime1 - pubTime3 < 0)){//check if Spark1 came before Spark2 and Spark3
            strcat(deviceEventData2, deviceEventData3); //concatenate Spark3 data to Spark2 data
            strcat(deviceEventData1, deviceEventData2); //concatenate Spark2 data to Spark1 data
            Particle.publish("Pad1,Pad2,Pad3",deviceEventData1); //publish the concatenated data Spark1Spark2Spark3
            numberOfDataSets=0;
        }
        else { //check if Spark3 came before Spark1 and Spark2
            strcat(deviceEventData2, deviceEventData1); //concatenate Spark1 data to Spark2 data
            strcat(deviceEventData3, deviceEventData2); //concatenate Spark2 data to Spark3 data
            Particle.publish("Pad3,Pad2,Pad1",deviceEventData3); //publish the concatenated data Spark3Spark2Spark1
            numberOfDataSets=0;
        } 
        // zero out the first value in the arrays if all had valid data, and we've published
        deviceEventData1[0] = 0;
        deviceEventData2[0] = 0;  
        deviceEventData3[0] = 0;
    }
    
    if (deviceEventData1[0] !=0  &&  deviceEventData2[0] != 0){ // Only publish if both data sets exist
        if (pubTime1 - pubTime2 < 0){ //check if Spark1 came before Spark2
            strcat(deviceEventData1, deviceEventData2); //concatenate Spark2 data to Spark1 data
            Particle.publish("Pad1,Pad2",deviceEventData1); //publish the concatenated data Spark1Spark2
            numberOfDataSets=0;
        }
        else { //check if Spark2 came before Spark1
            strcat(deviceEventData2, deviceEventData1);
            Particle.publish("Pad2,Pad1",deviceEventData2); //publish the concatenated data Spark2Spark1
            numberOfDataSets=0;
        }
        // zero out the first value in the arrays if all had valid data, and we've published
        deviceEventData1[0] = 0;
        deviceEventData2[0] = 0;    
    }   
        
    if  (deviceEventData2[0] != 0 && deviceEventData3[0] != 0){
          
        if (pubTime2 - pubTime3 < 0){ //check if Spark2 came before Spark3
            strcat(deviceEventData2, deviceEventData3);
            Particle.publish("Pad2,Pad3",deviceEventData1); //publish the concatenated data Spark2Spark3
            numberOfDataSets=0;
            }
        
        else { //check if Spark3 came before Spark2
            strcat(deviceEventData3, deviceEventData2); //concatenate Spark2 data to Spark3 data
            Particle.publish("Pad3,Pad2",deviceEventData3); //publish the concatenated data Spark3Spark2
            numberOfDataSets=0;
        }
        //zero out the first value in the arrays if all had valid data, and we've published
        deviceEventData2[0] = 0; 
	deviceEventData3[0] = 0;
   }
        
        
    if (deviceEventData1[0] != 0 && deviceEventData3[0] != 0){
         
        if (pubTime1 - pubTime3 < 0){ //check if Spark1 came before Spark3
            strcat(deviceEventData1, deviceEventData3); //concatenate Spark3 data to Spark1 data
            Particle.publish("Pad1,Pad3",deviceEventData1); //publish the concatenated data Spark1Spark3
            numberOfDataSets=0;
        }
        else { //check if Spark3 came before Spark1
            strcat(deviceEventData3, deviceEventData1); //concatenate Spark1 data to Spark3 data
            Particle.publish("Pad3,Pad1",deviceEventData3); //publish the concatenated data Spark3Spark1
            numberOfDataSets=0;
        } 
        // zero out the first value in the arrays if all had valid data, and we've published
        deviceEventData1[0] = 0; 
	deviceEventData3[0] = 0;  
    }
}

You should keep your timer callback a lot shorter and particularly not use Particle.publish()

BTW, if you used multi dimensional arrays instead if all your ....1, ....2 and ....3 variables you could make your code a lot simpler and better looking.
That would also allow to only use one subscription handler for all three events.

As said in another thread: C basics are crucial for a successful project
But this is not a C trainings camp. We like to help you, but we’d need some basis on your side to build upon.

2 Likes

Thanks for your reply @ScruffR. Firstly, my code was working normally without SOS before adding the Software Timer. Secondly, why I can’t use the Particle.publish()? Finally, I think that the complexity of the code doesn’t cause the SOS status, correct?
Thanks in advance.

@Ahmedsa1983, software timers are run in their own thread, calling the user-defined callback. The Particle.publish() function is run as part of the user thread and requires loop() to end or Particle.process() to be called. Thus, you should not be doing Particle.publish() in the timer callbacks. This explains why your code was running prior to adding the software timers.

I suggest you keep all but the actual Particle.publish() code in the callbacks to setup what needs to be published. Add a flag that loop() can pick up and do the publish itself. Since you call compareAndPublish() from each timer callback, instead call it in loop() when you a publish flag. :wink:

1 Like

@peekay123, @ScruffR, I tested this code on a Core, and I get the same SOS that @Ahmedsa1983 sees, and this happens right after startup. At this point only setup() has run, so the timer has not been started, nor have any of the subscription handlers been called. So, while I understand that you shouldn’t have long processes in a timer callback, I don’t think that explains what’s going on here.

1 Like

Does the timer start when you run the constructor above? If so, then it would be easy for there to be one second between the constructor running and setup() running since it can take a long time for the connection to be made on an Electron before setup is even run.

A better approach would be to construct the timer object as a global and then start it in setup when you know things are OK. This is the classic Arduino design pattern using obj.begin(...); in setup().

1 Like

According to the docs, a newly created timer is stopped, so this shouldn’t be the problem.

I just reflashed Tinker to my Core, then flashed the above code (with Build, so default 0.5.0 firmware), but with the line creating the timer, and the line starting it commented out, and I still get an SOS (hard fault). So, this appears to be a problem with the freertos4core library.

2 Likes

OK, I see now!

Not even the library timers sample does work anymore (not even with 0.4.9)
So that’s an issue for @mdma ( GitHub issue #990 )

1 Like