0

I am able to set up one hz interrupts using millis and other timers, but would like to do it instead using the RTC I have attached (DS3231)

I am not sure how I can do that. Can someone point me to a reference for this?

EDIT/UPDATE

I tried to use the square wave to trigger a call back/interrupt, but it's not working for me. I followed what I thought was the wiring diagram here and here and here

I also added a led in the circuit to see if I could figure out something that way, but no dice.

Here is some simple code for a sketch that I think should trigger a 1 second interrupt and call my function:

#include <DS3232RTC.h>    //http://github.com/JChristensen/DS3232RTC
#include <Time.h>         //http://www.arduino.cc/playground/Code/Time  
#include <Wire.h>         //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)

const int rtcTimerIntPin = 2;
bool flag = false;

void setup(void)
{
    Serial.begin(9600);
    setSyncProvider(gRTC.get);   // the function to get the time from the RTC
    if (timeStatus() != timeSet)
        Serial.println("Unable to sync with the RTC");
    else
        Serial.println("RTC has set the system time");

    bool stopped = gRTC.oscStopped();
    if (stopped)
    {
        Serial.println("Detected clock power loss - resetting RTC date");
        time_t newTime = cvt_date(__DATE__, __TIME__);
        adjustTime(newTime);
        setTime(newTime);
    }
    else {
        Serial.println("Clock did not lose power");
    }

    pinMode(13, OUTPUT);    // onboard LED
    pinMode(rtcTimerIntPin, INPUT);
    attachInterrupt(rtcTimerIntPin, rtc_interrupt, RISING);
    gRTC.squareWave(SQWAVE_1_HZ);

    digitalClockDisplay();
}

void rtc_interrupt(void)
{
    flag = true;
}

void loop(void)
{
    //digitalClockDisplay();

    if (flag) {
        digitalWrite(LED_BUILTIN, HIGH);    // flash the led
        digitalClockDisplay();              // this just prints time to serial port 
        delay(500);                         // wait a little bit
        digitalWrite(LED_BUILTIN, LOW);     // turn off led
        flag =  false;                      // clear the flag until timer sets it again
    }
}

void digitalClockDisplay()
{
// do something
}
  • What sort of precision are you looking for? The interrupt (once you get that working) should fire within a few microseconds of when the clock actually generates the signal. Are you looking for microseconds, or milliseconds? What is the purpose of this? If you are just flashing an LED very high precision is probably not required. – Nick Gammon Oct 8 '16 at 3:04
  • And if you just want to flash an LED every second, connect it to the clock output directly! (via a resistor). :P – Nick Gammon Oct 8 '16 at 3:05
  • I want to have a signal so that I do some check/processing every second. For this project tens of milliseconds is fine. It is for a timer to do time lapse photography. – Tim Oct 8 '16 at 20:40
2
+50

This answer addresses the original question of why the interrupts didn't work. I happened to have a DS3231 lying around so I made up a test.


Interrupt vs pin number

First, this is wrong in your code:

attachInterrupt(rtcTimerIntPin, rtc_interrupt, RISING);

You need an interrupt number, not a pin number. This is correct:

  attachInterrupt (digitalPinToInterrupt (rtcTimerIntPin), rtc_interrupt, CHANGE);

I also made it a CHANGE interrupt so you get both the rising and falling pulse.


Pull-up resistor

Next, the 1 Hz output needs a pull-up resistor, so you should add that or make it INPUT_PULLUP like this:

  pinMode (rtcTimerIntPin, INPUT_PULLUP);

Testing

I used this library from Adafruit.

Adapting one of their examples, and putting in your interrupt code (with my modifications), it worked!

#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

const byte rtcTimerIntPin = 2;

volatile byte flag = false;

void rtc_interrupt ()
{
  flag = true;
}  // end of rtc_interrupt

void setup () {

  Serial.begin(115200);

  while (!Serial); // for Leonardo/Micro/Zero

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // enable the 1 Hz output
  rtc.writeSqwPinMode (DS3231_SquareWave1Hz);

  // set up to handle interrupt from 1 Hz pin
  pinMode (LED_BUILTIN, OUTPUT);
  pinMode (rtcTimerIntPin, INPUT_PULLUP);
  attachInterrupt (digitalPinToInterrupt (rtcTimerIntPin), rtc_interrupt, CHANGE);
}

void loop () {
  if (flag) {
    digitalWrite(LED_BUILTIN, HIGH);    // flash the led
    delay(100);                         // wait a little bit
    digitalWrite(LED_BUILTIN, LOW);     // turn off led
    flag =  false;                      // clear the flag until timer sets it again
  }
}
  • I was missing two things: I used INPUT rather than INPUT_PULLUP and I did not have the digitalPinToInterrupt() in the first param for attachInterrupt() thanks! – Tim Oct 8 '16 at 18:35
1

Use this DS3231 library. It has a method to set the squarewave RTC.squareWave(SQWAVE_1_HZ)

  • Thanks Gerben - I will try to set up the squarewave to an input/interrupt. No success yet. – Tim Oct 7 '16 at 6:14
  • You could temporarily add an led, to see if you are getting any signal. – Gerben Oct 7 '16 at 13:46
  • Yep, I tried that - and no luck there. In fact, I used two different 3231 'boards' to see if it was my wiring/soldering. It may very well be that I wrecked two boards, but it seems more likely I am doing something else wrong. I will try to post a stripped down version of what i am doing. – Tim Oct 7 '16 at 19:07
  • You should add a little more detail. – Dat Ha Oct 8 '16 at 22:22
1

I tried to use the square wave to trigger a call back/interrupt, but it's not working for me.

 bool flag = false;
 ...
 void rtc_interrupt(void)
 {
     flag = true;
 }

Flags set in interrupts should be declared volatile otherwise the compiler may optimize away the comparison to the flag. That is:

volatile bool flag = false;

If you are using the Adafruit library (from the page you linked to) then it should have a couple of functions that can be used to detect when the second has changed, for example:

DateTime now = rtc.now();
int second = now.second();
unsigned long unixtime = now.unixtime();

In either case you just have to check if either one has changed in loop. For example:

void loop(void)
{
  static unsigned long previousTime = 0;
  DateTime now = rtc.now();
  unsigned long timeNow = now.unixtime();

  if (timeNow != previousTime) {
      digitalWrite(LED_BUILTIN, HIGH);    // flash the led
      digitalClockDisplay();              // this just prints time to serial port 
      delay(500);                         // wait a little bit
      digitalWrite(LED_BUILTIN, LOW);     // turn off led
      previousTime = timeNow;             // remember previous time
    }
}
  • thanks @nick - I fixed the volatile bit. Note that I want a callback/trigger/squarewave - not to delay()/check the time each iteration of the loop. – Tim Oct 8 '16 at 2:52
  • Does it work once you added volatile? The delay was from your own code. The delay is not part of working out if the second is up. I don't see a huge difference between checking the time from the clock, as you go around loop and checking a flag, each time you go around loop. Reading the time from the clock might take slightly longer, I suppose. – Nick Gammon Oct 8 '16 at 3:03
  • Volatile didn't fix the desired method of having an ISR triggered by a square wave, but I changed the loop() code to look like what you suggested. That works. Thanks for the help. It's going to bother me about the interrupt though... – Tim Oct 8 '16 at 4:07
1

Try the following code. It pretty much detects a change in time and when a change has been detected, it will do something. In this case, it will print "do something".

#include <DS3231.h>
DS3231 rtc(SDA, SCL);
String t; //time as a string
String s;    //second
String Ps;   //previous second

void setup() {
  Serial.begin(115200);
  rtc.begin();
}

void loop() {
  t = rtc.getTimeStr();             //finding time
  s = t.substring(6, 8);            //finding seconds

  if(s != Ps)                       //if the time has changed
  {
    Serial.println("do something"); //put whatever you want here
    Ps = t.substring(6, 8);         //redeclaring the previous second because it has advanced
  }
}
  • 1
    I advise against using the String class for something done every time around loop. String uses dynamic memory allocation, and if you aren't careful you will get heap fragmentation. To calculate seconds just use the "modulus" operator (ie. time mod 60). – Nick Gammon Oct 7 '16 at 22:14
  • I don't think it is possible because the DS3231 libray that I have only returns strings. – Dat Ha Oct 7 '16 at 22:29
  • Can you give a link to the library please? You should be able to get something other than Strings. – Nick Gammon Oct 7 '16 at 23:38
  • RinkyDinkElectronics.com Your the pro so see if you can find anything. Even I would like for it to be able to return ints – Dat Ha Oct 7 '16 at 23:41
  • Well for one thing the prototype is char *getTimeStr(uint8_t format=FORMAT_LONG); which means you get back a char * not a String. You can use char * without needing dynamic memory allocation. Also there is a function long getUnixTime(Time t); which gives you a long. AFAIK Unix Time is in seconds, so just call that and take modulus 60 of the result. – Nick Gammon Oct 7 '16 at 23:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.