Regular DS3231 Alarm & Arduino sleeping mode: working once but no more...

Hey everybody,

I got trouble trying to wake up my Arduino nano every minutes. I’m using a DS3231, with the Korneliusz Jarzebski library (https://github.com/jarzebski/Arduino-DS3231)

#include <Wire.h>
#include <DS3231.h>
#include <avr/sleep.h>

DS3231 clock;
RTCDateTime dt;

const byte wakePin = 2;                 // pin used for waking up (interupt 0)

void setup()
{
  Serial.begin(9600);
  
  // Initialize DS3231
  clock.begin();
  clock.armAlarm1(false);
  clock.armAlarm2(false);
  clock.clearAlarm1();
  clock.clearAlarm2();

  pinMode(wakePin, INPUT);

  byte temp_buffer = 0b11110111;        // to disable SQW pin of DS3231, not logical yes, but without that the Arduino doesn't go to sleep mode even the first time
  writeControlByte(temp_buffer, 0);
 
  clock.setDateTime(2014, 4, 25, 0, 0, 55);
  clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);
  attachInterrupt(0, wakeUpNow, FALLING); 
  delay(100);

  checkAlarms();
}

void wakeUpNow()
{
  sleep_disable();
  detachInterrupt(0);
  delay(100);
}

void sleepNow()
{
  sleep_enable();
  attachInterrupt(0, wakeUpNow, LOW);
  delay(100);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  sleep_bod_disable();
  sei();
  sleep_cpu();
  /* wake up here */
  sleep_disable();
}   

void loop()
{
  char print_date[16];
  Serial.println("go to sleep...");
  delay(100);
  sleepNow();
  dt = clock.getDateTime();
  sprintf(print_date, "%02d/%02d/%d %02d:%02d %02d", dt.day, dt.month, dt.year, dt.hour, dt.minute, dt.second);
  Serial.println(print_date);
}

void writeControlByte(byte control, bool which) {  // Set DS3121 RTC control bytes
   // Write the selected control byte.
   // which=false -> 0x0e, true->0x0f.
   Wire.beginTransmission(0x68);
   if (which) {
      Wire.write(0x0f);
   } else {
      Wire.write(0x0e);
   }
   Wire.write(control);
   Wire.endTransmission();
}

void checkAlarms()
{
  RTCAlarmTime a1;  
  RTCAlarmTime a2;

  if (clock.isArmed1())
  {
    a1 = clock.getAlarm1();

    Serial.print("Alarm1 is triggered ");
    switch (clock.getAlarmType1())
    {
      case DS3231_EVERY_SECOND:
        Serial.println("every second");
        break;
      case DS3231_MATCH_S:
        Serial.print("when seconds match: ");
        Serial.println(clock.dateFormat("__ __:__:s", a1));
        break;
      case DS3231_MATCH_M_S:
        Serial.print("when minutes and sencods match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a1));
        break;
      case DS3231_MATCH_H_M_S:
        Serial.print("when hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("__ H:i:s", a1));
        break;
      case DS3231_MATCH_DT_H_M_S:
        Serial.print("when date, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("d H:i:s", a1));
        break;
      case DS3231_MATCH_DY_H_M_S:
        Serial.print("when day of week, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("l H:i:s", a1));
        break;
      default: 
        Serial.println("UNKNOWN RULE");
        break;
    }
  } else
  {
    Serial.println("Alarm1 is disarmed.");
  }

  if (clock.isArmed2())
  {
    a2 = clock.getAlarm2();

    Serial.print("Alarm2 is triggered ");
    switch (clock.getAlarmType2())
    {
      case DS3231_EVERY_MINUTE:
        Serial.println("every minute");
        break;
      case DS3231_MATCH_M:
        Serial.print("when minutes match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a2));
        break;
      case DS3231_MATCH_H_M:
        Serial.print("when hours and minutes match:");
        Serial.println(clock.dateFormat("__ H:i:s", a2));
        break;
      case DS3231_MATCH_DT_H_M:
        Serial.print("when date, hours and minutes match: ");
        Serial.println(clock.dateFormat("d H:i:s", a2));
        break;
      case DS3231_MATCH_DY_H_M:
        Serial.println("when day of week, hours and minutes match: ");
        Serial.print(clock.dateFormat("l H:i:s", a2));
        break;
      default: 
        Serial.println("UNKNOWN RULE"); 
        break;
    }
  } else
  {
    Serial.println("Alarm2 is disarmed.");
  }
}

Already 2 days I’m trying different things and looking on forums for similar examples, but I didn’t found anything working…

What I expect from this example is to have the date displayed once every minute, and the Arduino sleeping meantime. What I obtain with this code is that the Arduino is going to sleep directly, wakeup after 5 seconds (it’s ok as I set the clock at 00:00:55, and the alarm is every minute). But after the Arduino do not come back to sleeping mode.

I also don’t understand why without disactivating first the SQW, the Arduino even don’t go in the first sleeping mode.

I’m out of ideas now. So any clue or advice are welcome! :smiley:

you are using

delay(100);

in the wakeUpNow() IRQ call back

the problem with this is that delay() relies on interrupts itself but because you are in the execution of interrupt 0, this can't be interrupted and delay will not work

you don't need the delay in the interrupt anyway - just put that back in the main code where you have the comment

  /* wake up here */

if you really want to wait a bit

Ok, thanks for this point!

Welcome,

When the alarm occurs, a flag is set inside the ds3231, and as long as this flag is set, the alarm will not be triggered again. It’s up to you to clear this flag (A1F and A2F, the two first bits of the Control register) when you want to re enable the alarm, exactly like a true alarm clock where you must press a button to stop the buzzer and reset the alarm for next morning :slight_smile:

I looked at the library, you must call function clearAlarm1 and/or 2

Thank you guix,

I think I made progress using clearAlarm as you said, like now the code is more logical… Reducing the ISR function helped too. But now nothing is waking up, even at the first round >:(

#include <Wire.h>
#include <DS3231.h>
#include <avr/sleep.h>

DS3231 clock;
RTCDateTime dt;

const byte wakePin = 2;                 // pin used for waking up (interupt 0)

void setup()
{
  Serial.begin(9600);
 
  // Initialize DS3231
  clock.begin();
  clock.armAlarm1(false);
  clock.armAlarm2(false);
  clock.clearAlarm1();
  clock.clearAlarm2();

  pinMode(wakePin, INPUT);
 
  clock.setDateTime(2014, 4, 25, 0, 0, 54);
  clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);

  checkAlarms();
}

void wakeUpNow()
{
  detachInterrupt(0);
}

void sleepNow()
{
  sleep_enable();
  attachInterrupt(0, wakeUpNow, FALLING);
  delay(100);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  sleep_bod_disable();
  sei();
  sleep_cpu();
  /* wake up here */
  sleep_disable();
  clock.clearAlarm1();
}   

void loop()
{
  Serial.println("go to sleep...");

  sleepNow();

  char print_date[16];
  dt = clock.getDateTime();
  sprintf(print_date, "%02d/%02d/%d %02d:%02d %02d", dt.day, dt.month, dt.year, dt.hour, dt.minute, dt.second);
  Serial.println(print_date);
}

void checkAlarms()
{
  RTCAlarmTime a1; 
  RTCAlarmTime a2;

  if (clock.isArmed1())
  {
    a1 = clock.getAlarm1();

    Serial.print("Alarm1 is triggered ");
    switch (clock.getAlarmType1())
    {
      case DS3231_EVERY_SECOND:
        Serial.println("every second");
        break;
      case DS3231_MATCH_S:
        Serial.print("when seconds match: ");
        Serial.println(clock.dateFormat("__ __:__:s", a1));
        break;
      case DS3231_MATCH_M_S:
        Serial.print("when minutes and sencods match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a1));
        break;
      case DS3231_MATCH_H_M_S:
        Serial.print("when hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("__ H:i:s", a1));
        break;
      case DS3231_MATCH_DT_H_M_S:
        Serial.print("when date, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("d H:i:s", a1));
        break;
      case DS3231_MATCH_DY_H_M_S:
        Serial.print("when day of week, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("l H:i:s", a1));
        break;
      default:
        Serial.println("UNKNOWN RULE");
        break;
    }
  } else
  {
    Serial.println("Alarm1 is disarmed.");
  }

  if (clock.isArmed2())
  {
    a2 = clock.getAlarm2();

    Serial.print("Alarm2 is triggered ");
    switch (clock.getAlarmType2())
    {
      case DS3231_EVERY_MINUTE:
        Serial.println("every minute");
        break;
      case DS3231_MATCH_M:
        Serial.print("when minutes match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a2));
        break;
      case DS3231_MATCH_H_M:
        Serial.print("when hours and minutes match:");
        Serial.println(clock.dateFormat("__ H:i:s", a2));
        break;
      case DS3231_MATCH_DT_H_M:
        Serial.print("when date, hours and minutes match: ");
        Serial.println(clock.dateFormat("d H:i:s", a2));
        break;
      case DS3231_MATCH_DY_H_M:
        Serial.println("when day of week, hours and minutes match: ");
        Serial.print(clock.dateFormat("l H:i:s", a2));
        break;
      default:
        Serial.println("UNKNOWN RULE");
        break;
    }
  } else
  {
    Serial.println("Alarm2 is disarmed.");
  }
}

There is no need to keep attaching/detaching the interrupt. Put attachInterrupt in the setup function where you originally had it. Remove all other occurrences of attachInterupt and of detachInterrupt.

Pete

Ok, but this way the Arduino don't seems to go/stay to sleep mode.

well that lines feels pretty weird with all those ZEROS...

clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);

what do you think this does?

So… :slight_smile:

@J-M-L Yes this line is right. It means that all the time the second matches, the alarm is triggered. For this example, when the seconds are 0, so at each new minutes.

I finally solved my problem. The issue was not coming from the sleeping mode, but for sure your remarks helped me to improve the code.

I just run a code with an alarm every second and a display of the interrupt, which was showing a very fast switch. Like a… square wave! So the lines I originally deleted as I found they didn’t have anymore effect were right. I found here (Adafruit customer service forums • View topic - DS3231 power consumption link I thought I put in my first post, sorry) how to disable the 32k pin of the DS3231, and I thought it was in this topic I rode that it was like an interference between 32k and SQW on the DS3231(?). It’s what I finally observed. Disabling the 32k solved it.

I post now my non-optimized but working code, waking up an Arduino Nano every minutes, working with a cheap chinese DS3231SN, using the Korneliusz Jarzebski library (https://github.com/jarzebski/Arduino-DS3231).

I was hoping finding something like this pretty easily as it is the first step to do a low power consumption data logger), but I finally didn’t found any working code. But it was interesting of course to go (a little bit) deeper in how the elements work.

#include <Wire.h>
#include <DS3231.h>
#include <avr/sleep.h>

DS3231 clock;
RTCDateTime dt;

const byte wakePin = 2;                 // pin used for waking up (interupt 0)

void setup()
{
  Serial.begin(9600);
 
  // Initialize DS3231
  clock.begin();
  byte temp_buffer = 0b11110111;        // to disable SQW pin of DS3231, not logical yes, but without that the Arduino doesn't go to sleep mode even the first time
  writeControlByte(temp_buffer, 0);
  clock.armAlarm1(false);
  clock.armAlarm2(false);
  clock.clearAlarm1();
  clock.clearAlarm2();

  pinMode(wakePin, INPUT);
  attachInterrupt(0, wakeUpNow, FALLING);
 
  clock.setDateTime(2014, 4, 25, 0, 0, 54);
  clock.setAlarm1(0, 0, 0, 10, DS3231_MATCH_S);

  checkAlarms();
}

void wakeUpNow()
{
  sleep_disable();
}

void sleepNow()
{
  sleep_enable();
  delay(100);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  sleep_bod_disable();
  sei();
  sleep_cpu();
  /* wake up here */
  clock.clearAlarm1();
  clock.setAlarm1(0, 0, 0, 10, DS3231_MATCH_S);
}   

void loop()
{
  Serial.println("go to sleep...");

  sleepNow();

  char print_date[16];
  dt = clock.getDateTime();
  sprintf(print_date, "%02d/%02d/%d %02d:%02d %02d", dt.day, dt.month, dt.year, dt.hour, dt.minute, dt.second);
  Serial.println(print_date);
}

void checkAlarms()
{
  RTCAlarmTime a1;
  RTCAlarmTime a2;

  if (clock.isArmed1())
  {
    a1 = clock.getAlarm1();

    Serial.print("Alarm1 is triggered ");
    switch (clock.getAlarmType1())
    {
      case DS3231_EVERY_SECOND:
        Serial.println("every second");
        break;
      case DS3231_MATCH_S:
        Serial.print("when seconds match: ");
        Serial.println(clock.dateFormat("__ __:__:s", a1));
        break;
      case DS3231_MATCH_M_S:
        Serial.print("when minutes and sencods match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a1));
        break;
      case DS3231_MATCH_H_M_S:
        Serial.print("when hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("__ H:i:s", a1));
        break;
      case DS3231_MATCH_DT_H_M_S:
        Serial.print("when date, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("d H:i:s", a1));
        break;
      case DS3231_MATCH_DY_H_M_S:
        Serial.print("when day of week, hours, minutes and seconds match: ");
        Serial.println(clock.dateFormat("l H:i:s", a1));
        break;
      default:
        Serial.println("UNKNOWN RULE");
        break;
    }
  } else
  {
    Serial.println("Alarm1 is disarmed.");
  }

  if (clock.isArmed2())
  {
    a2 = clock.getAlarm2();

    Serial.print("Alarm2 is triggered ");
    switch (clock.getAlarmType2())
    {
      case DS3231_EVERY_MINUTE:
        Serial.println("every minute");
        break;
      case DS3231_MATCH_M:
        Serial.print("when minutes match: ");
        Serial.println(clock.dateFormat("__ __:i:s", a2));
        break;
      case DS3231_MATCH_H_M:
        Serial.print("when hours and minutes match:");
        Serial.println(clock.dateFormat("__ H:i:s", a2));
        break;
      case DS3231_MATCH_DT_H_M:
        Serial.print("when date, hours and minutes match: ");
        Serial.println(clock.dateFormat("d H:i:s", a2));
        break;
      case DS3231_MATCH_DY_H_M:
        Serial.println("when day of week, hours and minutes match: ");
        Serial.print(clock.dateFormat("l H:i:s", a2));
        break;
      default:
        Serial.println("UNKNOWN RULE");
        break;
    }
  } else
  {
    Serial.println("Alarm2 is disarmed.");
  }
}

void writeControlByte(byte control, bool which) {  // Set DS3121 RTC control bytes
   // Write the selected control byte.
   // which=false -> 0x0e, true->0x0f.
   Wire.beginTransmission(0x68);
   if (which) {
      Wire.write(0x0f);
   } else {
      Wire.write(0x0e);
   }
   Wire.write(control);
   Wire.endTransmission();
}

So for me this post is solved now, thank you for all your helpful answers!
I will post a better code later, but I really need to run now.
Two days lost on this basic thing… not kidding! :slight_smile:

Here is the “final” code, I hope a little more clear:

#include <Wire.h>
#include <DS3231.h>
#include <avr/sleep.h>

DS3231 clock;
RTCDateTime dt;
const byte wakePin = 2;      // pin used for waking up the Arduino (interrupt 0)


void setup()
{
  // Initialize Serial
  Serial.begin(9600);
 
  // Initialize DS3231
  clock.begin();
  
  // Disable DS3231 32k pin (which interferes with SQW and prevent Alarm triggering)
  Wire.beginTransmission(0x68);
  Wire.write(0x0e);
  Wire.write(0b11110111);
  Wire.endTransmission();

  // Disable DS3231 Alarms 1 and 2
  clock.armAlarm1(false);
  clock.armAlarm2(false);
  clock.clearAlarm1();
  clock.clearAlarm2();

  // Put PIN 2 as an Interrupt (0)
  pinMode(wakePin, INPUT);
  attachInterrupt(digitalPinToInterrupt(wakePin), wakeUp, FALLING);     // The falling edge of the level triggers

  // Set the time if necessary: clock.setDateTime(2016, 6, 19, 12, 22, 50);
  // Set the time and the Alarm1, every minute (at 0 seconds)
  clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);
}

// Function to put the Arduino in sleeping mode
void goToSleep()
{
  cli();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);        // setting the sleep mode
  sleep_enable();                             // enables the sleep bit in the mcucr register
  sleep_bod_disable();
  sei();
  sleep_cpu();
  
  // The Arduino wake up here
  
  clock.clearAlarm1();                        // Clear the DS3231 alarm (ready for the next triggering)
}   

// ISR (Interrupt Service Routine) function to wake up the Arduino
void wakeUp()
{
  sleep_disable();                            // Just after wake up, disable the sleeping mode
}

void loop()
{
  Serial.println("go to sleep...");
  delay(100);
  goToSleep();

  // Display the date and time
  char print_date[16];
  dt = clock.getDateTime();
  sprintf(print_date, "%02d/%02d/%d %02d:%02d:%02d", dt.day, dt.month, dt.year, dt.hour, dt.minute, dt.second);
  Serial.println(print_date);
}

Thanks again for the advices! :slight_smile:

so

  // Disable DS3231 32k pin (which interferes with SQW and prevent Alarm triggering)
  Wire.beginTransmission(0x68);
  Wire.write(0x0e);
  Wire.write(0b11110111);
  Wire.endTransmission();

overwrites register 0xO[b]E[/b] with 0b11110111 to supposedly disable the DS3231 32k pin

Looking at the doc if you set 0b11110111 into 0x0E, starting with MSB

1 - the oscillator is stopped when the DS3231 switches to VBAT
1 - Battery-Backed Square-Wave Enable (but INTCN = 1 so no use)
1 - forces the temperature sensor to convert the temperature into digital
1****0- control the frequency of the square-wave output. 10–> 4.096kHz
1 - Interrupt Control - match between the time- keeping registers and either of the alarm registers acti- vates the INT/SQW output (if the alarm is also enabled).
1 - Alarm 2 Interrupt Enable
1 - Alarm 1 Interrupt Enable

So it does not appear you are disabling the 32k pin

When I read the spec for the DS3231, to disable the 32KHz pin you actually need to toggle bit3 of 0xO[b]F[/b]

Status Register (0Fh)

Bit 3: Enable 32kHz Output (EN32kHz). This bit controls the status of the 32kHz pin. When set to logic 1, the 32kHz pin is enabled and outputs a 32.768kHz square- wave signal. When set to logic 0, the 32kHz pin goes to a high-impedance state. The initial power-up state of this bit is logic 1, and a 32.768kHz square-wave signal appears at the 32kHz pin after a power source is applied to the DS3231 (if the oscillator is running).

so not sure exactly what this does… anyone has a view?

JML is right, this code will not disable to 32Khz output.

Also there is no problem having this output and alarms, all enabled.

I think your issue is that you had a square wave set. The INT/SQW pin can only be used for one function, either alarms (INT) or square wave(SQW). I think you had it set to output a SQW frequency, which is why your alarms didn't work. It's not related to the 32Khz output, which is a different pin.