Arduino Uno deep sleep wake-up with DS3231: from once an hour to several times

A well running routine wakes up an Arduino Uno from deep sleep using RTC.alarmInterrupt instruction, once a minute, at second 30. However, this instruction in general manages to wake up only once a minute, once an hour,..

I would like to change this to once every 10 minutes (or just several times an hour for that matter): how is that best accomplished in this program?

  /*
// https://www.instructables.com/id/Arduino-Sleep-and-Wakeup-Test-With-DS3231-RTC/
// Arduino wake-up with DS3231
// corrected the library for use with Atmega168P
// corrected by: https://github.com/rocketscream/Low-Power/issues/45
// and also: https://github.com/rocketscream/Low-Power/issues/14
// connect DS3231 pin INT/SQW to D2 on Arduino (INT0, PCINT18, PD2)
// v8: includes ethernet shield and upload to Thingspeak
//    code from https://github.com/iobridge/ThingSpeak-Arduino-Examples/blob/master/Ethernet/Arduino_to_ThingSpeak.ino
// and for two values: https://community.particle.io/t/uploading-sensor-data-to-thingspeak/5497
// V9: modified for transmission over HC12 via hardware serial
//   1. prepare values for transmission: int sendDutyCycle, time and date
//   2. serialPrint only what needs to be transmitted
//   3. includes Thingspeak code
//   4. send 3 integers: humidity %, minute, hour and sensor identification (A, B, ..)
//
// V10: code without thingspeak, no ethernet module, and cleaned up
// status 1/12/17: tested ok
// V11: send 4 values: duty cycle %, temperature, minute, hour and sensor identification (A, B, ..)
// status 5/01/2018: tested ok
// V12: added second alarm ALM2, added 5th value: analog battery value
// status 5/01/2018: tested ok
//
// Use with Arduino_serial_Arduino_RX_HC12_burst_test_hardwareSerial_v4
// Use with ESP8266_RX_HC12_hardware-Serial_Thingspeak_v1 (experimental, to be tested; 03/2018: tested and = ok)
// NEW: since 25/03/2018, use with ESP8266_irrigation_controller_v1 (copied from ESP8266_RX_HC12_hardware-Serial_Thingspeak_v1)

/***************************************
     ESP8266 irrigation controller:
          sensor module (TX)
 ***************************************/
 
*/

/*
   Connect:
   RX to HC12 TX
   TX to HC12 RX
   SDA to A4
   SCL to A5
   D2 to DS3231 SQW
   D9 to MOSFET powerdrive gate on dutycycle sensor
   D8 to dutycycle sensor output (pin 11)
   A0 to LM35 output
   A1 to battery voltage
   LM35 Vcc and GND to controller
   dutycycle ground to HC12 ground
   MOSFET drain to dutycycle sensor board ground
   MOSFET source to controller ground
   sensor board to external supply 5V
   external supply ground to controller ground
*/

#include <Wire.h>
#include <RTClibExtended.h>
#include <LowPower.h>
// #include <SPI.h> //Thingspeak
#define DS3231_I2C_ADDRESS 0x68
RTC_DS3231 RTC;      //we are using the DS3231 RTC

// Variable Setup
// NOTE: pin D8 is used for reading incoming square wave for duty cycle measurement

long lastConnectionTime = 0;
boolean lastConnected = false;
int failedCounter = 0;
int wakePin = 2;    //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
int ledPin = 13;    //use arduino on-board led for indicating sleep or wakeup status
int wakeStatus = 9; //use D10 to drive 5V power to sensor module and HC-12 with MOSFET, 1=wake
int temperaturePin = A0; // analog input for LM35 temperature sensor
float temperatureT1;
int batteryPin = A1;
float batteryVoltage;
int temperatureRead;
byte goToSleepNow = 1;
byte ledStatus = 1;
volatile word timerValue[4];
volatile byte testState = 0;
volatile boolean signalPresent = false;
boolean newValuesAvailable = false;
float pwmPeriod, pwmWidth, pwmFrequency, pwmDutyDisplay;
unsigned long pwmDuty;
const byte x = 10; // loop value: loop amount = x, number of dutycycle measurements in 1 run
int sendDutyCycle;  // contains integer value of float cycleValue
int dutyCycleStatus = 8; // Input Capture Pin

//-------------------------------------------------

void wakeUp()        // here the interrupt is handled after wakeup
{
}

//------------------------------------------------------------

void setup() {
  //Set pin D2 as INPUT for accepting the interrupt signal from DS3231
  pinMode(wakePin, INPUT);
  //switch-on the on-board led for 1 second for indicating that the sketch is ok and running
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  //Set MOSFET gate drive to output
  pinMode(wakeStatus, OUTPUT);
  digitalWrite(wakeStatus, LOW);
  analogReference(INTERNAL);
  delay(1000);
  //Initialize communication with the clock
  Wire.begin();
  RTC.begin();
  // A convenient constructor for using "the compiler's time":
  // DateTime now (__DATE__, __TIME__);
  // uncomment following line when compiling and uploading,
  // then comment following line and immediately upload again

  // RTC.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time, see instructions above

  //clear any pending alarms
  RTC.armAlarm(1, false);
  RTC.clearAlarm(1);
  RTC.alarmInterrupt(1, false);
  RTC.armAlarm(2, false);
  RTC.clearAlarm(2);
  RTC.alarmInterrupt(2, false);
  //Set SQW pin to OFF (in my case it was set by default to 1Hz)
  //The output of the DS3231 INT pin is connected to this pin
  //It must be connected to arduino D2 pin for wake-up
  RTC.writeSqwPinMode(DS3231_OFF);
  //example: Set alarm1 every day at 18:33: 00 seconds, 33 minutes, 18 hours, 0 = every day;
  // if for example Sunday then: dowSunday if a date then date of the month
  //
  // see for explanation: https://github.com/JChristensen/DS3232RTC#alarm-methods
  RTC.setAlarm(ALM1_MATCH_SECONDS, 30, 00, 0, 0);   //set your wake-up time here:
  RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 10, 0, 0);  //where "xx" is minutes
  // every 00 minutes past the hour;
  // if every minute is needed change MINUTES to SECONDS (only for ALM1)
  // matches seconds AND minutes when _MINUTES is used. Sequence of time:
  // first seconds, then minutes, hours, daydate
  // or: seconds (but enter 00, is ignored), minutes then hours, daydate for ALM2
  // zero's mean: always
  // example: Set alarm1 every day at 18:33
  // RTC.setAlarm(ALM1_MATCH_HOURS, 33, 18, 0);  set your wake-up time here
  // RTC.alarmInterrupt(1, true);

  RTC.alarmInterrupt(1, true); //set alarm1
  RTC.alarmInterrupt(2, true); //set alarm2

  Serial.begin(115200);
}

//------------------------------------------------------------

void loop() {

  //On first loop we enter the sleep mode
  if (goToSleepNow == 1) {                                 // value 1 = go to sleep
    attachInterrupt(0, wakeUp, LOW);                       //use interrupt 0 (pin PD2) and run function wakeUp when pin 2 gets LOW
    digitalWrite(ledPin, LOW);                             //switch-off the led for indicating that we enter the sleep mode
    ledStatus = 0;                                         //set the led status accordingly
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);   //arduino enters sleep mode here
    detachInterrupt(0); //execution resumes from here after wake-up
    //When exiting the sleep mode we clear the alarm
    RTC.armAlarm(1, false);
    RTC.clearAlarm(1);
    RTC.alarmInterrupt(1, false);
    RTC.armAlarm(2, false);
    RTC.clearAlarm(2);
    RTC.alarmInterrupt(2, false);
    goToSleepNow = 0;                                      // value 0 = do not go to sleep
  }

  //cycles the led to indicate that we are no more in sleep mode
  if (ledStatus == 0) {
    ledStatus = 1;
    digitalWrite(ledPin, HIGH);
  }
  digitalWrite(wakeStatus, HIGH);  // set wakeStatus to HIGH, wake mode, activate moisture sensor electronics
  delay (1000);
  measure(); //execute duty cycle measurement during wakeUp
  delay (500);
  digitalWrite(wakeStatus, LOW);                       //initiate wake status LOW, sleepmode, de-activate moisture sensor electronics
  goToSleepNow = 1;
  RTC.alarmInterrupt(1, true);
  RTC.alarmInterrupt(2, true);
}

...THIS FURTHER CODE OMMITED BECAUSE TOO LONG IN POST: SEE ATTACHMENT FOR FULL CODE.

Arduino_duty_cycle_measurement_v12.pdf (17 KB)

Arduino_duty_cycle_measurement_v12.ino (12.4 KB)

It’s documented in the code

  // see for explanation: https://github.com/JChristensen/DS3232RTC#alarm-methods
  RTC.setAlarm(ALM1_MATCH_SECONDS, 30, 00, 0, 0);   //set your wake-up time here:
  RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 10, 0, 0);  //where "xx" is minutes
  // every 00 minutes past the hour;
  // if every minute is needed change MINUTES to SECONDS (only for ALM1)
  // matches seconds AND minutes when _MINUTES is used. Sequence of time:
  // first seconds, then minutes, hours, daydate
  // or: seconds (but enter 00, is ignored), minutes then hours, daydate for ALM2
  // zero's mean: always
  // example: Set alarm1 every day at 18:33
  // RTC.setAlarm(ALM1_MATCH_HOURS, 33, 18, 0);  set your wake-up time here
  // RTC.alarmInterrupt(1, true);

J-M-L:
It’s documented in the code

  // see for explanation: https://github.com/JChristensen/DS3232RTC#alarm-methods

RTC.setAlarm(ALM1_MATCH_SECONDS, 30, 00, 0, 0);  //set your wake-up time here:
  RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 10, 0, 0);  //where "xx" is minutes
  // every 00 minutes past the hour;
  // if every minute is needed change MINUTES to SECONDS (only for ALM1)
  // matches seconds AND minutes when _MINUTES is used. Sequence of time:
  // first seconds, then minutes, hours, daydate
  // or: seconds (but enter 00, is ignored), minutes then hours, daydate for ALM2
  // zero's mean: always
  // example: Set alarm1 every day at 18:33
  // RTC.setAlarm(ALM1_MATCH_HOURS, 33, 18, 0);  set your wake-up time here
  // RTC.alarmInterrupt(1, true);

No, this is not what it says. The alarm function does set an alarm every so many minutes past the hour: that means for example if minutes is set at 10, then every 10 minutes past the hour the alarm is set. But I want the alarm to be set at 10 minutes past the hour AND at 20 minutes past thet hour AND at 40 minutes past the hour. For example.

But I want the alarm to be set at 10 minutes past the hour AND at 20 minutes past that hour AND at 40 minutes past the hour.

There is no direct alarm mode which covers this scenario. What you will have to do is that the code after wake up should set the next alarm.

Indeed - The documentation explains the capability - so you need to play with what’s available in hardware

Set alarm at **:10 and when you get woken up change the next wake time to be at **:20 and then at **:40 and cycle back to :10 etc

J-M-L:
Indeed - The documentation explains the capability - so you need to play with what’s available

Set alarm at **:10 and when you get woken up change the next wake time to be at **:20 and then at **:40 and cycle back to :10

A start for this I found here: Alarm RTC DS3231 wake Arduino? - Programming Questions - Arduino Forum
But he uses DS3231RTC library while I need to use RTClibExtended.h so either I have to find the similarities by perusing both libraries or someone here already has some code for it.
The solution using the first library would be (wake up every 20 minutes):

#include <DS3232RTC.h>
#include <Wire.h>

DS3232RTC rtc;

void setup()
{
    Serial.begin( 115200 );
    Wire.begin();
    rtc.alarm( ALARM_1 ); // makes sure A1F is cleared (see datasheet p.14)
    rtc.setAlarm( ALM1_MATCH_SECONDS, 0, 0, 0, 0 );
}

void loop()
{
    if ( rtc.alarm( ALARM_1 ) )
    {
        tmElements_t tm;
        rtc.read( tm );
        RTC.setAlarm(ALM1_MATCH_MINUTES, 0, (tm.Minute + 20) % 60, 0, 0);
        time_t now = makeTime( tm );
        Serial.println( now );
    }
}

..but tmElements_t tm; and rtc.read(tm); are not used in the second (RTClibExtended.h) library.

RTClibExtended.H is a fork of the Adafruit library extended for DS3231 only and supporting the alarms.

The adafruit library implements the DateTime class so you would do aDateTime now = rtc.now(); // instead of rtc.read( tm ); when you are woken up and now.minute() would be the equivalent to tm.Minute

See this example in the adafruit lib to understand how to access current time

I included a read from the DS3231 (, and a RTC alarm setup right after each wake-up: I read current minutes and add the requested amount of minutes (pollInterval) to be counted to get to the next wake-up.

Would this code, added richt after the wake-up, do it:

#include <Wire.h>
#include <RTClibExtended.h>
#include <LowPower.h>
// #include <DS3231RTC.h>
// #include <SPI.h> //Thingspeak
#define DS3231_I2C_ADDRESS 0x68
RTC_DS3231 RTC;      //we are using the DS3231 RTC

// Variable Setup
// NOTE: pin D8 is used for reading incoming square wave for duty cycle measurement

long lastConnectionTime = 0;
boolean lastConnected = false;
int failedCounter = 0;
int wakePin = 2;    //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
int ledPin = 13;    //use arduino on-board led for indicating sleep or wakeup status
int wakeStatus = 9; //use D10 to drive 5V power to sensor module and HC-12 with MOSFET, 1=wake
int temperaturePin = A0; // analog input for LM35 temperature sensor
float temperatureT1;
int batteryPin = A1;
float batteryVoltage;
int temperatureRead;
byte goToSleepNow = 1;
byte ledStatus = 1;
volatile word timerValue[4];
volatile byte testState = 0;
volatile boolean signalPresent = false;
boolean newValuesAvailable = false;
float pwmPeriod, pwmWidth, pwmFrequency, pwmDutyDisplay;
unsigned long pwmDuty;
const byte x = 10; // loop value: loop amount = x, number of dutycycle measurements in one run
const byte pollInterval = 5; // number of minutes interval between subsequent sensor_activation_and_datacollection
int sendDutyCycle;  // contains integer value of float cycleValue
int dutyCycleStatus = 8; // Input Capture Pin

//-------------------------------------------------

void wakeUp()        // here the interrupt is handled after wakeup
{
}

//------------------------------------------------------------

void setup() {
  //Set pin D2 as INPUT for accepting the interrupt signal from DS3231
  pinMode(wakePin, INPUT);
  //switch-on the on-board led for 1 second for indicating that the sketch is ok and running
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  //Set MOSFET gate drive to output
  pinMode(wakeStatus, OUTPUT);
  digitalWrite(wakeStatus, LOW);
  analogReference(INTERNAL);
  delay(1000);
  //Initialize communication with the clock
  Wire.begin();
  RTC.begin();
  // A convenient constructor for using "the compiler's time":
  // DateTime now (__DATE__, __TIME__);
  // uncomment following line when compiling and uploading,
  // then comment following line and immediately upload again

  // RTC.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time, see instructions above

  //clear any pending alarms
  RTC.armAlarm(1, false);
  RTC.clearAlarm(1);
  RTC.alarmInterrupt(1, false);
  RTC.armAlarm(2, false);
  RTC.clearAlarm(2);
  RTC.alarmInterrupt(2, false);
  //Set SQW pin to OFF (in my case it was set by default to 1Hz)
  //The output of the DS3231 INT pin is connected to this pin
  //It must be connected to arduino D2 pin for wake-up
  RTC.writeSqwPinMode(DS3231_OFF);
  //example: Set alarm1 every day at 18:33: 00 seconds, 33 minutes, 18 hours, 0 = every day;
  // if for example Sunday then: dowSunday if a date then date of the month
  //
  // see for explanation: https://github.com/JChristensen/DS3232RTC#alarm-methods
  RTC.setAlarm(ALM1_MATCH_SECONDS, 30, 00, 0, 0);   //set your wake-up time here:
  RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 10, 0, 0);  //where "xx" is minutes
  // every 00 minutes past the hour;
  // if every second is needed change MINUTES to SECONDS (only for ALM1)
  // matches seconds AND minutes when _MINUTES is used. Sequence of time:
  // first seconds, then minutes, hours, daydate
  // or: seconds (but enter 00, is ignored), minutes then hours, daydate for ALM2
  // zero's mean: always
  // example: Set alarm1 every day at 18:33
  // RTC.setAlarm(ALM1_MATCH_HOURS, 33, 18, 0);  set your wake-up time here
  // RTC.alarmInterrupt(1, true);
  RTC.alarmInterrupt(1, true); //set alarm1
  RTC.alarmInterrupt(2, true); //set alarm2
  Serial.begin(115200);
}

void loop() {

  //On first loop we enter the sleep mode
  if (goToSleepNow == 1) {                                 // value 1 = go to sleep
    attachInterrupt(0, wakeUp, LOW);                       //use interrupt 0 (pin PD2) and run function wakeUp when pin 2 gets LOW
    digitalWrite(ledPin, LOW);                             //switch-off the led for indicating that we enter the sleep mode
    ledStatus = 0;                                         //set the led status accordingly
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);   //arduino enters sleep mode here
    detachInterrupt(0); //execution resumes from here after wake-up
    //When exiting the sleep mode we clear the alarm
    RTC.armAlarm(1, false);
    RTC.clearAlarm(1);
    RTC.alarmInterrupt(1, false);
    RTC.armAlarm(2, false);
    RTC.clearAlarm(2);
    RTC.alarmInterrupt(2, false);
    goToSleepNow = 0;                                      // value 0 = do not go to sleep
  }

  //cycles the led to indicate that we are no more in sleep mode
  if (ledStatus == 0) {
    ledStatus = 1;
    digitalWrite(ledPin, HIGH);
  }
  digitalWrite(wakeStatus, HIGH);  // set wakeStatus to HIGH, wake mode, activate moisture sensor electronics
  // reset alarm clock to new values: enable wake-up more than once per minute or once per hour: next 2 lines
  DateTime now = RTC.now();
  RTC.setAlarm(ALM1_MATCH_MINUTES, 00, (now.minute() + 5) % 60, 0, 0);   //set your wake-up time here:

  delay (1000);
  measure(); //execute duty cycle measurement during wakeUp
  delay (500);
  digitalWrite(wakeStatus, LOW);                       //initiate wake status LOW, sleepmode, de-activate moisture sensor electronics
  goToSleepNow = 1;
  RTC.alarmInterrupt(1, true);
  RTC.alarmInterrupt(2, true);
}

Would this code, added richt after the wake-up, do it:

Why do you have two alarms? I think that the one set for every 30 seconds will be waking the Arduino and the alarm desired at the 5 minute intervals will be adjusted ahead and never reached.

cattledog:
Why do you have two alarms? I think that the one set for every 30 seconds will be waking the Arduino and the alarm desired at the 5 minute intervals will be adjusted ahead and never reached.

Indeed ALM2 is not needed and the setup line for this alarm must be omitted.
But I assumed the following:

  1. in setup ALM1 is set for every 30th second in the minute
  2. in loop ALM1 is changed right after wake-up to wake again 5 minutes after the current minute.

Why would the alarm ALM1 set at the 5 minute intervals be adjusted ahead and never be reached?

Why would the alarm ALM1 set at the 5 minute intervals be adjusted ahead and never be reached?

My mistake. I got confused between alarm1 and alarm2 and did not notice that you changed the alarm type for alarm1.

You should be able to test all this out faster, than you can ask questions on the forum. The answer will be better than mine as well. :slight_smile:

cattledog:
My mistake. I got confused between alarm1 and alarm2 and did not notice that you changed the alarm type for alarm1.

You should be able to test all this out faster, than you can ask questions on the forum. The answer will be better than mine as well. :slight_smile:

Good idea, and thank you (and the others in this thread) for the help. I will post the results as soon as tested.

brice3010:
A start for this I found here: Alarm RTC DS3231 wake Arduino? - Programming Questions - Arduino Forum
But he uses DS3231RTC library while I need to use RTClibExtended.h so either I have to find the similarities by perusing both libraries or someone here already has some code for it.
The solution using the first library would be (wake up every 20 minutes):

#include <DS3232RTC.h>

#include <Wire.h>

DS3232RTC rtc;

void setup()
{
    Serial.begin( 115200 );
    Wire.begin();
    rtc.alarm( ALARM_1 ); // makes sure A1F is cleared (see datasheet p.14)
    rtc.setAlarm( ALM1_MATCH_SECONDS, 0, 0, 0, 0 );
}

void loop()
{
    if ( rtc.alarm( ALARM_1 ) )
    {
        tmElements_t tm;
        rtc.read( tm );
        RTC.setAlarm(ALM1_MATCH_MINUTES, 0, (tm.Minute + 20) % 60, 0, 0);
        time_t now = makeTime( tm );
        Serial.println( now );
    }
}



..but tmElements_t tm; and rtc.read(tm); are not used in the second (RTClibExtended.h) library.

I need to make my arduino to wake up once in hour and goes sleep after 1 minute. will your code help me. what changes i should make. can you please guide me

bmg1234:
I need to make my arduino to wake up once in hour and goes sleep after 1 minute. will your code help me. what changes i should make. can you please guide me

  1. In post #7 in setup you need to change the RTC ALM1 to minutes, not seconds: RTC.setAlarm(ALM1_MATCH_MINUTES, 00, 00, 0, 0);
  2. Then, there in the loop, while woken up, currently the new "wakes" are set at 5 minutes past the hour.
    The loop is written so that you get back to sleep, to the end of void loop(){}
    If you want to wait for one minute before going to sleep after wake-up, insert a timer with this delay.

brice3010:

  1. In post #7 in setup you need to change the RTC ALM1 to minutes, not seconds: RTC.setAlarm(ALM1_MATCH_MINUTES, 00, 00, 0, 0);
  2. Then, there in the loop, while woken up, currently the new "wakes" are set at 5 minutes past the hour.
    The loop is written so that you get back to sleep, to the end of void loop(){}
    If you want to wait for one minute before going to sleep after wake-up, insert a timer with this delay.

I have tried to change something like you mentioned . can you please check.

i want device to wake up every 5 hours and goes sleep after 2 minutes.
i am new to this trying my best to understand can you help me in changes with the code

#include <DS3232RTC.h>
#include <Wire.h>

DS3232RTC rtc;

void setup()
{
Serial.begin( 115200 );
Wire.begin();
rtc.alarm( ALARM_1 ); // makes sure A1F is cleared (see datasheet p.14)
rtc.setAlarm(ALM1_MATCH_MINUTES, 00, 00, 0, 0);
}

void loop()
{
if ( rtc.alarm( ALARM_1 ) )
{
tmElements_t tm;
rtc.read( tm );
RTC.setAlarm(ALM1_MATCH_MINUTES, 0, (tm.Minute + 20) % 60, 0, 0);
time_t now = makeTime( tm );
Serial.println( now );
}
}

Hello everybody,
I have tried to use the attached code to wake up every 5 minutes.
The LED is always on and the alarm is not respected.
I changed from your initial code :
attachInterrupt(0, wakeUp, FALLING);

The arduino is never in low power (i;e sleep mode)).
I removed the pullup on DS3231 but not better.
Could you provide used libraries and the wiring diagram ?
Have you got an idea ?
Thanks.

TestSleep.cpp (7.74 KB)