Photon sleep mode wakeup on multiple interrupts?

I would like to put my photon in stop mode (to achieve low power and retain sram variables) but then wake up when one of my two defined interrupts occur. Is this possible?

Have a try reading this
https://docs.particle.io/reference/firmware/photon/#system-sleep-

If I am reading that correctly, the photon can only wake from a SINGLE interrrupt pin. So waking from either of two interrupts wont work, correct? @ScruffR

That’s right.

The moment the function System.sleep(xxxxx) is called, the device immediately goes to sleep and the way it is currently setup is to take only one pin as the triggering pin.

1 Like

That’s correct - at least with the current firmware.

I don’t think it’s a restriction of the µC, but as you can’t send a sleeping device to sleep via code again, to hook up a second pin I fear so.
But @satishgn might have a word to say to this too.

On the other hand, if you can provide some more detail about your interrupt sources and your use case, there might be alternatives :wink:


Kenneth was quicker again :wink:

1 Like

I have a magnetic rotary encoder. 2 magnetic reed switches, each tied to an interrupt. I need to monitor each of those interrupts for change in order to properly calculate position. But movement happens very infrequently so I would like to have the device (battery powered) be able to sleep in a lower power consumption state. Any ideas how I might work that out?

Thanks again for the help

I think it’s sufficient to simply register the interrupts using attachInterrupt(). The STM32 will wake on any EXTI interrupts, not just the one configured with System.sleep().

I’m just concluding this from looking at the code, so I may be mistaken. Please try and let me know! :smile:

Thanks, will give this a try today @mdma

Hmm, I’m not sure that System.sleep will give us a low enough consumption rate in order to entertain powering the photon with batteries. We are seeing 3mA in that mode which is only a few weeks of time. Unfortunately that we lose the SRAM vars while in deep sleep mode, because that does sleep low enough to do the battery powered thing.

@mdma, when are you anticipating adding support for assigning vars in the VBAT backed SRAM area?

@peekay123 @mdma that would be awesome! I really only need to track a single “position” variable of the encoder, being able to do that in deep sleep would be perfect.

If anyone has any ideas of low power ways to be able to store/retrieve something like that, even if it involves a separate chip, I am open to suggestions. I’d really rather keep this product based on the photon/electron as opposed to rolling something totally custom.

So do I understand correctly that both reed switches are on the same encoder and usually both will be triggering shortly after each other anyhow?
How is your expected timing for the impulses?
Will there be more than one trigger for each movement event?

As I picture the thing, I wouldn’t rely on the wake on trigger to be fast enough to catch the corresponding trigger of the other switch, but if you can expect multiple events, it wouldn’t matter if you only cought the second trig, but can measure the direction by subsequent events.

But if you absolutely need to trigger on either of the two, you might be able to tie both signal lines together (e.g via an OR gate or just some diodes) and connect them to a third (dedicated wake) pin.


Oops, when I answered I didn’t see all the other posts sincs post #6 :blush:

2 Likes

Only problem with my thinking (that all I need is a way to store a var that will persist in deep sleep mode) is that the only way to wake up would be to use the WKP pin, as the other interrupts will not wake the photon from deep sleep (I tested this). So I am back to the issue of needing to wake from deep sleep on multiple interrupts.

On a postive note. @mdma I did test and indeed if you are in normal sleep mode any/all of the interrupts that you set before you went to sleep will still fire.

You could in theory wire the reed contacts to both the resp. pin for detecting the interrupt and the WKP pin. Thus it would wakeup the Photon from deep sleep and you could find out which pin was triggered: However: For waking up from deep sleep the Photon actually needs several seconds! I guess, that won’t cut it for you. That would only detect very slow motion :smile:.

Here is a different proposal: Take an Attiny 45 which is very cheap ($1). The Attiny is pretty easy to program (using the Arduino IDE without any special hardware) and can be put to deep sleep where it consumes < 0.1 uA. Even in that mode it still retains the RAM. And it can still be woken from multiple interrupts sources. So it can count the interrupts easily.

You can configure the Photon to wakeup from time to time, ask the Attiny via serial for the number of counts it has, send it to some cloud (or whatever you need the Photon for) and let it go to sleep again.

1 Like

Thanks @Stevie for the recommendation, we’re exploring a solution along the same lines that you recommended. To clarify, the ATTiny would have to be awake to listen/respond to a serial command from the photon right?

I thought about something similar to your suggestion but to put between the photon and attiny one of the low power I2C devices which can store/maintain memory with extremely low consumption ~(1uA), this way the power hungry devices would simply wakeup and read/write to that single low power memory store.

The Attiny could sleep and be woken by triggering an input line from the Photon to the Attiny. Then it would be reachable via Serial.

You could also immedately let the Attiny wakeup the Photon when something happened and when the Photon is ready send the information to the Photon (of course continuing to count in the meantime).

The Attiny maintains the RAM even in the case of the lowest power mode and thus the separate I2C device should not be necessary.

There's no concrete date for this, but we'll try and make it available before the end of the year.

I imagine a special modifier on variables:

retained int myvar = 3;

Which would be initialized to 3 on hard reset, but keeps it's current value on subsequent powered resets / deep sleep wakeups (so long as VBAT has power.)

I can do the linker magic that places these variables into the backup SRAM - if there's anyone from the community that wants to help write the code to enable the backup domain and to help test, that is very much welcomed. :smile:

1 Like

I did a little test and found the following userland code to work fine:

#include "application.h"

#include "stm32f2xx_pwr.h"

SYSTEM_MODE(MANUAL);

static __IO uint32_t* BACKUPRAM = (__IO uint32_t*)BKPSRAM_BASE;

void enableBackupSRAM()
{
    Serial.println("Enabling backup SRAM");
    // Switch on backup SRAM clock
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
    // Disable write protection
    PWR_BackupAccessCmd(ENABLE);
    // Switch on backup power regulator, so that it survives the sleep mode
    PWR_BackupRegulatorCmd(ENABLE);

    // Content of SRAM is random after power up, so do some magic number check
    if (BACKUPRAM[0] != 0xdeadbeef)
    {
        Serial.println("Initializing backup SRAM");
        BACKUPRAM[0] = 0xdeadbeef;
        BACKUPRAM[1] = 0;
    }
}

void setup()
{
    Serial.begin(115200);

    enableBackupSRAM();

    Serial.println("Reading backup SRAM");
    uint32_t counter = BACKUPRAM[1];
    Serial.println(counter);

    Serial.println("Writing backup SRAM");
    BACKUPRAM[1] = ++counter;

    Serial.println("Sleeping to allow connecting to Serial");
    delay(10000);
}

void loop()
{
    Serial.println("Going to deep sleep");
    System.sleep(SLEEP_MODE_DEEP, 2);
}

It will increment a counter in backup SRAM and go to deep sleep.

Personally I think no linker magic should be required. Maybe just a call to enable the backup SRAM and another to get its location?

2 Likes

Thanks for the proof of concept!

So you want to work with raw bytes in backup ram? That's of course always a possibility.

But I was thinking it would be more pleasurable to code by using real datatypes, e.g.

retained   time_t time;
retained   unsigned count;
retained   int set_to_one = 1;

So the system takes care of the cold-start initialization using the provided initial values, and avoids re-initializing when doing a warm start.

In loop you only then need:

void loop()
{
     counter++;
}

For easier distinction of nonvolatile and volatile variables, you could put them in a struct

retained struct BackupData {
   time_t time;
   unsigned count;
   int set_to_one;
} backup = { .set_to_one = 1 };

void loop()
{
    backup.counter++;
}

The linker will also ensure all your data fits, rather than having to manually code some upper limit for each device. So the code will be more portable between different devices that support backup RAM.

Thoughts on that?

1 Like