Sleep mode explained using code samples


#1

There was a recently a question about sleep mode on the Photon, and it wasn’t anything I ever played with, so I made a little circuit and 4 test sketches to play around with it and see how things work, since I was still a little confused after reading the documentation. Each example has the code with a lot of comments followed by an annotated example output, so you can probably figure it out just by reading the examples even if you don’t run the code yourself.

Here’s the circuit:

The main thing is the button, which connects D0 to ground, with an external 10K pull-up resistor. Also, I connected a battery to VBAT to preserve the retained variables in SRAM. Actually, you don’t need the battery and the first three tests don’t use it.

###Test 1 - Wi-Fi Sleep

#include "Particle.h"

// Simple test of using sleep(), which really just puts the Wi-Fi adapter to sleep
// but your code continues to run. I think I'd prefer to use SYSTEM_MODE(MANUAL)
// instead for more explicit control, but this does work.

unsigned long firstAvailable = 0;
int counter;

void setup() {
	Serial.begin(9600);
}

void loop() {
	bool wifiReady = WiFi.ready();
	bool cloudReady = Particle.connected();

	Serial.printlnf("wifi=%s cloud=%s counter=%d", (wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"), counter++);

	if (wifiReady) {
		if (firstAvailable == 0) {
			firstAvailable = millis();
		}
		if (millis() - firstAvailable > 30000) {
			// After we've been up for 30 seconds, go to sleep
			Serial.println("calling System.sleep(30)");
			System.sleep(30);

			// This happens immediately - System.sleep(30) doesn't block and your code keeps running, just with
			// Wi-Fi and cloud turned off. The LED should breathe white instead of cyan in this state. Then,
			// after the time expires, reconnection to the cloud occurs
			Serial.println("returned from sleep");
		}
	}
	else {
		firstAvailable = 0;
	}

	delay(1000);
}

/*
[led breathing cyan]
[power consumption: ~ 0.07 A]
wifi=on cloud=on counter=28
wifi=on cloud=on counter=29
wifi=on cloud=on counter=30
calling System.sleep(30)
[power consumption: ~ 0.03 A]
[led breathing white now]
returned from sleep
wifi=off cloud=off counter=31
wifi=off cloud=off counter=32
wifi=off cloud=off counter=33
...
wifi=off cloud=off counter=57
wifi=off cloud=off counter=58
wifi=off cloud=off counter=59
[power consumption: ~ 0.07 A]
[led breathing cyan again]
wifi=on cloud=on counter=60
wifi=on cloud=on counter=61
wifi=on cloud=on counter=62
...

 */

###Test 2 - Deep Sleep

#include "Particle.h"

// Simple test of using sleep(SLEEP_MODE_DEEP, 30). This halts execution of your code, and when it
// wakes up again, it goes through setup() again with all variables cleared.

// Running this test without a battery connected to VBAT is interesting, however, because retained
// variables ARE preserved in deep sleep, even without a battery.

// IMPORTANT NOTE: If using retained variables to preserve values across deep sleep when NOT using
// a battery, be sure to tie VBAT to ground. This somewhat counter-intuitive step is necessary
// because otherwise when you first power up the device, the retained memory will not be initialized,
// so it will contain random values, which will probably confuse your code!

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));


unsigned long firstAvailable = 0;
int counter;
retained int retainedCounter = 0;

void setup() {
	Serial.begin(9600);
}

void loop() {
	bool wifiReady = WiFi.ready();
	bool cloudReady = Particle.connected();

	Serial.printlnf("wifi=%s cloud=%s counter=%d retainedCounter=%d", (wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"),
			counter++, retainedCounter++);

	if (wifiReady) {
		if (firstAvailable == 0) {
			firstAvailable = millis();
		}
		if (millis() - firstAvailable > 30000) {
			// After we've been up for 30 seconds, go to sleep. The delay is so the serial output gets written out before
			// sleeping.
			Serial.println("calling System.sleep(SLEEP_MODE_DEEP, 30)");
			delay(2);

			System.sleep(SLEEP_MODE_DEEP, 30);

			// The rest of the code here is not reached. SLEEP_MODE_DEEP causes the code execution to stop,
			// and when wake up occurs, it's like a reset where you start again with setup(), all variables are
			// cleared, etc.
			Serial.println("returned from sleep, should not occur");
		}
	}
	else {
		firstAvailable = 0;
	}

	delay(1000);
}

/*
[power consumption: ~ 0.07 A, battery NOT connected to VBAT]
wifi=on cloud=on counter=1 retainedCounter=1
wifi=on cloud=on counter=2 retainedCounter=2
..
wifi=on cloud=on counter=29 retainedCounter=29
wifi=on cloud=on counter=30 retainedCounter=30
calling System.sleep(SLEEP_MODE_DEEP, 30)
[serial disconnected, LED off]
[power consumption: 0.00 A. Not actually 0, but too small for my USB power meter to measure.]
[30 seconds later, device goes through normal boot sequence, then]
[power consumption: ~ 0.07 A]
wifi=on cloud=on counter=0 retainedCounter=31
wifi=on cloud=on counter=1 retainedCounter=32
wifi=on cloud=on counter=2 retainedCounter=33
wifi=on cloud=on counter=3 retainedCounter=34

[press reset button - retained values still preserved]
wifi=on cloud=on counter=0 retainedCounter=77
wifi=on cloud=on counter=1 retainedCounter=78
wifi=on cloud=on counter=2 retainedCounter=79
wifi=on cloud=on counter=3 retainedCounter=80

 */

###Test 3 - Pin change sleep

#include "Particle.h"

// Simple test of using System.sleep(D0, RISING, 30). This uses processor stop mode on the Photon
// to stop the processor and Wi-Fi until either the pin condition or the timeout occurs. When
// the wake occurs, execution continues where it left off with all variables preserved.

// Connect D0 to GND through a button with a 10K pull-up resistor.


unsigned long firstAvailable = 0;
int counter;

void setup() {
	Serial.begin(9600);
	Serial.println("setup called");
}

void loop() {
	bool wifiReady = WiFi.ready();
	bool cloudReady = Particle.connected();

	Serial.printlnf("wifi=%s cloud=%s counter=%d", (wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"), counter++);

	if (wifiReady) {
		if (firstAvailable == 0) {
			firstAvailable = millis();
		}
		if (millis() - firstAvailable > 30000) {
			// After we've been up for 30 seconds, go to sleep

			Serial.println("calling System.sleep(D0, RISING, 30)");

			// This delay is here so the serial print above goes out before going to sleep
			delay(1);


			System.sleep(D0, RISING, 30);

			// In this mode, setup() is not called again. Sometimes, but not always, the Serial port does
			// not restore properly. This solves the problem.
			Serial.begin(9600);

			// In this mode,after the sleep period is done, execution continues where we left off,
			// with variables intact.
			Serial.println("returned from sleep");

			// Important! If you don't reset this, you'll immediately go back to sleep, since variables
			// are preserved.
			firstAvailable = 0;
		}
	}
	else {
		firstAvailable = 0;
	}

	delay(1000);
}

/*
[power consumption: ~ 0.07 A]
wifi=on cloud=on counter=29
wifi=on cloud=on counter=30
calling System.sleep(D0, RISING, 30)
[serial disconnected LED is off]
[power consumption: 0.00 A. Not actually 0, but too small for my USB power meter to measure.]
...
[30 seconds later]
[power consumption: ~ 0.07 A]
returned from sleep
wifi=on cloud=on counter=31
wifi=on cloud=on counter=32
wifi=on cloud=on counter=33
 */

###Test 4 - Battery backup and WKP test

#include "Particle.h"

// Simple test of using System.sleep(SLEEP_MODE_DEEP, 30) along with a backup battery and the WKP pin.


// Connect WKP to 3V3 through a button with a 10K pull-down resistor.
// Connect a battery (I used a CR2032 coin cell) to VBAT

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));


unsigned long firstAvailable = 0;
int counter;
retained int retainedCounter = 0;

void setup() {
	Serial.begin(9600);
	Serial.println("setup called");
}

void loop() {
	bool wifiReady = WiFi.ready();
	bool cloudReady = Particle.connected();

	Serial.printlnf("wifi=%s cloud=%s counter=%d retainedCounter=%d",
			(wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"), counter++, retainedCounter++);

	if (wifiReady) {
		if (firstAvailable == 0) {
			firstAvailable = millis();
		}
		if (millis() - firstAvailable > 30000) {
			// After we've been up for 30 seconds, go to sleep

			Serial.println("calling System.sleep(SLEEP_MODE_DEEP, 30)");

			// This delay is here so the serial print above goes out before going to sleep
			delay(1);

			System.sleep(SLEEP_MODE_DEEP, 30);

			Serial.println("should never be reached");
		}
	}
	else {
		firstAvailable = 0;
	}

	delay(1000);
}

/*
setup called
wifi=on cloud=on counter=0 retainedCounter=27
wifi=on cloud=on counter=1 retainedCounter=28
...
wifi=on cloud=on counter=27 retainedCounter=54
wifi=on cloud=on counter=28 retainedCounter=55
wifi=on cloud=on counter=29 retainedCounter=56
wifi=on cloud=on counter=30 retainedCounter=57
calling System.sleep(SLEEP_MODE_DEEP, 30)
[30 seconds later]
setup called
wifi=on cloud=on counter=0 retainedCounter=58
wifi=on cloud=on counter=1 retainedCounter=59
wifi=on cloud=on counter=2 retainedCounter=60

[Using the wakeup button works like timeout does]
setup called
wifi=on cloud=on counter=0 retainedCounter=89
wifi=on cloud=on counter=1 retainedCounter=90
wifi=on cloud=on counter=2 retainedCounter=91

[press reset button]
setup called
wifi=on cloud=on counter=0 retainedCounter=223
wifi=on cloud=on counter=1 retainedCounter=224
wifi=on cloud=on counter=2 retainedCounter=225
wifi=on cloud=on counter=3 retainedCounter=226

[unplug USB power and plug back in, battery connected]
setup called
wifi=on cloud=on counter=0 retainedCounter=230
wifi=on cloud=on counter=1 retainedCounter=231
wifi=on cloud=on counter=2 retainedCounter=232

 */


Difference Between Power Saving Sleep Modes
Comparison table of the sleep modes
System.sleep(seconds) broken in 0.6.0?
P1 Sleep Current Consumption Not Matching Datasheet
#2

This was super helpfull!! thanks a lot!

for the Electron you just switch out
bool wifiReady = WiFi.ready(); with bool cellReady = Cellular.ready();


#3

Thanks for posting this Rick, it clearly is helping people!


#4

Thanks for the example! I am having issues with the WiFi reconnecting after System.sleep(); however. The LED never turns back to breathing cyan after the first System.sleep() call.

I loaded the first example up to test my theory, and sure enough, wifiReady and cloudReady both return false after the photon was scheduled to wake up!

/*
[led breathing cyan]
wifi=on cloud=on counter=28
wifi=on cloud=on counter=29
wifi=on cloud=on counter=30
calling System.sleep(30)
[led breathing white now]
returned from sleep
wifi=off cloud=off counter=31
wifi=off cloud=off counter=32
wifi=off cloud=off counter=33
...
wifi=off cloud=off counter=57
wifi=off cloud=off counter=58
wifi=off cloud=off counter=59

This all works the same on my photon, however…

[led still breathing white]
wifi=off cloud=off counter=60
wifi=off cloud=off counter=61
wifi=off cloud=off counter=62
...
wifi=off cloud=off counter=110
wifi=off cloud=off counter=111
wifi=off cloud=off counter=112
...
 */

It never seems to reconnect.

I am on build 0.5.3 and running the exact code in the first example. The second example’s System.sleep(SLEEP_MODE_DEEP, 30); does work as expected, shutting down and coming back on after 30s, however I can’t use this sleep mode because I have a couple software interrupts that I need to run while the photon is sleeping.

Any suggestions?


#5

The not actually sleep mode System.sleep(time) that stops the WiFi modem for a period of time appears to not wake back up at time expiration in 0.5.3 and 0.6.0rc2. It works fine in 0.5.2 and earlier in my quick and not exhaustive test.

While that would be a bug and ought to be fixed; since that mode isn’t really sleep anyway, I’d just use MANUAL system mode and use WiFi.on() and WiFi.off(). You may need to call Particle.connect() and Particle.disconnect() as well; I’m not positive about that.


#6

Appreciate it.

I was curious if there was anything more to the basic System.sleep(long seconds) mode than just WiFi management. Guess not! I ended up coding a manual delay and reconnection script upon “wake”. Strange that it was working in an earlier firmware. Perhaps it will get fixed in future updates

// Delay loop until "sleep" is finished
Serial.println("Sleeping");
for (int i = 0; i < secondsToSleep; i++){
  ledToggle = (ledToggle == HIGH) ? LOW : HIGH;
  digitalWrite(STATUS_LED, ledToggle);
  Serial.print(".");
  delay(1000); // Wait 1 second
}

//Reconnect to cloud if not already
if(!WiFi.ready()){
  WiFi.on();                               // Turn wifi on
  WiFi.connect(WIFI_CONNECT_SKIP_LISTEN);  // Connect to particle cloud
  Particle.process();                      // Run WLAN background loop
}

#7

Hey Rick. Picking up on an old thread. You say “The not actually sleep mode System.sleep(time) that stops the WiFi modem for a period of time appears to not wake back up at time expiration in 0.5.3”. Do you know if this also applies to cellular connections? I would assume so. I’m looking at an issue with devices on 0.5.3 not waking up after going into a System.sleep. Do you have any more details on this issue? It seems to happen pretty sporadically but when it does, it basically bricks remote devices.


#8

Which sleep version are you using? Just the System.sleep(time)? I know it didn’t work in a number of versions, 0.5.3 could have been one of them but I’m not sure. It’s not really all that useful, it’s better to use SYSTEM_MODE(SEMI_AUTOMATIC) and directly control the modem to turn on or off as needed.


#9

We’re using the following:
System.sleep(D6, CHANGE, SLEEP_NETWORK_STANDBY, sleepIntervalSeconds);

This was implemented a while ago, definitely before we heard about issues with System.sleep. From what you are saying, as well as comments by others including ScruffR, it sounds like the move is to change the system mode & directly control the modem + handle interrupts.


#10

No, that call should be fine. You may still want to manually control the modem, however. And also make sure your sleep interval isn’t less than, say 10 seconds. And upgrade to a newer system firmware version. 0.5.3 is pretty old and there are a lot of changes since that at 0.6.4.


#11

@rickkas7
Hello Rick. Thank you for posting this, it was quite helpful. But i need a little bit of help. None of these modes seems to suite my particular application:
I will be operating my photon in manual mode only.
I would like to put my photon into deep sleep mode for a specified amount of time and then it automatically wake up (without the need of a dedicated external input trigger signal to wake up).
Whilst in deep sleep mode i would like for it to still run the code associated with an interrupt i have programmed to trigger whenever a reed switch closes (which i am using to measure wind speed). Or even if it were to wake up to execute the code in the interrupt and than return to sleep mode that will be fine as well.
Once the time has elapsed i would like the photon to wake up from deep sleep and continue as normal, i.e not start from void setup again.

Aside from,

System.sleep(SLEEP_MODE_DEEP, 30)

I did come across a few different sleep commands in the particle docs and the,

System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)

seems to be the next best for my purpose. However i am unclear if it will acknowledge the use of my interrupt and since i am not using an external trigger to wake the photon, if it will work. Please advise. Thanks in advance.


#12

@Alli, the way deep sleep works is dictated by the design of the STM32F205 MCU in the Photon. By design, deep sleep (aka STANDBY sleep on the STM32) will ONLY wake with a rising edge on the WKP (wakeup) pin of the Photon and no other pin will work. It will also wake on an alarm condition from the RTC so you can set a time to wake. In addition, because this sleep mode turns off so much of the processor, waking does a RESET of the processor upon waking.

The only sleep mode that will work for your requirements is STOP sleep (using System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)) where you can wake on any interrupt including a specified time but current consumption is not the lowest. If you wake on the interrupts of a wind speed indicator, the resulting interrupts may not yield good power savings due to their frequency. However, it is possible to create loop() code that goes to STOP sleep mode where the ISR is serviced and the loop code goes right back to STOP sleep until a suitable condition is met (data accumulated over desired interval) after which is can process that condition and go back to sleep. Since DeviceOS 1.0.0 now allows your code to test the reason for waking (timeout vs interrrupt), when a timeout occurs you could turn on WiFi, connect to the cloud and publish your data, turn WiFi off and go back to sleep.

So, for power consumption it is not the best but it is the only option you have for your requirements and using only the Photon (ie. no secondary processor to do the windspeed data gathering).


#13

To clarify: There is no way to run your interrupt code whilst your device is in either actual sleep mode (neither Standby Mode nor Stop Mode).
Either the device sleeps or it is awake and will attend to your code, but not both at the same time.


#14

@peekay123 @ScruffR

Thank you guys for the advice. i will play around with this the STOP mode and see if it works well for me.


#15

@peekay123 @ScruffR

Is there a way to put my photon into deep sleep for less than 1 second using the

System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)

command or similar?


#16

That command does not deep sleep but stop sleep and no there is no easily accessable command for sub-second sleep.
What do you intend with such a short sleep phase?
Are you intending to have your device connected before and after the sleep phase?


#17

@ScruffR

I intend to put the photon to sleep every 100ms or so (still trying to find a suitable period) to run the Blynk.run commands and every 10 100ms (1 second) i will run the main functions of my program. I am not sure if

BlockquoteSystem.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long seconds)

will work since the time condition is defined in seconds? Is there a way to put the photon into a deep sleep or to STOP for a period less than a second?


#18

Since a WiFi reconnect would usually take more than a second sleeping the device for that short of a period doesn’t seem to be viable approach.

You explain what you want to do, but not why you want to sleep the device in the first place.


#19

@ScruffR

Sorry. Let me clarify. I will be using the device in manual mode so the issue of reconnecting to the wifi or to the particle cloud is not a concern. Communication of data will be between my photon and a Blynk application via Bluetooth. My design only requires an update of data every second, in the most frequent case, but to keep Blynk running i need to execute the Blynk.run command more often than every second. I am using trial an error to determine what the lowest frequency is that this command needs to be executed before i have connection issues with Blynk. This frequency will almost definitely be in the millisecond range. Arbitrarily i am stating a 100ms for now. In between this time and the time of execution of my main code, i do not need the photon to run any other code. Hence, i would like to stop code execution (aside from the running of time for my timers) or place the photon into deep sleep for that duration. This will dramatically reduce the overall power consumption of my project and increase the working hours i can get from my battery.


#20

During sleep there are not timers running on the Photon.
The interrupt for timed wake is produced by the RTC which only has 1sec resolution IIRC.

But when you are talking about BT/BLE you may like to jump ship to the Argon which has onboard BT/BLE capabilities and should also feature real-time-counter(s) which should still run during sleep phases (I haven’t really looked into these yet tho’).