Declaring Particle.function on class method

I have been running many versions (feature enhancements) over the past several years. The code has been developed using Particle Dev. Suddenly something has changed, and when flashed I get red SOS 1. This ‘change’ appears to be something in the cloud as I have good backup copies of project and previous versions. The development system is Windows 10 and Particle Dev, not upgraded since everything worked. I have isolated the problem to the global declaration of the Notifier class, and according to Particle docs (see below) this is not allowed (although it was working for several years).
There are several things I don’t understand about this:

  1. If I have local instances of Notifier, i.e. one in setup() and one in loop(), all works well, but I don’t understand how the particle.function knows which instance to call. Seems to me a global instance, which crashes SOS 1, would make more sense since there is only one to call.
  2. Is this a known change, i.e. to enforce this global usage, which had been working.
  3. And now my c++ ignorance shows, how can I access this Notifier object from other classes.
    A seeming ‘fluke’ of Particle Dev allowed me to access the global notify within other classes.
    My entire Alarm system project can be seen at https://github.com/dont45/photonMasterRemote
  4. Do I understand the Docs correctly, i.e. that I can not create a global instance of this class.
    Testing indicates that this is correct, i.e. local class instance in setup() and loop() work correctly, whereas global declaration results in SOS 1.
    Any insight about this is greatly appreciated. Currently my alarm of 2+ years won’t run! Help!
    Thanks

My class Notifier where I expose three of it’s methods to the cloud (as particle functions)

class Notifier {
 public:
     Notifier(char *lopri_event_name, char *event_name, char *priority_event_name, char *emergency_event_name, Sensor *s) {
  
     Particle.function("upd", &Notifier::upd, this);
     Particle.function("request", &Notifier::request, this);
     Particle.function("confirm", &Notifier::confirm, this);
  
     strcpy(low_pri_webhook_id, lopri_event_name);
  }

From the Particle Docs on functions:

// EXAMPLE USAGE WITH C++ OBJECT

class CoffeeMaker {
  public:
    CoffeeMaker() {
    }

    void setup() {
      // You should not call Particle.function from the constructor 
      // of an object that will be declared as a global variable.
      Particle.function("brew", &CoffeeMaker::brew, this);
    }

    int brew(String command) {
      // do stuff
      return 1;
    }
};

CoffeeMaker myCoffeeMaker;

void setup() {
    myCoffeeMaker.setup();
}

This is most likely due to the non-deterministinc order of execution of constructors spread over different modules.
Since the Particle object isn't fully instantiated by the time your constructor demands access you see the SOS panic.
The common scheme is to not use any such calls in the constructor but place them in a class::setup() or class::begin() method which is then called from a function like setup() which will make sure all objects have been already fully instantiated.
That's also what the docs samples illustrate.
And that enables you to define an object globally and still not run into the race condition.

You are passing the instance pointer as last parameter (this) and hence the system can call your function as yourInstance->udp() (where yourInstance holds the pointer that was provided as this.

Nope, when you do as outlined in the doc's example and explained above you absolutely can have global instances.

4 Likes

Thank You! I moved the Particle.functions to a setup() method, called notify.setup() in setup, and EVERYTHING works again… my alarm is running! And I think I understand the problem…just took a long time to figure out where in my code the SOS was coming from, and you to give me a solution.

1 Like