WakeOn RI in SLEEP_NETWORK_STANDBY?


#1

Hello community,

Thanks to @rickkas7 for the excellent write up found here:

It seems to me by doing a system.sleep with SLEEP_NETWORK_STANDBY along with setting up an interrupt on the RI signal would make for an awesome sleep mode?

I understand that the RI output of the modem is normally set up to assert when there is a phone call or SMS coming in, but according to the ‘UBlox AT Command Manual’, the RI output can be configured to assert when there is incoming data too by using the command ‘AT+URING=2’?

Does anyone have experiences/an example to show whether this is feasible or not?

The problem we are trying to solve is to avoid the network reconnect time by using the SLEEP_NETWORK_STANDBY mode and also being woken up when there is data coming in from the network.

My apologies if this topic has been covered before.

Thanks!

Gunnar


How to keep 3G modem on and save power at the same time?
Boron LTE w/ Solar- Trials
#2

Well that’s cool - it works!

Here’s the code I used on the Electron:

#include "Particle.h"

SerialLogHandler logHandler(LOG_LEVEL_TRACE);

const int SLEEP_PIN = D2;

void setup() {
	Serial.begin();
	pinMode(SLEEP_PIN, INPUT_PULLUP);

}

void loop() {
	if (digitalRead(SLEEP_PIN) == LOW) {
		Log.info("turning on AT+URING=1");

		// Enable wake on all URCs
		Cellular.command("AT+URING=1\r\n");

		delay(1000);

		System.sleep(RI_UC, RISING, 120, SLEEP_NETWORK_STANDBY);

		// This delay is to allow the serial monitor to reconnect only
		delay(2000);

		Log.info("woke from sleep mode");

		// Turn URING back to the default value (only notify on SMS or voice call)
		Cellular.command("AT+URING=0\r\n");
	}
}

I entered sleep by pulling D2 to GND. A few seconds later, the Electron went to sleep.

Then I used the ping feature in the console to ping the Electron, and it woke up and even returned success for the ping.

Log output:

0000222239 [app] INFO: turning on AT+URING=1
   222.239 AT send      12 "AT+URING=1\r\n"
   223.029 AT read OK    6 "\r\nOK\r\n"
0000224029 [comm] INFO: Waiting for Confirmed messages to be sent.
Serial connection closed.  Attempting to reconnect...
Serial monitor opened successfully:
   226.264 AT read  +   17 "\r\n+UUSORD: 0,33\r\n"
Socket 0: handle 0 has 33 bytes pending
   226.274 AT send      17 "AT+USORF=0,1024\r\n"
   226.294 AT read  +   71 "\r\n+USORF: 0,\"52.87.113.144\",5684,33,\"\x17\xfe\xfd\x00\x01\x00\x00\x00\x00\x00 \x00\x14\x00\x01\x00\x00\x00\x00\x00 \xbc\xfa\xa2\xb2\xbc.\xe4'G/\xf2\xf4\""
   226.306 AT read OK    6 "\r\nOK\r\n"
0000226306 [system] TRACE: received 33
0000226306 [comm.coap] TRACE: recieved ACK for message id=12
0000226307 [comm.protocol] INFO: rcv'd message type=13
0000227263 [app] INFO: woke from sleep mode
   227.263 AT send      12 "AT+URING=0\r\n"
   227.264 AT read  +   17 "\r\n+UUSORF: 0,38\r\n"
Socket 0: handle 0 has 38 bytes pending
   227.275 AT read OK    6 "\r\nOK\r\n"
   227.285 AT send      17 "AT+USORF=0,1024\r\n"
   227.305 AT read  +   76 "\r\n+USORF: 0,\"52.87.113.144\",5684,38,\"\x17\xfe\xfd\x00\x01\x00\x00\x00\x00\x00!\x00\x19\x00\x01\x00\x00\x00\x00\x00!\xb4G\x00g5\xb2\x10\xbcD\xd6}\"\xe39o>`\""
   227.317 AT read OK    6 "\r\nOK\r\n"
0000227317 [system] TRACE: received 38
0000227317 [system] TRACE: send 34
socketSendTo(0,52.87.113.144,5684,,34)
   227.318 AT send      36 "AT+USOST=0,\"52.87.113.144\",5684,34\r\n"
   227.329 AT read  >    3 "\r\n@"
   227.379 AT send      34 "\x17\xfe\xfd\x00\x01\x00\x00\x00\x00\x00!\x00\x15\x00\x01\x00\x00\x00\x00\x00!s\xf7\xce\xa7s\xe0\xb8\xd2\xe7\x85\xcd@\x16"
   227.520 AT read  +   16 "\r\n+USOST: 0,34\r\n"
   227.530 AT read OK    6 "\r\nOK\r\n"

Electron sleep or current saving mode
#3

Here’s a more elaborate example. It allows:

  • Wake by cellular
  • Wake by another pin (RISING only)
  • Wake by sleep time expired

It requires 0.8.0 (tested with 0.8.0-rc.8) because it require multi-pin wake support and SleepResult. (The example above works with 0.7.0.)

It also has a function, variable, and event subscription, and those work too on wake from cellular.

To run the test:

  • To go to sleep, pull D2 to GND
  • To wake by pin, pull D3 to 3V3
  • You can wake by ping, function (“testFn”), variable (“testVar”), or publish (“wakeEvent”)

And some other caveats:

  • It only works with stop mode sleep (pin + time). You can’t wake from SLEEP_MODE_DEEP from a pin other than WKP, so you can’t wake on RI_UC.
  • You need to use SLEEP_NETWORK_STANDBY, otherwise the modem is turned off and obviously can’t receive the request.
  • You can wake on more than 2 pins, but all of the pins must wake on RISING as that’s what RI_UC requires.
  • You probably won’t see the function or subscribe log messages because serial won’t have reconnected yet.
#include "Particle.h"

#if SYSTEM_VERSION < 0x00080000
#error "You must target system firmware 0.8.0 or later to use this code"
#endif

SerialLogHandler logHandler(LOG_LEVEL_TRACE);

const int SLEEP_PIN = D2;
const int WAKE_PIN = D3;
const unsigned long SLEEP_TIME_SECS = 120;

int testVar = 0;

int testFn(String param);
void wakeEvent(const char *event, const char *data);

void setup() {
	Serial.begin();

	pinMode(SLEEP_PIN, INPUT_PULLUP);
	pinMode(WAKE_PIN, INPUT_PULLDOWN);

	Particle.variable("testVar", testVar);
	Particle.function("testFn", testFn);
	Particle.subscribe("wakeEvent", wakeEvent, MY_DEVICES);
}

void loop() {
	if (digitalRead(SLEEP_PIN) == LOW) {
		Log.info("turning on AT+URING=1");

		// Enable wake on all URCs
		Cellular.command("AT+URING=1\r\n");

		delay(1000);

		SleepResult sleepResult = System.sleep({RI_UC, WAKE_PIN}, RISING, SLEEP_TIME_SECS, SLEEP_NETWORK_STANDBY);

		// This delay is to allow the serial monitor to reconnect only
		delay(2000);

		if (sleepResult.wokenUpByRtc()) {
			Log.info("woke up by time expired");
		}
		else
		if (sleepResult.pin() == RI_UC) {
			Log.info("woke up by cellular");
		}
		else {
			Log.info("woke up by WAKE_PIN");
		}

		// Turn URING back to the default value (only notify on SMS or voice call)
		Cellular.command("AT+URING=0\r\n");

		testVar++;
	}
}

int testFn(String param) {
	Log.info("testFn %s", param.c_str());
	return 0;
}

void wakeEvent(const char *event, const char *data) {
	Log.info("wakeEvent %s %s", event, data);
}


buttonMirror() soft power down
#4

Awesome work @rickkas7! Thank you for your help, I will check on my end too to see that this works as intended. By using this mode, the power usage for a system could be drastically reduced and the battery life extended by a lot. Time to redo the power measurements? :wink:


#5

Wow !

I assume this method can be used to wake a SLEEP_NETWORK_STANDBY Electron for OTA updates or to call a Function to update Variables without the hassle of checking another endpoint during the next Awake Cycle.

I wonder how often the Electron will be “accidentally” woken up by the modem from Cellular/Cloud interactions while sleeping ?

This is very interesting, thanks to both of you !


#6

I tested OTA flash, and that will wake up the Electron and succeed.

I don’t think there will be any unexpected wake ups, as the cloud should never send data to the Electron outside of the normal circumstances (function, variable, subscription, OTA flash, or ping). But I have it running with the sleep period set to 22 minutes and we’ll see if it wakes up unexpectedly.

Note; The maximum sleep period must be less than the keep-alive interval, as this requires a working UDP back-channel to the device.


#7

Just now saw this!

Really cool feature for saving power yet still being cellular connected!

Great work!


Electron - Low power wake up from cellular message?
#8

@rickkas7 Hi Rick, just wanted to share some numbers with you on this. We have confirmed that this works very well and that our current consumption went from 20mA to about 4mA for a 30Mhz underclocked 3G Electron based product that is always ‘on’, ie always reachable from the back end. This functionality alone helped us get 5X more out of a single battery charge. Drastic improvement. Awesome work @rickkas7, thank you for your help and support!


#9

This is great work. Excited to implement this in our code.

I’m wondering if this will work with the Boron. I’ve got the Eagle files for the Boron – it looks like RI_UC is not connected.

Is there another way to implement this?


#10

Just thinking through this – I guess if I’m not worried about using the 20mA power vs 4mA power, I could start and stop the uC calling Particle.process() to check the RX buffer on the modem every 5 seconds or so. This would depend on the ping information being in the RX buffer.

Is this possible? Or do I need to call Particle.connect() before calling Particle.process()?


#11

The details of what will be exposed on the mesh platform for sleep modes have not been finalized. The nRF52840 has a crazy large number of sleep modes, and can do things like wake on serial data, which I presume is why the RI_UC pin isn’t connected. But exactly what will be exposed and when has not been determined.


#12

I’m trying to understand why this saves battery life. Does implementing this method reduce the network reconnection time (therefore less current draw from battery) while in SLEEP_NETWORK_STANDBY? Trying to get a clear picture on why this special command does that.


#13

It adds another use case: Wake by cellular.
So during SLEEP_NETWORK_STANDBY, Cloud events can wake the Electron.
OTA Firmware Flash, function calls, Particle.Variable Requests, Subscribed Events, etc, can wake the Sleeping Electron.

Per rickkas7 test results, the most power efficient sleeping method is to use NETWORK_STANDBY if you need to publish every 12 minutes or sooner. If you’re publishing at longer intervals, use DEEP SLEEP.

However, if your power budget can afford the 4.5 mA sleeping current of NETWORK_STANDBY, you always have access (from the Cloud) to your Electron with WakeOn RI_UC as a “virtual” wake pin.


#14

@Rftop Gotcha! That paints a much better picture and makes sense now. Thanks for the clarification.