MKR Zero board goes to sleep mode and wake up periodically

Hi all,

I want to make my MKR Zero board go to sleep mode to save power and wake up periodically to do something and then go back to sleep until next wake up. I am using RTCZero library now. But the problem is that every time when I upload the sketch to the board, it seems to be upload to the board successfully because the LED blinked as what I wrote in the sketch, but then the Arduino IDE gives an error of "couldn't find the board". I don't know the reason for that. Is it possible that the sleep mode (standby mode) results in such an error? If so, what should I do? Below is the code I used from online:

#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 = 00;
const byte hours = 00;

/* Change these values to set the current initial date */
const byte day = 15;
const byte month = 7;
const byte year = 19;

bool matched = false;

void setup() 
{
  delay(10000); //delay so we can see normal current draw for a timespan e.g. 10 seconds
  pinMode(LED_BUILTIN, OUTPUT); //set LED pin to output
  digitalWrite(LED_BUILTIN, LOW); //turn LED off

  rtc.begin(); //Start RTC library, this is where the clock source is initialized

  rtc.setTime(hours, minutes, seconds); //set time
  rtc.setDate(day, month, year); //set date

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(alarmMatch); //creates an interrupt that wakes the SAMD21
  
  //puts SAMD21 to sleep
  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;
}

I already explained the problem in your other thread:
https://forum.arduino.cc/index.php?topic=626158.msg4242840#msg4242840

pert:
I already explained the problem in your other thread:
Check if the board is in sleep mode - #4 by pert - MKRZero - Arduino Forum

Thank you very much pert! Now I can upload the sketch. There is another question which is about the RTC Zero function. From the code I put above, I don't really understand the part

rtc.setAlarmTime(00, 00, 10);

Does that mean that the alarm will take 10 seconds to go off after it has been triggered?

And another confusing part is:

nt alarmMinutes = rtc.getMinutes();
alarmMinutes += 1;
if (alarmMinutes >= 60) {
alarmMinutes -= 60;
}

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

what does this part do?
Thank you in advance for any help you can give.

And when the board wake up, will it go straight to the loop function or start from the setup part and run through all the code again? Just wondering how this code work

Shawn_Li:
I don't really understand the part

rtc.setAlarmTime(00, 00, 10);

Does that mean that the alarm will take 10 seconds to go off after it has been triggered?

Yes, 10 seconds after you call rtc.enableAlarm(rtc.MATCH_HHMMSS). This is because you set the time to 00:00:00 in this line:

rtc.setTime(hours, minutes, seconds); //set time

Shawn_Li:
And another confusing part is:

nt alarmMinutes = rtc.getMinutes();
alarmMinutes += 1;
if (alarmMinutes >= 60) {
alarmMinutes -= 60;
}

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

what does this part do?

It puts the microcontroller in standby mode for a minute, unless the current time is xx:59:xx, in which case the microcontroller will be in standby mode for 23 hours and 59 minutes (likely not what the programmer intended).

Shawn_Li:
And when the board wake up, will it go straight to the loop function or start from the setup part and run through all the code again? Just wondering how this code work

The first thing it will do is run the code in alarmMatch(). After that, it will resume the code on the next line after you put it in standby mode. The first time you do that in your code is at the end of setup(), so when it wakes up it will start at the top of loop(). The second time you do that in your code is at the end of loop(), so when it wakes up it will start at the top of loop.

Hi Pert,

Thank you very much for the feedback! Sorry for keeping asking some other questions :-[

rtc.setAlarmTime(00, 00, 10);

Is this '10 seconds' included in the 1-minute count later to trigger the alarm?

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

Why is the processor keep in standby mode for 23 hours 59 mins? How to calculate this time? And if I want to keep it repeat the progress of sleeping and waking up after 59 mins, what can I do for that?

I am trying to combine a LoRa sender code with the RTC Zero, which means sending a data when the MKR board is woke up and go back to sleep until next waking up, sending data, going back to sleep again.

The code I used is from the link ESP32 with LoRa using Arduino IDE | Random Nerd Tutorials and it works fine without adding RTC functionality. But when I combine this two code together, the board never wake up after the first standby function in the setup, as shown below

void setup() {
  delay(10000);
  Serial.begin(9600);
  while(!Serial);
  Serial.println("LoRa Sender");
  LoRa.setPins(ss,rst,dio0);
  while(!LoRa.begin(868E6)){
    Serial.println(".");
    delay(500);
    }
    
  rtc.begin(); //Start RTC library, this is where the clock source is initialized
  
  rtc.setTime(hours, minutes, seconds); 
  rtc.setDate(day, month, year); 

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(alarmMatch); //creates an interrupt that wakes the SAMD21
  
  //puts SAMD21 to sleep
  [b]rtc.standbyMode();[/b]

But the board did wake up with the example of RTC Zero function which is exactly the code I put at the beginning of this topic.

I am so confused about the difference between my combining code and the example code. Why it can't wake up once I added the LoRa code. :confused: :confused: :confused:

Shawn_Li:
Is this '10 seconds' included in the 1-minute count later to trigger the alarm?

No. Every time you call rtc.setAlarmTime() you override any previous alarm time you set.

Shawn_Li:
Why is the processor keep in standby mode for 23 hours 59 mins? How to calculate this time? And if I want to keep it repeat the progress of sleeping and waking up after 59 mins, what can I do for that?

I should have said 23 hours and 1 minute Think it through: let's say the time is currently 03:59:00. rtc.getMinutes() returns 59. 59 + 1 = 60. 60 - 60 = 0. So when the alarm time is set, it's set to:

rtc.setAlarmTime(3, 0, 0);

Since the library uses a 24 hour clock (00:00:00 - 23:59:59), the alarm won't go off until the next day, 23 hours and 1 minute later. It seems like the intention of the author of the code was to set an alarm for 1 minute, so they should have incremented the hour value when minutes >= 60. Then you also need to handle the case when the hour is 23.

Shawn_Li:
the board never wake up after the first standby function in the setup, as shown below

What is your evidence that the board never woke up?

Hi pert,

Thank you for the patient explanation! Now I know that if I want to keep it repeat the sleep and wake up, I need to add

alarmHours += 1; after alarmMinutes -= 60;

if the value of Minutes equals to 59. Am I right?

What is your evidence that the board never woke up?

Because I tried to put the following code into the loop function

Serial.print("Sending packet: ");
Serial.println(counter);

then the serial monitor didn't display anything. I have already set the Serial.begin(9600) at the setup function like this:

void setup() {
  delay(10000);
  
  while(!Serial);
  Serial.println("LoRa Sender");
  LoRa.setPins(ss,rst,dio0);
  while(!LoRa.begin(868E6)){
    Serial.println(".");
    delay(500);
    
    
  rtc.begin(); //Start RTC library, this is where the clock source is initialized
  
  rtc.setTime(hours, minutes, seconds); 
  rtc.setDate(day, month, year); 

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(alarmMatch); 
//puts SAMD21 to sleep
  rtc.standbyMode();

As you said before, once the board wake up from standby mode, it will run the code in alarmMatch(), and then it will resume the code on the next line after I put it in standby mode, which is the loop function. But the serial monitor didn't display anything so that's why I thought it never wake up from the standby mode at the setup function.

Shawn_Li:
if the value of Minutes equals to 59. Am I right?

Yes, but you need to consider what happens if the current hour is 23 and write some code to deal with that correctly.

Shawn_Li:
But the serial monitor didn't display anything so that's why I thought it never wake up from the standby mode at the setup function.

OK, that's logical. Do you see the "LoRa Sender" text in Serial Monitor?

One thing to keep in mind with serial output and sleeping is that serial output is relatively slow and happens asynchronously. What I mean by "asynchronously" is that when you call Serial.println("Sending packet: "), it doesn't immediately send that over Serial. Instead, it puts that text in a buffer and then the function returns. There is some background code that knows when it's time to send each byte of the text in the buffer and does this automatically. That's really nice because it means you're not blocked from running the rest of your code while waiting for this slow serial message to go out. However, if you try to do a Serial print and then put the microcontroller to sleep right after, there is no time for the serial data to get sent. You'll encounter the same problem if you try to use Serial.println() to figure out which line of your code is causing the microcontroller to reset. What you can do is call Serial.flush() before going to sleep. This function will wait until the data in the outgoing serial buffer has all been sent before it returns.

Hi pert,

I tried to use Serial.flush() function but it still can't display the variable. I think the problem is that the board doesn't wake up once it goes into sleep when I use the LoRa.h library. But RTC Zero is an internal real-time clock so there is no pins conflict. It works fine when I just blink a LED in the loop function. I also tried to move the LoRa setup code after the RTC setup and it doesn't have any different result. Do you have any idea that may cause this problem?

The blink thing was a good idea.

I don't have any ideas about the problem. It might be helpful if you posted your full sketch. If it exceeds the 9000 characters allowed by the forum, you can attach it to your reply. If you click the "Reply" button, you'll see an "Attachments and other settings" link that will allow you to make attachments.

Hi pert,

The following is my code of combining the LoRa sender and RTC

#include <SPI.h>
#include <LoRa.h>
#include <RTCZero.h>

#define ss 5
#define rst 14
#define dio0 2

RTCZero rtc;

int counter = 0;

const byte seconds = 0;
const byte minutes = 00;
const byte hours = 00;
const byte day = 20;
const byte month = 7;
const byte year = 19;

bool matched = false;

void setup() {
  delay(10000);
  while(!Serial);
  Serial.println("LoRa Sender");
  LoRa.setPins(ss,rst,dio0);
  while(!LoRa.begin(868E6)){
    Serial.println(".");
    delay(500);
  } 
  
  rtc.begin(); //Start RTC library, this is where the clock source is initialized
  
  rtc.setTime(hours, minutes, seconds); 
  rtc.setDate(day, month, year); 

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(alarmMatch); //creates an interrupt that wakes the SAMD21
  
  //puts SAMD21 to sleep
  rtc.standbyMode(); 
  
  // Uncomment this function if you wish to attach function dummy when RTC wakes up the chip
  // LowPower.attachInterruptWakeup(RTC_ALARM_WAKEUP, dummy, CHANGE);  
}


void loop() {
  if (matched) {
    matched = false; 
    Serial.print("Sending packet: ");
    Serial.println(counter);
    //Send LoRa packet to Rx
    LoRa.beginPacket();
    LoRa.print("hello");
    LoRa.print(counter);
    LoRa.endPacket();
    counter++;
  //delay(5000);
  }
  int alarmMinutes = rtc.getMinutes();
    int alarmHours = rtc.getHours();
    alarmMinutes += 1;
    if (alarmMinutes >= 60) {
      alarmMinutes -= 60;
        alarmHours += 1;
    }

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

void alarmMatch() {
  matched = true;
}

Thank you very much for the help!