[SOLVED] Particle.process role and keepAlive when Electron system threading is enabled in automatic mode

UPDATE:

It would appear that the reason why my keepAlive pings weren't being sent out had to do with the way that I was calling Particle.keepAlive(). I was calling it from a startup macro, which apparently wasn't actually setting the keepAlive time. Moving the keepAlive() call to setup() resulted in my the pings being sent out correctly. So, in short, it turns out that my problem didn't have anything to do with Particle.process() calls.


Original post:

Me and @SCM are wondering what role Particle.process() plays when operating an Electron with the following configuration:

SYSTEM_THREAD(ENABLED) and SYSTEM_MODE(AUTOMATIC)

I am using a 3rd party SIM card and it would appear that the keepAlive ping is not happening. The symptom that leads me to believe this is the case is that my code attempts to publish an event to the cloud after running for about 15 minutes. About 75 seconds after my code calls Particle.publish(), the Electron starts rapid cyan blinking indicating it is re-establishing a connection to the cloud. If i then force the Electron to try and publish again once it has reconnected, it works.

I know that the keepAlive time for my carrier is 180 seconds, and have conservatively configured my code to use a keepAlive of 100 seconds as per:

void startupMacroCode(){
  cellular_credentials_set("znet", "", "", NULL);   //Zantel, Tanzania
  Particle.keepAlive(100); //Set the keepALive for pinging cell tower to maintain our IP address (test results show keepAlive of 117 seconds for Zantel)
}

STARTUP(startupMacroCode());

I know this keepAlive time is correct because I have tested it both through toggling LEDs periodically (increasing period) using a modified version of Tinker, and by running a test program I made that configures the keepalive time to be an hour, then publishes to the particle cloud with decreasing frequency until a publish doesn't work (i.e. waits 10 seconds then publishes, then waits 20 seconds then publishes, etc etc).

At the moment, I do not have any calls to Particle.process() in my code, because the write-ups in the reference documentation seem to indicate that I shouldn't need it...

from System Thread reference:

Particle.process() and delay() are not needed to keep the background tasks active - they run independently. These functions have a new role in keeping the application events serviced. Application events are:

  • cloud function calls
  • cloud events
  • system events

It would seem to me that the keepAlive doesn't fall into any of these categories, though I must admit that I don't quite understand what is meant by the statement "Application events are system events".

On the other hand the reference documentation says the following about Particle.process() :

Particle.process() checks the Wi-Fi module for incoming messages from the Cloud, and processes any messages that have come in. It also sends keep-alive pings to the Cloud, so if it's not called frequently, the connection to the Cloud may be lost.

So what's the verdict? What exactly is Particle.process() actually doing when system threading is enabled?

In multithreaded mode, the keepAlive pings are handled by the system as part of its regular housekeeping.

In general, if your application code blocks for a long time, it’s a good idea to call Particle.process() to keep the system functioning normally.

3 Likes

Okay, that's kinda what I thought. In other words, Particle.process() shouldn't need to get called in order for the keepAlive ping to happen when running in multi-threaded mode.

How would application code block? I thought the whole point of multi-threaded mode is that system code can't be blocked by application code and vice versa. For example, if my loop() code looked like this:

void loop(){
    while(1){};
}

...then wouldn't the keepAlive pings keep happening since FreeRTOS kernel is automatically switching back to the system thread every ms?

To re-quote your quote :wink:

Particle.process() and delay() are not needed to keep the background tasks active - they run independently. These functions have a new role in keeping the application events serviced. Application events are:

  • cloud function calls (to application defined functions)
  • cloud events (application subscriptions)
  • system events (application handlers for system events)

so if you care about any of these things then Particle.process() is needed so that these things can be handled on the application thread.

--> Had to do it for the love of meta (requoting a requote of a quote). :doge:

I wrote a test program to test whether or not Particle.process() needed to be called for the keepAlive() pings to keep getting sent in the event of an infinitely blocking application loop. It would appear that they do indeed keep getting sent out without calling Particle.process()

Here is the test code:

#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

int keepAliveTime = 0;

long publishTimerStart = 0;
long publishTimeout = 190000; //Publish every 190 seconds (10 seconds longer than known keepAlive time)

void startupMacroCode(){
  cellular_credentials_set("internet.com", "", "", NULL);  //Rogers Wireless, Canada
  keepAliveTime = 170; //Set the keepALive for pinging cell tower to maintain our IP address (test results show keepAlive of 180 seconds for Rogers)
}

STARTUP(startupMacroCode());

//==============================================================================
void setup(){

  Serial.begin(9600);
  Particle.keepAlive(keepAliveTime);  //Need to set keepAlive time in the loop (not in the startup macro)
  delay(10000); //Time to open the serial monitor
  publishTimerStart = millis();
  Serial.println("Exiting setup, going into infinite loop!");

}

int publishCount = 0;
const int publishBufferSize = 100;
char publishBuffer[publishBufferSize];

void loop(){

  //Infinite loop
  while(1){

    if( (millis() - publishTimerStart) > publishTimeout){

      publishCount++;

      publishBuffer[0]='\0';
      snprintf(publishBuffer, publishBufferSize, "Publish # %d", publishCount);

      Serial.printlnf("Now publishing to the Particle cloud... %s", publishBuffer);

      Particle.publish("testEvent", publishBuffer);

      publishTimerStart = millis();

    }

  };

}

Hi @jaza_tom

I think there are two parts here and I want to make sure you understand them both.

  1. The Electron keep-alives run in a system thread and do not require you to call any Particle function or method.
  2. Particle methods make sure the Particle.process() service keeps running.

So this means that your publish example works OK, since calling Particle.publish() will force the cloud to be serviced.

If on the other hand you defined a Particle.function() callback from the cloud and your loop code did your while(1){} as above with no Particle method calls, then the callback function will never get called since it needs to run in the user thread (not the system thread) but the user thread never yields to a cloud function. The important part is that code you write runs in the user thread.

Hope that makes sense.

2 Likes

Okay, that makes sense. Thanks for letting me know! So basically, if I had registered a cloud function and wanted to have a callback function invoked when that function got called from the cloud, then I would need to insert Particle.process() into my infinite loop.

This would have the effect of providing a kind of wormhole where I call Particle.process() from the application thread, which then starts executing the Particle.process() function (still on application thread), which then calls my callback function (still on application thread).

So in other words, any function contained in the application code (the code a user writes, e.g. a callback function) that gets called from the system code (the Particle firmware, e.g. calling a callback when a cloud event is received) must be called from the application thread, which implies that the application code needs to make a call to Particle.process() for it to work.

Hard to find non-confusing language to describe it, but I think I get it! :beers:

Just for curiosity, why do you actually need an infinite loop inside the infinite main loop?

loop() alone would do just the same thing - and do what an explicit call to Particle.process() would do too.

This was just an example program I wrote to see if the keepAlive ping could be affected by a long-blocking piece of application code. In practice, you are quite right that an infinite loop inside loop() would be about as useful as a left-handed hammer :ok_hand:

1 Like