System.sleep() STOP mode and IRQs

Greetings. I’m trying to determine if the STOP mode will go back to sleep after an IRQ triggers a wake-up but before the timer expires. I’ve got it setup like so:

    JustAsec.mode(SystemSleepMode::STOP)
        .flag(SystemSleepFlag::WAIT_CLOUD)
        .duration(1000);

And then using:

System.sleep(JustAsec);  

In my loop to put the CPU into a lower power mode. I tried ULP mode but too much was turned off and I need IRQs to work. So the crux of the question is when an IRQ fires, will the CPU go back to sleep? I’ve found the following from an external site that suggests it’s a possibility:

HAL_PWR_EnableSleepOnExit ();

This is from https://controllerstech.com/low-power-modes-in-stm32/

Thanks!

@BenCranston ,

There are a number of us who are focused on using sleep to lower power consumption. Based on what you wrote, I think the answer is:

  1. I use ULP mode and hardware and timing interrupts to wake the device. You may want to take another look at this mode once you have worked out your example on STOP mode sleep. Note, the more information you provide, the better the answers. Some sleep questions will change based on whether you are using a Generation 2 (Electron, Photon) or 3 (Boron, Argon) device.

  2. Once the device wakes from sleep (in your example after a second), it will stay awake until you tell it to sleep again. So, if you set up an interrupt, you could initialize a timestamp, service the interrupt and then go back to sleep either immediately or after a delay from your timestamp. So, to answer your question, yes it will go back to sleep - but only if you tell it to.

Does this answer your question?

Thanks,

Chip

1 Like

Chip,
Thanks for the info. It does help. I’m on a Photon, so I guess that’s Get 2. The application is a weather station. I’ve got two different IRQ services, one for anemometer and the other for rain gauge. The Photon must wake for these events to record them. I also need the timer that drives the millis() function to stay active as it’s used throughout. That’s why I gave up on the ULP mode as all timers stop hard upon entering ULP sleep. I’ve got i2c devices I sample on every N seconds. Temp, Humidity, Pressure, and power measurements for the battery. This whole thing is solar powered and I have a 3 cell 18650 pack to keep it functioning when the sun is down. I managed 36 hours of battery power by sleeping for 1 second with STOP mode each cycle of my processing loop. I’m trying 2s of STOP sleep right now to see if it changes the battery dynamics any.

At some level I almost want to switch the whole thing over to IRQ based actions. Sleep until woken by a sensor. Then on a timer interval of 5 minutes fire an IRQ to send the current telemetry over WiFi to the 'net.

I’d really like to get several days of power outta the battery pack as we often have cloud cover days and I’m not expecting lots of full charge sunny days back to back.

Ok, I’ve re-read your reply. So…

I enter STOP sleep
IRQ fires
CPU wakes and processes
CPU stays away until next time we STOP sleep

So I need to have logic to address the case of _was_sleeping_rude_IRQ_woke_me and _not_done_sleeping_yet where we fire off another STOP sleep. However STOP really only has 1s resolution so gotta do some maths on how much to sleep or just dummy delay() to true up the time gap. Or maybe I’m over thinking this and I should just immediately go back to STOP sleep after any IRQ is serviced…

@BenCranston ,

Couple clarifying questions/ statements:

  1. sleep time duration resolution is the millisecond
  2. no reason why you cannot do this but, from a practical standpoint, why are you sleeping for only a second. Typically, sleep periods are much longer and more power benefits accrue when you sleep for 10 minutes or more and can power down the cellular modem.
  3. you are correct about having to calculate the time till next wake / sleep cycle. There are some neat ways of doing this if you would like help here.

Does this make sense? Happy to help you get to the finish line.

Thanks,

Chip

Yes and Please and Thank You! I’d love some help getting this to the finish line.

  1. yes, the .duration() resolution is in ms. Correct. However https://docs.particle.io/cards/firmware/sleep-sleep/duration-systemsleepconfiguration/ says for Gen2 devices it’s only 1s for resolution and the minimum is 1s. Am I reading that correctly?
  2. I’d love to sleep longer and I believe it makes sense to do so. However all of the code is based on a 1s loop for timing of the wind speed and calculations for percip rates. I’d have to revisit all of that for a longer period. I do want an update once every 5 minutes. It’s a Gen2 Photon so I’m just on the WiFi for communications.
  3. I was worried about that… Love to hear how you are working the sleep/wake cycles. I already have an external ATtiny on the board to manage low power cut off to protect the battery. The main loop goes into a HIBERNATE stop waiting for WKP assertion. The ATtiny checks the power from the solar and if sufficient and we are charging the battery, then it asserts WKP to get the photon back and running.

I’m almost thinking that I might be best served to have the everything based on IRQs waking the system up to do something. Dunno. That’d be a substantial re-working of the code, but yet possible. What’s the state of the art on this? I’ve been meaning to re-code the main loop into a state machine.

Thanks again for the offer to help out. What’s the next step? I can share the code (its a mess) somewhere if that makes sense.

-Ben

@BenCranston ,

OK, so recall I said the answers will vary based on the device you are using…

  1. Yes, you are correct, Gen 2 devices such as the Electron and Photon cannot sleep for less than a second. However, they can, sleep longer. Here is an example of what that might look like for a device that wakes once an hour:
// before setup
const int wakeBoundary = 1*3600 + 0*60 + 0;         // 1 hour 0 minutes 0 seconds

// In the main loop - where we put the device to sleep:
    int wakeInSeconds = constrain(wakeBoundary - Time.now() % wakeBoundary, 1, wakeBoundary);
    config.mode(SystemSleepMode::ULTRA_LOW_POWER)
      .gpio(userSwitch,CHANGE)
      .duration(wakeInSeconds * 1000);
  1. For battery based solutions, sleep is essential. Waking every 5 minutes, taking a reading and then going back to sleep makes sense. In my code, I have a very short period of time to wake when making a measurement but will stay awake longer at the top of each hour so I have a better chance of catching the device remotely when I know it will be on-line.

  2. It sounds like you are already thinking about many of the right things. Using interrupts is a great way to reduce power consumption between readings to a minimum.

I think you will find many members of the Particle community are glad to help. If you like, you can see my code (which uses sleep and a finite state machine) and please feel free to ask questions - that way everyone benefits.

Thanks,

Chip

Wow! That’s some good code. Well commented and readable. Thanks for sharing.

I think the trick is going to be the timing logic between inputs. I’m using the SparkFun Photon Weather Shield https://learn.sparkfun.com/tutorials/photon-weather-shield-hookup-guide-v11?_ga=2.122824008.218167128.1597251796-1334959781.1596901392

So, on to questions:

  1. The code examples I find (and what I’ve implemented) have this kind of IRQ handler for wind speed:
void wspeedIRQ()
// Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3
{
  if (millis() - lastWindIRQ > 10) // Ignore switch-bounce glitches less than 10ms (142MPH max reading) after the reed switch closes
  {
    lastWindIRQ = millis(); //Grab the current time
    windClicks++; //There is 1.492MPH for each click per second.
  }
}

Which relies on an external loop cycle of 1s to resolve the windClicks into a speed. We get 2 IRQs per rotation and at 1s counting that results in 1.492MPH per IRQ per second.
So if I utilize the ULP mode, can I associate the IRQ pin as the .gpio() to wake from? And then will the IRQ actually fire in the code? I can handle the math for ticks per some sleep interval. :slightly_smiling_face:

  1. After a long sleep period, does it make sense to poll the i2c sensors multiple times and average out the result? I’m assuming the sensors are basically stable so one reading should suffice, but want to implement best practices.

  2. I’m concerned about the system.sleep() times as in the time it takes to get into the sleep mode and to resume processing. Some simple math an at a 35MPH wind gust we should expect ~53 IRQs in a second. That’s not a long time to slide in and out of sleep. Steady state could be ~2 IRQs a second for ~3 MPH wind (not uncommon for long periods)… Did I just talk myself out of ULP and into STOP?

I wonder if others who’ve implemented a wind speed sensor like this have thoughts on sleep modes? At some point I need to calculate wind gust values as well and I think that the longer I sleep the more averaged these values will get as I’m not capturing impulse like values in my samples. Thoughts? Compensations?

Thanks again for all the help!

Hi. Don't know if you have seen this tutorial. Very good write up. It might help some with your decisions/questions:
https://community.particle.io/t/learn-more-about-sleep-tutorial/61534

robc,
I started there before asking questions here. Yup, I’ve seen it. thanks. Good stuff and it helped a bunch. In my understanding the interrupts are turned off in ULP stop so it makes no sense to use that as I’ll miss the anemometer ticks while sleeping. At this point I’m planning on doing a STOP for 10s and then processing counters, pulling new sensor data, etc. Also, re-implementing the main loop into a state machine. then once every 5 minutes wakeup the WiFi and publish to the cloud. shutdown WifI and begin the counting down and STOP cycles again. Maybe at night I’ll switch from 5 minutes to 10 minutes for cloud updates. Save a bit more power when the sun is down and we are totally reliant on the battery.

@BenCranston ,

Sounds like you are making some progress. In learning more about your use case, I would recommend you consider the following:

  1. You need to decide what "resolution" you want to have on the wind measurements. While wind can change frequently, you typically assign a "wind" speed and direction as averages so you might consider the following approach:
  • disable wind interrupts
  • sleep for some time - based on your energy budget - 1-10minutes
  • wake and enable interrupts
  • spend a few seconds gathering data on wind speed and direction
  • Put these values into a "ring buffer" - test your way into the "depth" and this is related to the frequency of your samples.
  • Take an average of your wind speed and direction values from the buffer
  • Report your data
  • repeat
    Given you are using a Photon and you cannot sleep for less than 1 second, this is the only approach that will work for you. Still, it is likely how I would do it on a 3rd generation device too.
  1. While not needed in the approach above, I wanted to clarify one thing:

In my understanding the interrupts are turned off in ULP stop so it makes no sense to use that as I’ll miss the anemometer ticks while sleeping.

Hardware interrupts are allowed in ULP mode.

  1. As for your other i2c devices, whether you need to sample and average depends on the device. In my experience, if you give the i2c device enough time to wake, you can get an adequate measurement. This is often a better approach (wait, then sample) than trying to average out an incorrect measurement that was taken before the device is ready. This is the case as the "not ready" measurements can often be at the extremes of the measurement range.

I hope this is helpful and, based on the other folks responding to this thread, you will likely find significant interest in your project as you continue to evolve it.

Thanks,

Chip

1 Like

Chip,
I took your advice and did the ULP sleep. My loop was for 5 minutes, wake up read sensors and post, then right back to sleep. I managed ~100 hours on a single charge that way. I’m still in the process of retooling the main loop into a proper FSM, but I now have the tools to make this work. Thank you!

I’d like to ask another clarifying question… So I made a comment on the ULP mode not allowing interrupts. I think I got that in my head as the millis() timer interrupt is apparently halted during ULP, so I thought it applied to all interrupts. I see that interrupts will bring the CPU out of ULP. I’m going to leverage Time.now() mechanisms from the RTC to see if I can allow interrupts to wake, check that the RTC time is not what we expected, and go back to sleep for the remainder. Still thinking thru how to implement that.

Anyway, is my assessment of what is left running close? RTC is on, and enough of the I/O for a wakeup based on GPIO pin interrupt. Everything else is halted, correct? thanks.

@BenCranston ,

Very happy to hear you are making progress. ~100 hours on a charge sounds like you are on your way.

On your other question, I think you are on the right track:

  • In ULP sleep on the Photon (assuming you are current on deviceOS), millis and software timers stop while sleeping.
  • In order to wake from sleep using time, you need to use the RTC as we discussed above

In my code, I will take a timestamp upon wake and use that for program execution. So, it is RTC for sleep and millis() while awake.

BTW, there are other interesting ways to wake when you move to the generation 3 devices. With the Argon, you get an integrated LiPO battery charging capability that might be useful for your weather station.

Chip

1 Like

Morning all. I made another run to test the battery life by sleeping for 10 minutes, waking, taking and posting samples, and going back to sleep. It was a 27% increase over the 5 minute sleep test. Not bad, another day of power. At 10 minute sleep intervals it’s running for almost 5.5 days. I’m pretty sure I’ll pick up solar charge in that span and be able to keep on chugging.

Next test is with wind and rain sensors attached to see how much the IRQs are going to wake and cause additional power drain. Plan is to leave the wind IRQ off until it has woken for the sample cycle. However I’m going to leave the rain IRQ on. I think sampling the wind once every 9 minutes for a 1min is sufficient. However missing any amount of rain while in the sleep cycle I think is unacceptable. I’ll post my findings and also if I have any issues with the IRQs waking from sleep, etc.

1 Like

@BenCranston , For some applications (when you have the room), it’s easier to go with a 12V panel and SLA battery. Then your Power Budget isn’t nearly as tight.

You can pair this $33 Panel with a $15-$20 12V SLA battery:

You can add a simple Voltage Divider to measure the 12V battery if you want to track the 12V Panel & SLA battery’s daily performance.

This gives you Weeks of Backup Power, or you can leave the Boron ON most of the time without needing to sleep.

Another way to think of this combo is to not monitor the 12V SLA and only get concerned when your Boron’s Backup Li-Po starts draining. Then it’s time to institute you’re Sleeping Schedule.

If you’d rather stay with a 6V panel and Li-Po, you can use an external IC counter (that remains powered) to count the Rain and Wind Pulses. The Boron just needs to read the Counter IC on every wake cycle.

[Edit] @peekay123 kindly reminded me that the OP is using a Photon instead of a Boron. The Solar Charge Controller in the kit that I linked has a 5V usb output, so I assume it’s still a viable alternative.