DS3231 Alarms Trouble... HELP!

Hi Everyone,

First time posting on this Forum… I’m basically really stuck.

I’m not very good at code is probably the best place for me to start but hey I wanted to have a go.

The idea of my project is this:

3.3V Arduino Pro Mini

DS3231 RTC

SD Card

DHT11 Temp and Humidity

So I basically want the arduino to record date, time, temperature and humidity to the SD card, the arduino to go to sleep, wake up every hour from an alarm from the DS3231 and record all of that again and so on and so forth until the batteries (2XAA Batteries) die.

So far everything works… apart from the alarms to wake the thing up!!!

No matter what I try it doesn’t work :frowning:

If I leave the Alarm pin on the RTC connected to PIN 2 of the arduino it wont sleep… if I disconnect it the arduino sleeps until I connect the pins again.

So basically could somebody help me get the alarm working?

I hope this all makes sense and thanks for any help in advanced.

#include <SD.h>
#include <SPI.h>
#include <avr/sleep.h>//this AVR library contains the methods that controls the sleep modes
#include "DHT.h"
#include "Wire.h"
 
  
int CS_pin = 10;

File dataFile;

#define DS3231_I2C_ADDRESS 0x68
#define DHTPIN 3
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}
void displayTime()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);
  // send it to the serial monitor
  dataFile.print(hour, DEC);
  // convert the byte variable to a decimal number when displayed
  dataFile.print(":");
  if (minute<10)
  {
    dataFile.print("0");
  }
  dataFile.print(minute, DEC);
  dataFile.print(":");
  if (second<10)
  {
    dataFile.print("0");
  }
  dataFile.print(second, DEC);
  dataFile.print(" ");
  dataFile.print(dayOfMonth, DEC);
  dataFile.print("/");
  dataFile.print(month, DEC);
  dataFile.print("/");
  dataFile.print(year, DEC);  
  switch(dayOfWeek){
  case 1:
    dataFile.print(" Sunday ");
    break;
  case 2:
    dataFile.print(" Monday ");
    break;
  case 3:
    dataFile.print(" Tuesday ");
    break;
  case 4:
    dataFile.print(" Wednesday ");
    break;
  case 5:
    dataFile.print(" Thursday ");
    break;
  case 6:
    dataFile.print(" Friday ");
    break;
  case 7:
    dataFile.print(" Saturday ");
    break;
  }
}

void setup()
{
  // put your setup code here, to run once:
  Wire.begin();
  Serial.begin(9600);
  Serial.println("Start Up");
  pinMode(CS_pin, OUTPUT);

  //check sd card
  if (!SD.begin(10))
  {
    Serial.println("Card Failed");
    return;
  }
  Serial.println("Card Ready");

  pinMode(2, INPUT);      //Set interrupt pin 2 as input
  digitalWrite(2,HIGH);   //activate the internal pull-up resistor to pull it high when jumper is not connected to GND

  dht.begin();
}

void loop()
{
  String dataString = "Hello World";
  
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(t) || isnan(h)){
    Serial.println("Failed to read from DHT");
  } else {
    Serial.print("Humidity: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature: "); 
    Serial.print(t);
    Serial.println(" *C");
    
  }
  delay(2000);

  //Open and make file

  dataFile = SD.open("log.txt", FILE_WRITE);
  if (dataFile)
  {
    displayTime();
    dataFile.print("Humidity: "); 
    dataFile.print(h);
    dataFile.print(" %\t");
    dataFile.print("Temperature: "); 
    dataFile.print(t);
    dataFile.println(" *C");
    dataFile.println();
    dataFile.close();
    Serial.println(dataString);
    
  }
  else
  {
    Serial.println("Couldn't access file");
  }
  delay(5000);
  sleepSetup();   //now we see ~27 mA until pin 2 is connected to GND
}

void sleepSetup()
{
    sleep_enable();
    attachInterrupt(0, pinInterrupt, HIGH);//Set pin 2 as interrupt and attach Interrupt Service Routine (ISR)
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);//define power down sleep mode
    digitalWrite(13,LOW);   // turn LED off to indicate sleep
    sleep_cpu();//Set sleep enable (SE) bit, this puts the ATmega to sleep
    Serial.println("just woke up!");//When it wakes up due to the interrupt the program continues with the instruction following sleep_cpu() 
    digitalWrite(13,HIGH);   // turn LED on to indicate wake up
}

void pinInterrupt()//ISR
{
  sleep_disable();//this is important. It is possible that the interrupt is called between executing "attachInterrupt(...)" and sleep_CPU() in the main loop
                  //if that happens without the sleep_disable() in the ISR, the ISR would be called, the interrupt detached and the device put to sleep.
                  //since the interrupt would be disabled at that point, there would be no way to wake the device up anymore.
                  //by putting sleep_disable() in the ISR, sleep_cpu() would not be effective during that loop, i.e. the main loop would run one more time
                  //and then properly attach the interrupt before hitting the sleep_cpu() a second time. At that point the device would go to sleep, but 
                  //the interrupt would now be activated, i.e. wake-up can be induced.
  detachInterrupt(0);//disable the interrupt. This effectively debounces the interrupt mechanism to prevent multiple interrupt calls.

}

You are using the INT/SQW output, which seems to me active LOW, but you trigger the ISR on HIGH.

    attachInterrupt(0, pinInterrupt, HIGH);

You should attach the interrupt once in setup() and not detach it at all. The alarm pin doesn't need debouncing. When the RTC alarm is set it causes the interrupt but you must then clear the alarm bit otherwise the interrupt will never occur again.

As pointed out by Whandall, you are triggering the interrupt on HIGH. You need to trigger when the pin changes from HIGH to LOW, so use FALLING instead of HIGH.

Pete

Hi All,

Thank you very much, I will make these changes once I leave work, so how do I set the Alarm?

Scott

In the library I use you could use

void setAlarm1(uint8_t dydw, uint8_t hour, uint8_t minute, uint8_t second, DS3231_alarm1_t mode, bool armed = true);

I am using this library, sorry guys I am really bad at all this, but I am learning I promise :’)

DS3231.h - Header file for the DS3231 Real-Time Clock

Version: 1.0.1
(c) 2014 Korneliusz Jarzebski
www.jarzebski.pl

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

BTW, you can't clear the alarm bit in the interrupt routine because you have to write to the I2C device which requires interrupts to be running. When the interrupt occurs, set a one-byte flag. In the loop function look for this flag to be set. When it is, clear it and also write zero into the control status register.

Oh, Duh. I hadn't noticed that you aren't even setting the alarm :) You write a zero into the Control register and the Control Status register. This will start the 1Hz clock running.

I use this function to handle writing to a register - change :

// Write the value to the specified RTC register
void RTC_write_register(unsigned char reg,unsigned char value)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(reg); 
  Wire.write(value);
  Wire.endTransmission();
}

This starts the alarm:

 // Clear both control registers.
  // This causes a 1Hz interrupt
  RTC_write_register(RTC_CONTROL,0x00);
  RTC_write_register(RTC_CTLSTAT,0x00);

and this is used after an interrupt to clear the status register:

  // Reset the alarm bit
  RTC_write_register(RTC_CTLSTAT,0x00);

With these defines:

// RTC register addresses
#define RTC_CONTROL  0x0E
#define RTC_CTLSTAT  0x0F
#define RTC_CRYSTAL  0x10

Pete

el_supremo:
BTW, you can’t clear the alarm bit in the interrupt routine because you have to write to the I2C device which requires interrupts to be running. When the interrupt occurs, set a one-byte flag. In the loop function look for this flag to be set. When it is, clear it and also write zero into the control status register.

Oh, Duh. I hadn’t noticed that you aren’t even setting the alarm :slight_smile:
You write a zero into the Control register and the Control Status register. This will start the 1Hz clock running.

I use this function to handle writing to a register - change :

// Write the value to the specified RTC register

void RTC_write_register(unsigned char reg,unsigned char value)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}




This starts the alarm:


// Clear both control registers.
  // This causes a 1Hz interrupt
  RTC_write_register(RTC_CONTROL,0x00);
  RTC_write_register(RTC_CTLSTAT,0x00);




and this is used after an interrupt to clear the status register:


// Reset the alarm bit
  RTC_write_register(RTC_CTLSTAT,0x00);




With these defines:


// RTC register addresses
#define RTC_CONTROL  0x0E
#define RTC_CTLSTAT  0x0F
#define RTC_CRYSTAL  0x10





Pete

Right,

El_Supremo obviously i’m putting the defines at the start of my code, but could you advise on where the put the rest please, I’ve changed my code so the arduino is looking for a falling edge of pin 2.

Thanks already guys I really appreciate your help.

Scott

Put the RTC_write_register function in front of the setup function.

// Clear both control registers. Goes in setup, after Wire.begin();.

// Reset the alarm bit This goes in loop() where you test whether the interrupt flag has been set.

In your interrupt routine, the only thing you do is set a flag. The flag must be global and volatile. You can declare it before the setup function.

volatile unsigned char RTC_flag = 0;

Then in the interrupt routine:   RTC_flag = 1;

and in loop():

  if(RTC_flag) {
    RTC_flag = 0;
    RTC_write_register(RTC_CTLSTAT,0x00);
    // Plus whatever else you need to do on each RTC interrupt
    .
    .
  }

Pete

Ok, so i have pput in your suggestions where you have said, could you give it the once over for me?

#include <SD.h>
#include <SPI.h>
#include <avr/sleep.h>//this AVR library contains the methods that controls the sleep modes
#include "DHT.h"
#include "Wire.h"
 
  
int CS_pin = 10;

File dataFile;

#define DS3231_I2C_ADDRESS 0x68
// RTC REGISTER addresses
#define RTC_CONTROL  0x0E
#define RTC_CTLSTAT  0x0F
#define RTC_CRYSTAL  0x10
// RTC REGISTER addresses
#define DHTPIN 3
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}
void displayTime()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from DS3231
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);
  // send it to the serial monitor
  dataFile.print(hour, DEC);
  // convert the byte variable to a decimal number when displayed
  dataFile.print(":");
  if (minute<10)
  {
    dataFile.print("0");
  }
  dataFile.print(minute, DEC);
  dataFile.print(":");
  if (second<10)
  {
    dataFile.print("0");
  }
  dataFile.print(second, DEC);
  dataFile.print(" ");
  dataFile.print(dayOfMonth, DEC);
  dataFile.print("/");
  dataFile.print(month, DEC);
  dataFile.print("/");
  dataFile.print(year, DEC);  
  switch(dayOfWeek){
  case 1:
    dataFile.print(" Sunday ");
    break;
  case 2:
    dataFile.print(" Monday ");
    break;
  case 3:
    dataFile.print(" Tuesday ");
    break;
  case 4:
    dataFile.print(" Wednesday ");
    break;
  case 5:
    dataFile.print(" Thursday ");
    break;
  case 6:
    dataFile.print(" Friday ");
    break;
  case 7:
    dataFile.print(" Saturday ");
    break;
  }
}

void RTC_write_register(unsigned char reg, unsigned char value)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

volatile unsigned char RTC_flag = 0;
RTC_flag = 1;

void setup()
{
  // put your setup code here, to run once:
  Wire.begin(); 
// Clear both control REGISTERS.
  // This causes a 1Hz interrupt
  RTC_write_register(RTC_CONTROL,0x00);
  RTC_write_register(RTC_CTLSTAT,0x00);
  Serial.begin(9600);
  Serial.println("Start Up");
  pinMode(CS_pin, OUTPUT);

  //check sd card
  if (!SD.begin(10))
  {
    Serial.println("Card Failed");
    return;
  }
  Serial.println("Card Ready");

  pinMode(2, INPUT);      //Set interrupt pin 2 as input
  digitalWrite(2,HIGH);   //activate the internal pull-up resistor to pull it high when jumper is not connected to GND

  dht.begin();
}

void loop()
{

    if (RTC_flag) {
    RTC_flag = 0;
    RTC_write_register(RTC_CTLSTAT,0x00);
    // Plus whatever else you need to do on each RTC interrupt
    
    
  }
  String dataString = "Hello World";
  
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(t) || isnan(h)){
    Serial.println("Failed to read from DHT");
  } else {
    Serial.print("Humidity: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature: "); 
    Serial.print(t);
    Serial.println(" *C");
    
  }
  delay(2000);

  //Open and make file

  dataFile = SD.open("log.txt", FILE_WRITE);
  if (dataFile)
  {
    displayTime();
    dataFile.print("Humidity: "); 
    dataFile.print(h);
    dataFile.print(" %\t");
    dataFile.print("Temperature: "); 
    dataFile.print(t);
    dataFile.println(" *C");
    dataFile.println();
    dataFile.close();
    Serial.println(dataString);
    
  }
  else
  {
    Serial.println("Couldn't access file");
  }
  delay(5000);
  
  // Reset the alarm bit
  RTC_write_register(RTC_CTLSTAT,0x00);
  
  sleepSetup();   //now we see ~27 mA until pin 2 is connected to GND
}

void sleepSetup()
{
    sleep_enable();
    attachInterrupt(0, pinInterrupt, FALLING);//Set pin 2 as interrupt and attach Interrupt Service Routine (ISR)
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);//define power down sleep mode
    digitalWrite(13,LOW);   // turn LED off to indicate sleep
    sleep_cpu();//Set sleep enable (SE) bit, this puts the ATmega to sleep
    Serial.println("just woke up!");//When it wakes up due to the interrupt the program continues with the instruction following sleep_cpu() 
    digitalWrite(13,HIGH);   // turn LED on to indicate wake up
}

void pinInterrupt()//ISR
{
  sleep_disable();//this is important. It is possible that the interrupt is called between executing "attachInterrupt(...)" and sleep_CPU() in the main loop
                  //if that happens without the sleep_disable() in the ISR, the ISR would be called, the interrupt detached and the device put to sleep.
                  //since the interrupt would be disabled at that point, there would be no way to wake the device up anymore.
                  //by putting sleep_disable() in the ISR, sleep_cpu() would not be effective during that loop, i.e. the main loop would run one more time
                  //and then properly attach the interrupt before hitting the sleep_cpu() a second time. At that point the device would go to sleep, but 
                  //the interrupt would now be activated, i.e. wake-up can be induced.
  //detachInterrupt(0);//disable the interrupt. This effectively debounces the interrupt mechanism to prevent multiple interrupt calls.

}

I am also getting this error code:

exit status 1
‘RTC_flag’ does not name a type

Whandall: In the library I use you could use

void setAlarm1(uint8_t dydw, uint8_t hour, uint8_t minute, uint8_t second, DS3231_alarm1_t mode, bool armed = true);

Whandall: In the library I use you could use

void setAlarm1(uint8_t dydw, uint8_t hour, uint8_t minute, uint8_t second, DS3231_alarm1_t mode, bool armed = true);

So what would i put after this to set the alarm for every 15 minutes?

This has been a real struggle haha :')

Scott

First of all, you would have to use the library. (Mine is from https://github.com/jarzebski/Arduino-DS3231)

I don't know whether the register code would be compatible.

There is an alarm function with a granularity of minutes

void setAlarm2(uint8_t dydw, uint8_t hour, uint8_t minute, DS3231_alarm2_t mode, bool armed = true);

So I would get the current time, add 15 to the minutes (mod 60) and arm it with

setAlarm2(0, 0, computedMinutes, DS3231_MATCH_M);

Whandall: First of all, you would have to use the library. (Mine is from https://github.com/jarzebski/Arduino-DS3231)

I don't know whether the register code would be compatible.

There is an alarm function with a granularity of minutes

void setAlarm2(uint8_t dydw, uint8_t hour, uint8_t minute, DS3231_alarm2_t mode, bool armed = true);

So I would get the current time, add 15 to the minutes (mod 60) and arm it with

setAlarm2(0, 0, computedMinutes, DS3231_MATCH_M);

Your going to have to treat me like an absolute novice here I'm afraid...

  void setAlarm2(uint8_t dydw, uint8_t hour, uint8_t minute, DS3231_alarm2_t mode, bool armed = true);
  setAlarm2(0, 0, computedMinutes, DS3231_MATCH_M);

Where should i declare computed minutes and how would you g about doing it?

We have the same library :)

Declare before use.

Initialize with current minute.

Add 15 to this value.

If the value is now above 59, subtract 60.

Whandall:
Declare before use.

Initialize with current minute.

Add 15 to this value.

If the value is now above 59, subtract 60.

Like i said, a novice… this is what i got:

void computedMinutes()
{
  readDS3231time;
  byte minute+15;
  if byte minute>59
  {
    - 60;
  }
  
}

I know its wrong i’m getting this error code:

expected initializer before ‘+’ token

where have i gone wrong?

Thanks for all the help

Scott

The code you presented is censored that makes any compiler puke.

A very explicit suggestion

byte in15Minutes(DS3231& rtc) {
  RTCDateTime now = rtc.getDateTime();

  now.minute = now.minute + 15;
  if (now.minute > 59) {
    now.minute = now.minute - 60;
  }
  return now.minute;
}

a more terse version

byte in15Minutes(DS3231& rtc) {
  RTCDateTime now = rtc.getDateTime();
  return (now.minute + 15) % 60;
}

Whandall: The code you presented is censored that makes any compiler puke.

A very explicit suggestion

byte in15Minutes(DS3231& rtc) {
  RTCDateTime now = rtc.getDateTime();

  now.minute = now.minute + 15;   if (now.minute > 59) {     now.minute = now.minute - 60;   }   return now.minute; }



a more terse version


byte in15Minutes(DS3231& rtc) {   RTCDateTime now = rtc.getDateTime();   return (now.minute + 15) % 60; } ```

Ok so I've got this just before setup:

void computedMinutes()
{
byte in15Minutes(DS3231& rtc) {
  RTCDateTime now = rtc.getDateTime();
  return (now.minute + 15) % 60;
}
}

 void setAlarm2(uint8_t dydw, uint8_t hour, uint8_t minute, DS3231_alarm2_t mode, bool armed = true);
 setAlarm2(0, 0, computedMinutes, DS3231_MATCH_M);

I'm being struck by:

a function-definition is not allowed here before '{' token

Sorry if these mistakes are really obvious to you guys, i've been battling with the alarms for a week now. :confused:

You should battle with C++ basics, before starting any projects.

You can't declare a function inside another function.

I think this is the problem:

void computedMinutes()  << this is the start of a function
{
byte in15Minutes(DS3231& rtc) {            << and so is this
  RTCDateTime now = rtc.getDateTime();
  return (now.minute + 15) % 60;
}
}

You can’t define a function within a function.