Wake on interrupt


#21

There may be a bug there, I can verify later.


#22

Thanks @eric Sorry to drag another one of these out :confused: It is becoming a pretty cool feature though! Talk about low power!


#23

No worries! People digging in and finding bugs is part of the game, and makes the product better for everyone. This is why I am here!


#24

Hey @eric,

Please excuse my grittiness but have you had a chance to look into the bug on attaching multiple pins to the wake interrupt?
Thanks Eric


Minimising power usage
#25

Sorry, been a bit of a crazy week with the gateways coming in. I will try and get to that by Monday.


#26

Hey @IOTrav! Sorry for the delay here, but this issue is now fixed. It was a simple change in the nrf51 driver that restricted GPIOTE to only 1 pin. It is now set to 6, so you can enable interrupts on up to 6 pins at a time.

I verified with your code and can now use D2-D6 to wake up the board.


#27

Hi @IOTrav, I’m wondering about your field wiring for this. In my current (no pun intended) configuration, I’m using NO magnetic switches that are closed when the garage door is in the closed position. I had assumed (hopefully, incorrectly) that this would result in current flowing all the time if I had an INPUT_PULLUP configuration.

In my case, I briefly turn on a digital output to provide voltage to the switch which is then connected to a INPUT_PULLDOWN port. Feedback from this forum indicated to me that it was a very energy-conscious technique. However, I’ve love to be able to simplify the code and utilize interrupts as long as the battery life is extended through that approach.


#28

Hi @ctmorrison

What you are describing is very close to what I am working on. In my configuration I wake the Bluz module any time the status of the input changes. This means if your switch goes from closed to open or from open to close the module will wake up and run setup and then the main loop. This should work for your application.

If you’re just using a NO magnetic switch then I would not worry about wetting it with a voltage. Just set the input to pull up and wire the switch to pull the input to ground. This will wake the module and run your code.


#29

Hey @eric,

Thank you very much for the update. It is working(Wake on interrupt, multiple pins). Sorry I did not chime in sooner. I am in the process of determining a wake on interrupt failure rate. I have seen it fail a few times but am setting up a long term test so others can know how reliable the wake on interrupt is. It works quite well but I am a stickler for in depth testing :slight_smile: I figure that is the least I can do since you have worked so hard on this(in depth testing).


#30

Hey @eric

As promised I setup an in depth failure rate test for the bluz module interrupt. I simplified and optimized the code in both the Bluz module which I am firing the interrupt on and the Photon module which is acting as the controller in this setup.

Here’s the idea. I use digital lines connected between the Photon and the Bluz module. D2 on the Photon is set as a digital output. That is connected to the Bluz module’s D2 pin which is set as an input and also used as an interrupt line to wake the Bluz module up from deep sleep on pin change. This allows the Photon module to wake the Bluz module by simply changing the state of it’s D2 line.

I then set the Photon module’s D3 and D4 lines as pulled up inputs. These are connected to the Bluz module’s D5 and D6 pins which are set as outputs.

When the Bluz module wakes up it checks the state of it’s D2 input to see if it is HIGH or LOW. If D2 is HIGH then it sets D6 LOW and then sets D5 HIGH. If D2 is LOW on wake up then it sets D5 LOW and then sets D6 HIGH.

After the Photon module sets its D2 output HIGH it starts monitoring it’s D3 input and waits for it to go HIGH. As soon as it goes HIGH the Photon module knows that the interrupt on the Bluz module fired correctly. When the Photon module set’s its D2 output LOW it starts monitoring it’s D4 input, once that goes HIGH then it knows the interrupt on the bluz module fired correctly.

Here is a photo of the wiring between the Photon and Bluz module(USB on Photon is used for powering the whole setup as well as logging of status information):

Here is the code written in the Bluz module:
// This #include statement was automatically added by the Particle IDE.
#include “application.h”

/* Function prototypes -------------------------------------------------------*/
void callback();

/* This function is called once at start up ----------------------------------*/
void setup()
{
	pinMode(D2, INPUT_PULLUP);
	attachInterrupt(D2, callback, CHANGE);
	pinMode(D5, OUTPUT);
	pinMode(D6, OUTPUT);
	digitalWrite(D5, LOW);
	digitalWrite(D6, LOW);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	if(digitalRead(D2) == HIGH){
		digitalWrite(D6, LOW);
		digitalWrite(D5, HIGH);
	}else{
		digitalWrite(D5, LOW);
		digitalWrite(D6, HIGH);
	}
	System.sleep(SLEEP_MODE_DEEP);
}

void callback(){
	//Do nothing for test purposes.  After this Setup and loop will run.
}

Here is the code in the Photon module:
unsigned long tOut = 1000;

unsigned long onStart = 0;

unsigned long offStart = 0;

unsigned long totalCycles = 0;

unsigned long failCycles = 0;

unsigned long successCycles = 0;

unsigned long tTally = 0;

SYSTEM_MODE(MANUAL);

void setup() {
    pinMode(D2, OUTPUT);
    pinMode(D3, INPUT_PULLUP);
    pinMode(D4, INPUT_PULLUP);
}

void loop() {
    for(int i = 0; i < 100; i++){
        onStart = millis();
        digitalWrite(D2, HIGH);
        while(digitalRead(D3) != HIGH && millis() < onStart + tOut);
        if(digitalRead(D3) != HIGH){
            //failure
            Serial.println("On Failed");
            failCycles++;
            totalCycles++;
        }else{
            //Success
            successCycles++;
            tTally += millis()-onStart;
            totalCycles++;
        }
        delay(1);
        
        offStart = millis();
        digitalWrite(D2, LOW);
        while(digitalRead(D4) != HIGH && millis() < offStart +tOut);
        if(digitalRead(D4) != HIGH){
            //failure
            Serial.println("Off Failed");
            failCycles++;
            totalCycles++;
        }else{
            //Success
            successCycles++;
            tTally += millis()-offStart;
            totalCycles++;
            
        }
        delay(1);
    }
    Serial.printf("Average Time: %i \n", tTally/100);
    Serial.printf("Total Cycles: %i \n", totalCycles);
    Serial.printf("Failed Cycles: %i \n", failCycles);
    Serial.printf("Success Cycles: %i \n", successCycles);
    Serial.printf("Failure Rate: %.2f \n", ((double)failCycles/totalCycles)*100);
    tTally = 0;
}

The test setup is still sitting here running and I am up to 7400 total cycles without a single failure. Average time from the Photon module triggering the input on the Bluz module to the Photon detecting a successful wake interrupt on the Bluz module is right around 672mS.

Hopefully this can act as a test case for wake on interrupt on the Bluz module.

If anyone has any ideas on optimization for the test code above please let me know.

Thanks @eric and great job on this new feature addition. I should also note that with my equipment I am only seeing about a 14-16 uA current draw while the Bluz module is in deep sleep at 3.3VDC. By my calculations that means 2 AA batteries could power the Bluz module in deep sleep for around 22 years or so :joy:


#31

20K successful interrupt cycles with 0 failures. I am calling this good :slight_smile:


#32

@IOTrav You mentioned the need for EEPROM support, well we just added this into the develop branch. You can see the post here: [SOLVED] EEPROM Not Supported


#33

Great @eric! I can remove my I2C memory module from the design! :slight_smile: I will try to check out the EEPROM feature in the develop branch this week. I’ll try to put it through the ringer and let you know.


#34

Hello @eric, This thread seems to contain some of the issues I am researching. I would like to verify that this thread and others are still relevant before I get too far.
I want to enhance robustness by adding a watchdog to reset the Bluz in case it gets stuck. The watchdog looks for I/O activity and resets the Bluz if more than 6 hours have passed without activity.
I also would like to minimize current draw and make the Bluz respond to a network request from a gateway within 1 second.

My first question is which I/O lines support wake up on edge change? The nRF51 reference says all of them. This thread indicates up to 6, so I am not sure if any particular ones.

Is the logic level of the I/Os preserved during sleep?

As long as I keep the radio on (not using deep sleep), network requests will wake up the Bluz. Then the Bluz will restart the loop() function. Presuming the loop() is short, can the Bluz return a variable within 1 sec?

This thread did not conclude the discussion. Is it possible to sleep for any amount of time?

If I were to use deep sleep, would the response time to the network be as long as if just powered up, up to a minute? Just planning ahead in case the consumption is still high (haven’t decided on battery yet).

Finally, I am curious about the EEPROM.
If I decided to go with deep sleep, I would need to save some data in EEPROM. How much space is allocated for this? The Photon docs say 2047 bytes. Same for Bluz? Is that the size of the page?
I read in the docs that this memory is less prone to burnout. Is it because it is managed in pages? I expect to write 3 new values no more than 10 times per day.

Is reset the same as waking from deep sleep? I am not clear on what mechanism restores data from EEPROM and if reset can also do that.
EDIT: Thinking about the reference docs, it seems this is a manual put()/get() process, so I don’t understand how to manage this just before sleep and after wakeup.

Sorry for peppering. I could not find these answers in your low power tutorial.

Thanks,
Pescatore
PS: You might be wondering what makes the Bluz get stuck. That’s a topic for another day. I want to send my board to fab and I can remove the watchdog later.


#35

You can wake on any pin, you can only enable 6 at a time, however. So it doesn’t matter which ones, just don’t do more than 6.

When bluz goes into deep sleep, it is basically the same as a power down. All internal variable states will be lost, except for data stored using EEPROM. When the board wakes, it will run your setup function again.

If you are using System.sleep(SLEEP_MODE_CPU), then the CPU of the board is asleep. It will only be woken up by BLE radio events, software timers, interrupts, or the systems timer. The system timer fires every 100 mSec and is used to check for data from the cloud, reset the WDT, and perform other system functions. When you ask if bluz can return a variable within 1 second, I am not sure what you are timing from as that would be an external request that involves overhead from the cloud and the requester.

In deep sleep mode, you can sleep for any amount of time.

Deep sleep is basically the same as a power reset. So waking up involved reconnecting to the cloud, which does take time.

Bluz currently offers 512 bytes of EEPROM storage.We may be able to increase this from looking at it, but that would have to be tested. Our EEPROM is stored on the external SPI flash which is rated at 100,000 cycles. Each time any variable is written we write the entire flash space, so grouping data into single writes will lessen the wear on the flash space.

EEPROM data will only be lost if you explicitly overwrite it or erase the space. It will not be lost on a reset or deep sleep cycle.


#36

Thanks for the response.
Ok, so the SLEEP_MODE_CPU can only sleep for 100ms because of housekeeping?
Are the I/O logic levels preserved during this sleep mode?

The 1 second qustion is about responding to a request from an Alexa skill. This may prevent me from using deep sleep, so I have to decide on a bigger battery and charging solution.

About the EEPROM, 512 bytes is plenty.
I guess I need to store the latest value of critical variables. Then the setup() function always assumes there was a reset or a deep sleep.
Is that the usual practice?

Thanks,
Pescatore


#37

CPU Sleep is indeed interrupted every 100 mSec, but doesn’t do much if there is no Particle data waiting. All variables will be preserved in this state, nothing gets reset, the CPU just shits off.

Yes, that is the normal floe, store critical variables and read them in setup(). That way you always have the right values loaded after a deep sleep.


#38

When the CPU shuts off is the voltage level of the I/O pins preserved, or does it toggle hi/lo with the CPU?
Can the SLEEP_MODE_CPU be changed to sleep longer? 1 second?

Thanks,
Pescatore


#39

When the CPU is asleep, everything else remains. Really, the CPU just stops processing instructions, but the state of all peripherals and RAM is preserved.

Right now that time is not configurable. However, I don’t think it will cause a major current draw. When the CPU does wake up, it is probably on for microseconds as it does some quick housekeeping and then goes back to sleep. Your loop function will run there as well, so if that does a lot it will delay the time before the CPU shuts off again. But we need that housekeeping to happen on a pretty regular interval


#40

Got it!
As always, thank you for your time.
Pescatore