Optimizing power consumption on Electron with System.sleep()

Dear all,

We are developing a device based on Electron that is supposed to work in wake/sleep cycles. Wake-up for just short jobs up to 2 min (registers in cellular network and particle, publishes identification data about cellular tower and falls asleep for days to save energy. We chose to use System.sleep(SLEEP_MODE_DEEP, TIME_VARIABLE) as the most appropriate power saving mode (keeps the battery gauge and consumes very small current).

Below is a simplified firmware we have for now but I am seeking for feedback on what you think could be done better, if any. The firmware works ok in my office.

#pragma PARTICLE_NO_PREPROCESSOR
#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

char cellular_data[200] = "";

int cb(int type, const char* buf, int len, char* cellular_data);

//This function to use instead delay() to avoid blocking code - period in milliseconds
void mydelay(int period);

//This function to start deep sleep mode
void sleepInMinutes(void);

void setup() {
	mydelay(60000);
//need to wait for 60 sec to allow enough time for cellular and particle connect if possible
	
	
}

void loop() {
    
    if ((RESP_OK == Cellular.command(cb, cellular_data, 10000, "AT+CGED=3\r\n"))
        && (strcmp(cellular_data, "") != 0)) {
        Serial.printlnf("Cellular info: %s\r\n", cellular_data);
    } else {
        Serial.printlnf("Cellular info not found");
    }
    
    if (strlen(cellular_data) > 0) {
        Particle.publish("cellData", cellular_data, PRIVATE);
        //To obey publish limit

        mydelay(1000);

        //Reset string to not publish the same one again 
        cellular_data[0] = '\0';
    }

    sleepInMinutes();
}

int cb(int type, const char* buf, int len, char* cellular_data) {
    if ((type == TYPE_PLUS) && cellular_data) {
        if(sscanf(buf, "\r\n+CGED: %[^\r]\r\n", cellular_data) == 1)
        /*nothing*/;
    }
	
    return WAIT;
}

void sleepInMinutes(void) {
  Particle.publish("Sleep", "Go To Deep Sleep For 3 minutes", PRIVATE);
    Particle.disconnect();
	mydelay(5000);
	Cellular.disconnect();
	Cellular.off(); 
    
    //sleep for 20 minutes for testing only, will be days
    System.sleep(SLEEP_MODE_DEEP, 180);

}

void mydelay(int period) {
	int time_now = millis();
	//using millis() instead of delay() to avoid blocking code & delay 1 second before deep sleep
     while(millis() < time_now + period){
        //wait delay ms
    }
}

My alternative code would be to do explicitly

void loop() {
Cellular.on(); 
if(Cellular.ready()){Cellular.connect();}; 
Particle.connect();

//the rest of the loop() code as above
}

but my developer says that it is redundant because after System.sleep() it goes to setup() where the above jobs get implicitly done by setup() and, additionally, we allow up to 60 sec for completion of these jobs by mydelay() for doing exactly cellular.on() and then cellular.connect() and particle.connect(). Maybe it could be done better to avoid some not so obvious glitches or the above code is fine as is. Thank you

Regards

Dmitry

Your myDelay() call is counter productive and won’t necessarily do what you want.
Cell connections may take up to 5 minutes and trapping the code in a tight loop isn’t best style either.

A better approach would be waitFor(Particle.connected, 300000).

It is also true that after deep sleep your code will run from start as it wakes with a system reset.

Unless you are actively cutting the cloud and/or cell connection there is also no need for Cellular.on() or Particle.connect() as you are running AUTOMATIC mode.

1 Like

@ScruffR

Thanks for a quick and helpful reply.

Just to clarify, does the position of waitFor(Particle.connected, 300000) within setup{} as below look OK to you? Or it should be inside loop() at the start of loop() code?

I also deleted mydelay() throughout as redundant (thanks for the suggestion)

#pragma PARTICLE_NO_PREPROCESSOR
#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);

char cellular_data[200] = "";
int cb(int type, const char* buf, int len, char* cellular_data);

//This function to start deep sleep mode
void sleepInMinutes(void);

void setup() {
	waitFor(Particle.connected, 300000) ;
}

void loop() {

    
    if ((RESP_OK == Cellular.command(cb, cellular_data, 10000, "AT+CGED=3\r\n"))
        && (strcmp(cellular_data, "") != 0)) {
        Serial.printlnf("Cellular info: %s\r\n", cellular_data);
    } else {
        Serial.printlnf("Cellular info not found");
    }
    
    if (strlen(cellular_data) > 0) {
        Particle.publish("cellData", cellular_data, PRIVATE);
               
        //Reset string to not publish the same one again 
        cellular_data[0] = '\0';
    }
    
    sleepInMinutes();
}

int cb(int type, const char* buf, int len, char* cellular_data) {
    if ((type == TYPE_PLUS) && cellular_data) {
        if(sscanf(buf, "\r\n+CGED: %[^\r]\r\n", cellular_data) == 1)
        /*nothing*/;
    }
	
    return WAIT;
}

void sleepInMinutes(void) {
    
	Particle.publish("Sleep", "Go To Deep Sleep For 3 minutes", PRIVATE);
    Particle.disconnect();
	Cellular.disconnect();
	Cellular.off(); 
    
    //sleep for 3 minutes
    System.sleep(SLEEP_MODE_DEEP, 180);

}

1 Like

Since you are using deep sleep and go to sleep after the first run of loop() you could even put all your code in setup().

With waitFor() you can even check for the result and gracefully handle a failed connection attempt.

e.g.

void setup() {
  if (!waitFor(Particle.connected, 300000)) {
    // for some reason no connection possible
    // take counter measures
  }
  
  // from here business as usual
}
3 Likes

@ScruffR

Great tip, thanks!

@ScruffR

What would be a code prototype in setup() and loop() parts if I want to

  1. wait before entering into the looped routine ONLY the first time (say I send a device to a customer and want a delay of days but this is one-off thing before the start of normal routine)

  2. After this first delay I want to start looping through awaking -> routine -> deep sleep -> repeat?

In my mind, I would have a for loop in loop() part counting for 2 and during the 1st pass it skips to deep sleep mode but afterwards it never does the skipping. Not sure that the for loop counter variable is saved in the memory during deep sleep mode

I’d use EEPROM to store the “first-run” flag.
After the first run you clear that flag.

Whenever the device starts it first checks the state of that flag and acts accordingly.

2 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.