Particle.subscribe not working when SYSTEM_THREAD(ENABLED);

Hi,

I have added SYSTEM_THREAD(ENABLED); to my code and now Particle.subscribe does not work anymore.
Is this a bug or did I understand something wrong? My code was working before for several days without any problems.

Kind regards,
Tom

You’d need to show your code, since Particle.subscribe() does not act any different with or without SYSTEM_THREAD(ENABLED), but things like Particle.process() and delay() do (at least pre 0.4.9).
Which firmware version are you targeting?

Hi @ScruffR,

I use firmware version 0.4.9 .

SYSTEM_THREAD(ENABLED);

// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_BME280/Adafruit_BME280.h"

// This #include statement was automatically added by the Particle IDE.
#include "PowerShield/PowerShield.h"

#define BME_SCK D4
#define BME_MISO D3
#define BME_MOSI D2
#define BME_CS D5

#define SEALEVELPRESSURE_HPA (1013.25)


unsigned long lastPostTime = millis();

const unsigned long sleep = 300; //sleep for x seconds

const uint16_t pin = D2;

volatile byte state = 0;

Adafruit_BME280 bme; // I2C

PowerShield batteryMonitor;

void intHandler() {
  state = !state;
}

bool canSleep = false; // set to true when the Photon can sleep
void subscribeHandler(const char* event, const char* data) {
    // data set to the device ID that wants to sleep.
    String id = Particle.deviceID();
    Serial.println("I am here");
    // see if this was a notification from this core that we can sleep
    if (!strcmp(data, id.c_str()))  
        canSleep = true;
        Serial.println("canSleep is true");
}

void setup() {
    
    Serial.begin(9600); 
    
    Serial.println(F("BME280 test"));
    
    if (!bme.begin()) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
    }

    batteryMonitor.begin(); 
    // This sets up the fuel gauge
    batteryMonitor.quickStart();
    // Wait for it to settle down
    delay(5000);

    pinMode(pin, INPUT);
    attachInterrupt(pin, intHandler, CHANGE); //attach the interrupt function "intHandler" to "pin" whenever the state changes
    
    Particle.subscribe("sleep", subscribeHandler, MY_DEVICES);  // listen for sleep events

}

void loop() {
    
    Serial.print("Temperature = ");
    float temp = bme.readTemperature();
    Serial.print(temp);
    Serial.println(" *C");

    Serial.print("Pressure = ");
    float pressure = bme.readPressure() / 100.0F;
    Serial.print(pressure);
    Serial.println(" hPa");

    Serial.print("Humidity = ");
    float hum = bme.readHumidity();
    Serial.print(hum);
    Serial.println(" %");
    
    // Read the volatge of the LiPo
    float cellVoltage = batteryMonitor.getVCell();
    // Read the State of Charge of the LiPo
    float stateOfCharge = batteryMonitor.getSoC();
    
    // Send the Voltage and SoC readings over serial
    Serial.println(cellVoltage);
    Serial.println(stateOfCharge);

    // wait for the cloud connection to be connected or timeout after 10 seconds
    if (waitFor(Particle.connected, 10000)) {
        bool sent = Particle.publish("monitor", String::format("[{\"t\":%.2f,\"p\":%.2f,\"h\":%.2f,\"v\":%.2f,\"c\":%.0f}, {\"Name\":\"test\",\"Location\":\"garden\"}]", temp, pressure, hum, cellVoltage, stateOfCharge), 60, PRIVATE); 
        Serial.println(sent);
        
        // publish the sleep event last
        String deviceID = Particle.deviceID();
        Particle.publish("sleep", deviceID.c_str(), 60, PRIVATE);
    
        while (!canSleep) {
            delay(100);   // waiting for the event - delay runs the system idle code
        }
        Serial.println("sleeping");
        canSleep = false;
        System.sleep(pin, RISING, sleep);
    }

It seems that my Photon stays in the while loop at the end of my code when SYSTEM_THREAD(ENABLED); So it never visits my subscribeHandler.

The behaviour you describe was the reason for this issue I opened a while back and 0.4.9 was said to correct this.
https://github.com/spark/firmware/issues/659

Particle.subscribe() handlers and Particle.functions() are user code and hence have to be run by the application thread, but Particle.process() and hence delay() with it was reduced to a mere stub whith SYSTEM_THREAD(ENABLED), but this should now work.

Having said this try adding Particle.process() inside your while loop.

But I’ve also noticed this in your code

void  subscribeHandler(const char* event, const char* data) {
    ...
    if (!strcmp(data, id.c_str()))  
        canSleep = true;
        Serial.println("canSleep is true");
}

Judging by the indentation I’d assume that you expect the message only to be printed if the condition is true, but that’s not the case since you are missing curly braces.
So maybe your canSleep never really becomes true.

BTW: What are you using to trigger your interrupt? If you are using a button, have you added an external pull resistor to avoid your pin to be floating?

1 Like

I had a similar problem (0.4.7 though) and solved it by subscribing as Early in Setup as possible. Otherwise it didn’t Work reliable if the subscibe was as Late in Setup as its the case in your setup when System_thread was enabled. That might have been Fixed already though

Thank you for you fast reply!
I have added Particle.process() inside my while loop and added the missing curly braces.
My code continued to stay in the while loop.

I would like to trigger my interrupt with an infrared sensor but until now I am only using wakeup after specified seconds. I would like to use threading because it could happen that the following:
Device wakes up after specified seconds and publishes the measured values. During that time my infrared sensor gets triggered. As I count the time how long it is triggerd I fear that without threading it could happen that I lose a few seconds.

After I moved Particle.subscribe at the beginning of my setup it is breathing white. Befor it was breathing cyan. As I found out this means that the wifi module is off. Every 10 seconds I see my values printed in the serial console. So this means that

if (waitFor(Particle.connected, 10000))

times out.

Is there any solution for this?

Ah, sorry - didn't get the notification since you didn't reply to me nor pinged me.

You say the code is trapped in

        while (!canSleep) 
        {
           Particle.process();   // waiting for the event - delay runs the system idle code
        }

But you do get the "canSleep is true" message from this bit of code

    if (!strcmp(data, id.c_str())) 
    {
        canSleep = true;
        Serial.println("canSleep is true");
    }

That's odd then. An option would be to use volatile bool canSleep;, just to make sure.

But on the other hand you say

This would mean that you are not trapped in the while() since this only could only happen if waitFor() succeedes :confused:


Please repost your current code, to be absolutely certain.

1 Like

Thanks for the issue report. Thanks for the code also, it would have been perfect if you could provide a simple test app and the steps to produce the problem, so we can jump straight into hunting the problem rather than spending time removing application details from the test case.

Here’s the stripped down code by way of example:

#include "application.h"

SYSTEM_THREAD(ENABLED);

bool canSleep = false; // set to true when the Photon can sleep
void subscribeHandler(const char* event, const char* data) {
	// data set to the device ID that wants to sleep.
    String id = Particle.deviceID();
    Serial.println("Got sleep event");
    // see if this was a notification from this core that we can sleep
    if (!strcmp(data, id.c_str())) {
        canSleep = true;
        Serial.println("set canSleep = true");
    }
    else
    {
    		Serial.println("deviceID didn't match");
    }
}

void setup() {

    Serial.begin(9600);
    Particle.subscribe("sleep", subscribeHandler, MY_DEVICES);  // listen for sleep events
}

void loop() {

	// wait for the cloud connection to be connected or timeout after 10 seconds
    if (waitFor(Particle.connected, 10000)) {
        // publish the sleep event last
        String deviceID = Particle.deviceID();
        Particle.publish("sleep", deviceID.c_str(), 60, PRIVATE);

        while (!canSleep) {
        		Particle.process();
        }
        Serial.println("sleeping...(not really!)");
        canSleep = false;
    }
}

When I run this, serial is empty until the device connects to the cloud, then this is repeated in the serial output:

sleeping...(not really!)
Got sleep event
set canSleep = true
sleeping...(not really!)
Got sleep event
set canSleep = true
sleeping...(not really!)
Got sleep event
set canSleep = true
sleeping...(not really!)
Got sleep event
set canSleep = true
sleeping...(not really!)
...

So event subscriptions are definitely working. :smile:

You might try adding parts of your application back to this stripped down code and then observing at which point subscriptions stop working.

2 Likes

First of all thank you very much for your support @mdma and @ScruffR! :smile:

I used your code @mdma to build my sketch step by step again. Just copy and pasting your sketch produces the same output in the serial console of ParticleDev.
After that I have added System.sleep(SLEEP_MODE_DEEP,15); and //System.sleep(D3,RISING,15);

So the code now looks like:

#include "application.h"

SYSTEM_THREAD(ENABLED);

bool canSleep = false; // set to true when the Photon can sleep
void subscribeHandler(const char* event, const char* data) {
	// data set to the device ID that wants to sleep.
    String id = Particle.deviceID();
    Serial.println("Got sleep event");
    // see if this was a notification from this core that we can sleep
    if (!strcmp(data, id.c_str())) {
        canSleep = true;
        Serial.println("set canSleep = true");
    }
    else
    {
    		Serial.println("deviceID didn't match");
    }
}

void setup() {

    Serial.begin(9600);
    Particle.subscribe("sleep", subscribeHandler, MY_DEVICES);  // listen for sleep events
}

void loop() {

	// wait for the cloud connection to be connected or timeout after 10 seconds
    if (waitFor(Particle.connected, 10000)) {
        // publish the sleep event last
        String deviceID = Particle.deviceID();
        Particle.publish("sleep", deviceID.c_str(), 60, PRIVATE);

        while (!canSleep) {
        		Particle.process();
        }
        Serial.println("sleeping...(not really!)");
        canSleep = false;
        
        System.sleep(SLEEP_MODE_DEEP,15);
        //System.sleep(D3,RISING,15);
    }
}  

When I use System.sleep(SLEEP_MODE_DEEP,15); it works as expected. The Photon wakes up every 15 seconds and after it published the sleep event it goes sleeping. I will try tomorrow to wake it up on the wkp pin.

When I use System.sleep(D3,RISING,15); my Photon starts breathing white after publishing the sleep event for the first time. And it never starts breathing cyan again. I am also not able to wake it up on pin D3 (also tried D4). And ther is no output in the serial console.

I am going to sleep about it and try to use another sensor to wake it up.

There is an issue where USB can wake up the device. If you can power without USB, and try (ust to prove that’s the cause, that will help narrow down our scope. There’s already a fix for this which will be in the next release.

@hl68fx, in your loop() code, you call Serial.print() and System.sleep() immediately after the sleep publish event. You may want to a Particle.process() and a delay(200) just before sleep() to allow the publish and Serial.print() to complete. This is not ideal but should work.

Waking on a pin interrupt requires that you have that pin pulled high or low EXTERNALLY since the Photon pins will float during STOP (wake on any pin interrupt) or STANDBY (deep sleep) modes. So, for a RISING pin wake condition, you need to have a pull-down on the pin.

I don’t use the STOP mode sleep mode since it uses more power than deep sleep. In this mode the code stops after sleep is called and processing continues at the line following the sleep call. The breathing white indicates that the wifi is OFF. @mdma, with system mode AUTOMATIC, should WiFi and Cloud come back on automatically after a STOP mode sleep?

Just a note - though the WKP pin can be used to wake from STOP mode sleep, it is the only pin which can wake (using a RISING edge only) the Photon from a deep sleep (STANDBY). :grinning:

1 Like

@mdma I am using the power shield and a li-ion battery but it did not change anything.

@peekay123 I have added Particle.process() and delay(200) just before sleep(). The result is that it goes to sleep and then keeps flashing green. But I think that it was not necessary to add these two lines because in this code publishing works reliable because it stays in the while loop until it is allowed to sleep.
I read yesterday about deep sleep (standby) and that it needs less power. When I am later back at home I will try to use a VCNL4010 to wake it up on the wkp pin.

Thank you all very much for your kind help!

Edit: After pressing the reset button my photon is not flashing green. It is breathing white again so WiFi seems to be off.

If code continues where it was before sleep, I'd even suggest that it might be desirable that WiFi and cloud (including Particle.subscribe()s) come back to what they were beforehand in any SYSTEM_MODE (at least with SYSTEM_THREAD(ENABLED))

[quote=“hl68fx, post:13, topic:19616”]
Edit: After pressing the reset button my photon is not flashing green. It is breathing white again so WiFi seems to be off.
[/quote] That is weird. Reset should go back to AUTOMATIC mode and restart WiFi and Cloud. Are you sure you hit reset?

Also, remember that deep sleep causes a reset when awakened, versus just continuing from the sleep command. :wink:

Hi,

I can confirm that everything works with SLEEP_MODE_DEEP for several hours now. I will try to do the same with System.sleep(D3,RISING,15); and report back :smile:
I am pretty sure I pushed the reset button but it was really really late.

After about a half day my Photon got stuck in the while loop:

while (!canSleep) {
    Particle.process();
}

So I assume there was an error with my Wifi. To prevent this in the future I have changed the while loop to:

unsigned long time = millis();

while (!canSleep) {
    Particle.process();
    if(millis() - time >= timeout){
    System.reset();
    }
}

Is there a better or more elegant way to achieve this? @mdma, @ScruffR, @peekay123

I guess if a System.reset() is acceptable in this case a drop out of this loop is just fine as well and on next wake you should be good again (at least with deep sleep).

Or a more graceful way might be to try reconnecting, retrying the last action that supposedly failed with the most recently obtained data.