Sleep mode explained using code samples

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

 */

19 Likes