Wake on interrupt


#1

I am working on a battery power application and I need to extend battery life as long as possible.

I see that the bluz module is rated for 60uA in standby mode which is great. If I can get to that I am golden. In my application I will generally only wake the module about once per day or so.

What I want is for the bluz module to be sitting in standby mode at all times unless I trigger a digital input firing an interrupt on the module. I do not want the module to wake on interval which is the documented functionality:
System.sleep(SLEEP_MODE_CPU);
This wakes the module every 100 mS which results in a constant current draw around 250-300 uA. This just is not quite low enough for my battery power application.

Is there any documentation on how to put the module to sleep and only wake on interrupt? I cant seem to figure out the System.sleep function.


#2

This is the code I am currently running:

#include "application.h"

/* This function is called once at start up ----------------------------------*/
void setup()
{
	//Take control of RGB so no power is lost there.
	RGB.control(true);
	//Make sure LED on D7 Line is off.
	digitalWrite(D7, LOW);

	//Set module to wake up when D3 is pulled to ground or wake up every 3 seconds.  Bluetooth radio off.
	System.sleep(D3, FALLING, 3, SLEEP_NETWORK_OFF);
}

/* This function loops forever --------------------------------------------*/
void loop()
{

}

I’m seeing a constant current draw of 4.71 mA at 3.3VDC. If I switch to:
System.sleep(SLEEP_MODE_CPU);
Then current draw goes down to around 250 - 300 uA


#3

So just so I understand, you want the board to fully powered down, no BLE connection, and then wake up once a day on an interrupt, correct?

This is currently not available in 1.1.47, but we can add it very easily to develop. The interrupts will wake up the board already, we just need to add deep sleep mode.

Let me know if this is what you need, I can try and get it into develop quickly


#4

Hi @eric

I need the board to fully power down(no BLE connection) and only power up on pin interrupt. I like the sleep function to wake on interrupt or on interval, however this is not currently getting the power consumption low enough. I think if that function would put the module into deep sleep I will be set.

Here is basically a brief rundown on what I am doing. Not required reading beyond this point.

I have multiple boards with contact closure inputs. These boards have a Bluz module which is connected via RS232 to a high power 900mHz wireless module. When an input is closed the Bluz module wakes up, then wakes up the 900 mHz module and sends a data packet to a receiver board which energizes a relay, after acknowledgment is received back from the relay board the Bluz module puts the 900 mHz module back to sleep and then goes back to sleep itself. The function of the Bluz module is to act as the system CPU and also allow bluetooth connection for configuration changes to functionality.

Currently I am just using the System.sleep(SLEEP_MODE_CPU) function. Functionally everything is working perfectly. However since the Bluz module is waking up every 100mS the power consumption is about 250-300 mA. At this rate 2 AA batteries will only keep the unit alive for approximately 346 days if no transmission is ever sent. I am shooting to get battery life as high as possible. If I can achieve a 60 uA current consumption this will be an awesome product!

Sorry for writing a book :slight_smile:

Thanks as always @eric!


#5

I can add the DEEP_SLEEP mode, which will allow you to turn bluz off and wake up on interrupt, that would give you the absolute lowest power consumption. I will try and get this into develop as soon as possible.

There are two things you could do in the meantime, if you aren’t already. First, ensure that Serial is disabled by using Serial1.end() when you don’t need it. Keeping Serial enabled will consume a lot of power as it keeps the 16MHz clock running at all times.

Second, you could turn off BLE advertising if you don’t want to stay connected. Not sure if you saw this thread: DK energy usage data

But @AndyW did some great work on profiling the power consumption. The biggest power usage is to maintain the BLE connection. So if you disconnect and turn off advertising, and only turn it back on when you need it, you would decrease your power consumption as much as possible.

All of this will be moot once I get you the DEEP_SLEEP function, so I will update this thread as soon as I have it checked in.


#6

Hey @eric,

Sounds great.

I did already have the Serial1.end() implemented for when I’m done with the serial port. However I never turned off BLE advertising. I do use SYSTEM_MODE(MANUAL); would that not turn advertising off unless specifically turned on in code? Of course like you said once you have deep sleep implemented on the interrupt sleep function this is all moot.

I’m just sitting here looking at the system_sleep.cpp file and looking at the differences between system_sleep_pin_impl and system_sleep Ill be interested to see what you do here. I am a bit lost :slight_smile:

Thanks @eric!


#7

Ok, I added this. It is not fully supporting the syntax of the Photon, in other words, you cannot call the all in one command:

System.sleep(uint16_t wakeUpPin, uint16_t edgeTriggerMode);

Instead, you need to set an interrupt in setup() as such:

pinMode(D2,INPUT_PULLUP);
attachInterrupt(D2, nothing, FALLING);

Then, when you are ready to sleep:

System.sleep(SLEEP_MODE_DEEP);

This will set D2 with a PULLUP and trigger the wakeup on the FALLING edge. If you want to reverse this you can set a PULLDOWN and wake on a RISING edge.

I will work on getting the better option working so you can just call:

System.sleep(D0,RISING);

It may just take a bit longer


#8

Hi @eric,

Just to confirm am I really suppose to put in nothing as the callback function when attaching the interrupt? If so will this just cause the loop to run on wake up?

I’m thinking something like this. Right?

#include "application.h"

//SYSTEM_MODE(MANUAL);

void callback();

/* This function is called once at start up ----------------------------------*/
void setup()
{
	//Take control of RGB so no power is lost there.
	RGB.control(true);
	//Make sure LED on D7 Line is off.
	digitalWrite(D7, LOW);

	pinMode(D2, INPUT_PULLUP);
	attachInterrupt(D2, callback, FALLING);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	delay(5000);
	System.sleep(SLEEP_MODE_DEEP);
}

void callback(){
	//Do nothing for test purposes.
}

#9

With this code I am drawing 4.4 mA at all times. Seems SLEEP_MODE_DEEP is not functioning as expected.(Edit) Don’t forget to flash the system part stupid :slight_smile: Working now. Will check in after further testing


#10

Glad it is working. Yes, the callback function remaining blank is fine, I just need to merge these two functionalities into one to get the better syntax working. Soon you will just call:

System.sleep(D2,FALLING);

I just wanted to get you what I had so far


#11

Hey @eric,

I hit a snag with the System.sleep(SLEEP_MODE_DEEP); Really weird one to.

I had trouble explaining the issue so I posted a video demonstrating the issue here:

Here is my code(simplified as much as possible):

#include "application.h"

//SYSTEM_MODE(MANUAL);

int previousStatus = LOW;

void callback();

/* This function is called once at start up ----------------------------------*/
void setup()
{
	//Take control of RGB so no power is lost there.
//	RGB.control(true);
	//Make sure LED on D7 Line is off.
	digitalWrite(D7, LOW);
	Serial1.begin(115200);

	pinMode(D2, INPUT_PULLUP);
	attachInterrupt(D2, callback, CHANGE);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	int currentStatus = digitalRead(D2);
	if(previousStatus != currentStatus){

		if(currentStatus == LOW){
			Serial1.println("LOW");
		}else{
			Serial1.println("HIGH");
		}
	}
	previousStatus = currentStatus;			//This Lines does not work.  What gives!!!!
	delay(30);

	System.sleep(SLEEP_MODE_DEEP);
}

void callback(){
	//Do nothing for test purposes.
}

Any ideas?

Thanks @eric


#12

Would you not need a retained variable for that? I’m not 100% sure how the deep sleep works on the bluz, but on a core/photon after waking from deep sleep the normal variables are gone


#13

Yes, @Hootie81 is correct. Deep sleep is basically like a reset, same as on the Photon. The board completely reboots when it is awakened and all variables would be reset to their initial (startup) state.

If that isn’t what you need we can try to figure out some other options. Perhaps the best thing really would be to disable BLE and stay in a CPU sleep mode, which means variables will be retained. This mode isn’t quite as low in power consumption, clearly, but should still be very low.

Let me know what you are thinking and what you may need, we can probably figure something out for your use case.


#14

Thanks @Hootie81 and @eric

That was what I figured was going on. Thank you for confirming that.

I do need to retain that value essentially so I can determine the current status of inputs compared to previous status. This way I know if the input opened or closed.

I am thinking about storing previous status in EEPROM and evaluating the current status against that. Any thoughts on that? Do you think this is a bad idea because of memory wear?

I am seeing extremely low power consumption with Deep Sleep which is exactly what I wanted. Best I can tell with my equipment it is down around 25-30 uA which I believe is simply due to the inputs being pulled high mostly.

Thanks guys


#15

EEPROM support is currently on our list, but there are other ways to store in the short term. The SPI Flash can also work, as long as it is done very carefully.

At the moment, there is no way to persist data across power reset, but we are working on that quickly!


#16

Haha. I’m glad to hear you say that because I was trying to store to it this morning and could not get it to work. I thought I was doing something stupid :slight_smile:

Well in that case I an another alternative I could try(MCP23008 on I2C port). It has an interrupt line and captures input change in a register. I will pursue this alternative in the mean time. I will have to evaluate the current consumption of the chip to make sure it will work for this application.

Any sort of idea on EEPROM support ETA? I know you have your hands full!

Thanks @eric


#17

You are officially the third person to ask me for it recently, so it is quickly getting bumped up the list. I will try and take a look quickly and get something to you this week (unless I fond some blocking issue, but I don’t think that will be the case).


#18

Thanks @eric,

Also I do not believe I2C is working after wake from deep sleep. Is this a possibility? I seem to be able to read from it but write does not seem to be working which makes no sense. Also noticed that Wire.isEnabled() always returns true so I posted an issue on the Github repo for that.

Any idea why I2C would not be fully initialized after wake or am I crazy? My code works if I remove sleep. I also added delays after Wire.begin after waking to see if that was taking a long time but doesn’t seem to be the case. Ive got an I2C EEPROM module connected.


#19

My mistake. Please disregard previous post. I am having a hard time remembering that setup runs after wake from deep sleep.


#20

Hi @eric,

Is it possible to set the wake on interrupt on multiple pins? My code is showing that it is only possible to wake on interrupt from one pin. Can you confirm or deny this? This is my code as simplified as possible:

#include "application.h"

int input[5] = {D2, D3, D4, D5, D6};

void callback();

/* This function is called once at start up ----------------------------------*/
void setup()
{
	digitalWrite(D7, LOW);
	Serial1.begin(115200);

	for(int i = 0; i < 5; i++){
		pinMode(input[i], INPUT_PULLUP);
		attachInterrupt(input[i], callback, CHANGE);
	}

}

/* This function loops forever --------------------------------------------*/
void loop()
{
	Serial1.println("Loop Ran");
	delay(30);
	System.sleep(SLEEP_MODE_DEEP);
}

void callback(){
	//Do nothing for test purposes.
} 

With this code the module only wakes on D2 change. It seems to not attach the interrupt on the other pins.