Sleep mode for MKR1000 using RTCZero

I am working on a project with MKR1000, that should acquire dht22 data every 15minutes and send message to thingspeak. Everything works fine, but the battery life doesn't go longer than 8 hours. So I tried to implement some sleep logic, starting from the example of RTCZero library (https://www.arduino.cc/en/Tutorial/SleepRTCAlarm). However it is not clear to me how the library can be used to wake up the board every 15 minutes. If I understood correctly, the alarm can wake the board when the rtc.setAlarmTime matches with the time of the RTC. So if I use rtc.MATCH_MMSS, where MM is 15 and SS is 00, the alarm will wake the board every first quarter of a hour and not on 30, 45 and 00 minutes. Is it correct?

Maybe this is very easy, but I am not expert of this topic.

thanks

Hi, you are on the right way, but with a trick. :slight_smile:
You have to use the MATCH_MMSS but in the callback attached to the alarm you have to shift the alarm.

When the alarm is matched you have to use the setAlarmTime function in such a way:

void setup(){
  ....
  ....
  setAlarmTime(xx, 0, xx); //your starting time
  ...
  ...
}

void alarmMatch(){
  int alarmMinutes = rtc.getMinutes();
  alarmMinutes += 15;
  if (alarmMinutes >= 60){
    alarmMinutes -= 60;
  }

  setAlarmTime(rtc.getHours(), alarmMinutes, rtc.getSeconds());
}

a_guadalupi:
Hi, you are on the right way, but with a trick. :slight_smile:
You have to use the MATCH_MMSS but in the callback attached to the alarm you have to shift the alarm.

When the alarm is matched you have to use the setAlarmTime function in such a way:

void setup(){

....
  ....
  setAlarmTime(xx, 0, xx); //your starting time
  ...
  ...
}

void alarmMatch(){
  int alarmMinutes = rtc.getMinutes();
  alarmMinutes += 15;
  if (alarmMinutes >= 60){
    alarmMinutes -= 60;
  }

setAlarmTime(rtc.getHours(), alarmMinutes, rtc.getSeconds());
}

Thanks for the trick. But there is something strange, it seems not executing more than one line in the alarmMatch void. For example I tried led blinking every minute and it does not switch it off. The board put led HIGH at first alarm and doesn not put it LOW after delay(500). Here is the example:

#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 05;
const byte hours = 12;

/* Change these values to set the current initial date */
const byte day = 12;
const byte month = 01;
const byte year = 17;

void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

rtc.begin();

rtc.setTime(hours, minutes, seconds);
rtc.setDate(day, month, year);

rtc.setAlarmTime(12, 06, 00);
rtc.enableAlarm(rtc.MATCH_MMSS);

rtc.attachInterrupt(alarmMatch);

rtc.standbyMode();
}

void loop()
{
rtc.standbyMode(); // Sleep until next alarm match
}

void alarmMatch()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
int alarmMinutes = rtc.getMinutes();
alarmMinutes += 1;
if (alarmMinutes >= 60){
alarmMinutes -= 60;
}

rtc.setAlarmTime(rtc.getHours(), alarmMinutes, rtc.getSeconds());
}

Hi, actually is a good practice to have an interrupt routine shorter as possible (with no delay!!!). So you should do something like this:

#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 5;
const byte hours = 12;

/* Change these values to set the current initial date */
const byte day = 12;
const byte month = 1;
const byte year = 17;

bool matched = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  rtc.begin();

  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(day, month, year);

  rtc.setAlarmTime(12, 06, 00);
  rtc.enableAlarm(rtc.MATCH_MMSS);

  rtc.attachInterrupt(alarmMatch);

  rtc.standbyMode();
}

void loop() {
  if (matched) {
    matched = false;
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);

    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
    int alarmMinutes = rtc.getMinutes();
    alarmMinutes += 1;
    if (alarmMinutes >= 60) {
      alarmMinutes -= 60;
    }

    rtc.setAlarmTime(rtc.getHours(), alarmMinutes, rtc.getSeconds());
    rtc.standbyMode();    // Sleep until next alarm match
  }
}

void alarmMatch() {
  matched = true;
}

Great!!

Actually the delay was a problem in the alarmMatch. I tried your solution and it works fine. I have also included a call to a data log function (connect to the wifi and send T/RH to thingspeak) between the two led blink and it works perfectly.
Next step is to test the battery life with this upgrade. I will post results asap.

Thanks!

Hi

Would be interesting to hear about your findings.
I work on a very similar project (with a different temperature sensor) and found the battery life also not very useful.

I solved the wakeup very similar to what @a_guadalup suggested;

int alarmminute = 15 ;  //start time for alarm
const int alarminterval = 15 ;  //period to take temp in minutes   


in loop: 
    alarmminute= (alarmminute+alarminterval) % 60;
    rtc.setAlarmMinutes(alarmminute);
    rtc.standbyMode();

only setting AlarmMinutes will make sure you wake up at fixes intervals and not after a fixed interval.

For sending data to thingspeak you will need the wifi board. This uses much more power than the MKR1000 itself. make sure to to use WiFi.disconnect() before entering into standby mode.
Power consuption is still not perfect (I read one could go down to 2 mA), but I have not figured out how.

Uncle82:
Great!!

Actually the delay was a problem in the alarmMatch. I tried your solution and it works fine. I have also included a call to a data log function (connect to the wifi and send T/RH to thingspeak) between the two led blink and it works perfectly.
Next step is to test the battery life with this upgrade. I will post results asap.

Thanks!

Hi uncle82,
do you have any news about the test of the battery life with this solution?

Thanks
Daniele

Hello All,

I am measuring about 97mA after executing rtc.standbyMode() and I am measuring about 105mA during WiFi active. I am using USB power (and not a battery). Therefore, the POWER ON LED is illuminating continuously, and this is contributing to the current cosumption either case. According to the schematic, I believe this is DL3 and is likely adding about 10mA.

I tried to measure the current when using a battery (and not USB power). I don't have the right jump wires to do so. My two jump wires are two big for the female receptacle.

I have run an experiment in which the system is in rtc.standbyMode() for 15min, then wakes for about 10s to read a value and push it to my Google sheet. My battery's rating is 1200mAh @ 3.7V. I was able to run the system for about 12.5 hours. Using the battery rating and system specs, I expected a duration (1200mAh/(97mA - ~10mA)) = 13.79h. So my observation closely matches the math and it gives me high confidence.

Nevertheless, I do not 'feel' that 97mA after executing rtc.standbyMode(), even with the POWER ON LED illuminated, is a sufficiently low power consumption.
I was hoping for MUCH LOWER power consumption, maybe in the <10mA range... >:(

I would love to hear from others.
Are others also seeing similar power consumption?
Has anyone figured out a way to lower power consumption?

CowboySkippy

IIRC the firmware upgrade was supposed to fix some power issues.
You all did that right ?

Hello All,

On 20170818, one month ago, I measured 97mA after executing rtcStandbyMode() and 105mA during WiFi active.
I have made changes to my Arduino sketch and I now measure 1.25mA after executing rtcStandbyMode(). This is two orders of magnitude better that 97mA!
Below is pseudocode showing my changes:

void loop(){

  // if (myRTCAlarmExpired)
  {
    WiFi.noLowPowerMode();
    WiFi.begin(ssid,pass);
    delay(9000);
    // do stuff, such as read a sensor, post to PushingBox, etc....
    // set the rtc Alarm for the next interval...
    WiFi.disconnent(); // This is the line of code that dropped my current draw from 97mA to 1.25mA !!!
    WiFi.end();

  }

  rtc.standbyMode();
}

I am using the following library versions:

  • WiFi Built-In by Arduino Version 1.2.7 Installed
  • WiFi101 by Arduino Version 0.14.3 Installed

I confirm my MKR1000 ran 16 days, 19 hours and 8 minutes with a fully charged 1200mAh battery.
The average current draw, remember, is > 1.25mA because the circuit is drawing ~100mA when WiFi is active and transmitting data. I am very pleased that I am only drawing 1.25mA (vs ~100mA).

I am very pleased with my current performance.
But, I can optimize further:

  • Collect several sensor samples throughout the hour or day and enable Wifi once an hour or day and post them to my Google Sheet.
  • Minimize the about of time that WiFi is enabled. I am current looping several seconds with a generous safety margin and can probably lower that number. The current draw is maximal while WiFi is enabled.

Thanks for the feedback!
CowboySkippy

Do you have to add a wire on the board for it to be interrupted to wake up?

Can you share your code for how you did get it to ~1.5 mA current ?

thanks in advance

Do you have to add a wire on the board for it to be interrupted to wake up?

No, it's not necessary to add a wire to wake up the board.

To save power the RTC is clocked the low frequency 32.768kHz external crystal, while the rest of the processor is placed into deep sleep (STANDBY mode). The RTC is able wake up the processor after a predefined time by triggering its interrupt service routine (ISR).

It's also possible place the processor into deep sleep with the __WFI() (Wait For Interrupt) function and wake it up again with an external pin interrupt:

// Put the CPU core into deep sleep and awake with a logic (+3.3V) HIGH interrupt on D12
void setup() { 
  pinMode(13, OUTPUT);                          // Set digital pin 13 to an output
  pinMode(12, INPUT_PULLDOWN);                  // Set digital pin D12 to an input with an internal pull-down resistor
  attachInterrupt(12, interrupt, HIGH);         // Set up an interrupt on digital pin D12 on a logic HIGH level
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;            // Set up the CPU to enter low power STANDBY mode (rather than IDLE mode)
}

void loop() 
{
  delay(1000);                                  // Wait for 1 second
  digitalWrite(13, LOW);                        // Set D13 output to logic LOW
  __WFI();                                      // Put the SAMD21 in deep sleep (STANDBY mode), Zzzzzzz....
}

void interrupt()                                // Interrupt Service Routine (ISR)
{
  digitalWrite(13, HIGH);                       // Set D13 output to logic HIGH
}

I did not yet check for the reduction of power consumption, but @karliwalti @a_guadalup and all the other got as many +karma points as I could give because their help made it work for my Arduino MKR GSM 1400.

Like the others, I gather sensoric data and store it somehow. I actually chose the MKR ENV Shield together with an SD Card, because the Arduino IoT and Thingspeak did not work for me flawlessly. And stable measurements are actually much more important to my project then conveniently plot the values by a cloud IoT because RStudio can manage that for me.

Anyway here is what worked for me:

ofc you need to adapt to your project

....
//RTC for waking
RTCZero myRTC;  //now every command needs to start with "myRTC.commandhere();" this is because sometimes the word "rtc" is already taken by a library
void wakeup(); //if woken up match = true
bool matched = true; //for the wakeup
int alarmminute = 1 ;  //start time for alarm
const int alarminterval = 1 ;  //period to take temp in minutes  
.....

void setup {
....
    myRTC.begin();
    myRTC.enableAlarm(myRTC.MATCH_MMSS);
....
}


void loop {

if(matched) {

...sensor readings...
...store to SD card...
...payload of any kind...

    alarmminute= (alarmminute+alarminterval) % 60;
    myRTC.setAlarmMinutes(alarmminute);
....
  } //matched end
    myRTC.standbyMode();
} //loop end


void wakeup(){
  matched = true; 
}

I currently enhance this code and will come back to this thread to post a result once I find time.

Update from my side:

The code worked only partly, and so I might say it just didn't work. Because of some mysterious interaction, the SD-Card Datalogger did not wake up.

So I tried ArduinoLowPower.h library again.
I firstly discarded the library (aka. the command "LowPower.deepSleep()"), because it did not wake up the SIM Module.

Now that I have rearranged my setup and abandoned the idea of storing data in a cloud but instead on an SD Card that is plugged into the MKR ENV Shield, I tried the command again and it worked just fine.

Power consumption normal ~ 135 mA
Power consumption deep sleep ~111 mA

The Dust Sensor HM3301 draws a lot of power no matter how sleepy the MCU is and so I have to cope with this big amount of power withdraw.

The code was more than simple:

#include  ArduinoLowPower.h


void loop{
... 
LowPower.deepSleep(20000); //for 20s of sleep
}

My Setup:
Arduino MKR GSM 1400
Arduino MKR ENV Shield + SD CARD
Arduino MKR connector carrier
HM3301 - dust sensor
DHT22 - Humi&Temp, because the ENV Shield sits right on top of a heat source
DS1307 - RTC clock with Grove connector

Goal:
Analyze and characterize the HM3301 (Masterthesis - TU Berlin)