Tag Archives: DS3231

Template Program that uses a DS3231 RTC to wake up an Arduino

So, my idea is to use a cheap but reasonably accurate RTC chip module based upon the DS3231 chip to periodically wake a sleeping Arduino. I tried getting it working yesterday, but had little luck. I don’t know whether it was Bailey’s insistence on being petted or simple sleep deprivation, but it eluded me yesterday. Today, I decided to go about it more methodically, and with the help of my trust Rigol DS1102E oscilloscope, I made sure that the module was generating a proper pulse stream, and eventually stumbled on the right order of operations to get this working.

For the purposes of this demonstration, I’ve programmed ALARM_2 to trigger every minute (it fires when the seconds are 00) and then programmed ALARM_1 when the trigger matches 30. This means that the interrupt fires to wake up the Arduino every 30 seconds. When it wakes at the moment, it doesn’t do anything particularly interesting: it just prints the time and temperature (which doesn’t seem right, so I’ll have to dig into why).

Anyway, the code isn’t particularly complex, but there were a few details to work through, and it might be useful in the future. It will require a tiny bit of extra work to (say) trigger every 5 minutes. You basically will have to use the setAlarm function to set the alarm to go off at a particular point in the future, and when that alarm is triggered, then tell it to trigger again 5 minutes in the future. I don’t foresee any trouble.

Anyway, here is the code:

#include <LowPower.h>
#include <DS3232RTC.h>        // https://github.com/JChristensen/DS3232RTC
#include <Streaming.h>        // http://arduiniana.org/libraries/streaming/
#include <Wire.h>

/*  _   _               
 * | |_(_)_ __  ___ _ _ 
 * |  _| | '  \/ -_) '_|
 *  \__|_|_|_|_\___|_|  
 *                      
 * Some simple code to test the idea of using a DS3231 module's "alarm"
 * functions to periodically wake up an Arduino which is in POWER_DOWN
 * mode so that it will do something interesting.   This is really just
 * a skeleton now, but will serve as an outline for a more advanced sensor
 * sketch.
 *
 * Written by mvandewettering@gmail.com
 */

const int wakeupPin = 2 ;

volatile int woken = 0 ;

void
wakeUp()
{
    woken = 1 ;
}

void
setup()
{
    pinMode(wakeupPin, INPUT_PULLUP) ;
    pinMode(LED_BUILTIN, OUTPUT) ;

    Serial.begin(115200) ;
    delay(50) ;
    Serial.println(F("WAKING UP...")) ;

    Serial.println(F("I2C set to 400K")) ;
    Wire.setClock(400000) ;

    setSyncProvider(RTC.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");

    Serial.print("::: INITIAL TIME ") ;
    printDateTime(RTC.get()+7UL*3600UL) ;
    Serial.println() ;
    delay(50) ;

    // initialize the alarms to known values, 
    // clear the alarm flags, clear the alarm interrupt flags
    // ALARM_1 will trigger at 30 seconds into each minute
    // ALARM_2 will trigger every minute, at 0 seconds.
    RTC.setAlarm(ALM1_MATCH_SECONDS, 30, 0, 0, 0);
    RTC.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0, 0);

    
    // clear both alarm flags
    RTC.alarm(ALARM_1); RTC.alarm(ALARM_2);

    // We are going to output the alarm by going low onto the 
    // SQW output, which should be wired to wakeupPin
    RTC.squareWave(SQWAVE_NONE);

    // Both alarms should generate an interrupt
    RTC.alarmInterrupt(ALARM_1, true);
    RTC.alarmInterrupt(ALARM_2, true);
}

void
loop()
{
    // The INT/SQW pin from the DS3231 is wired to the wakeup pin, and
    // will go low when the alarm is triggered.  We are going to trigger
    // on the falling edge of that pulse.
    attachInterrupt(digitalPinToInterrupt(wakeupPin), wakeUp, FALLING) ;

    // Go into powerdown mode, waiting to be woken up by INT pin...
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF) ;

    // For now, let's just ignore transitions on this pin...
    detachInterrupt(digitalPinToInterrupt(wakeupPin)) ;

    // We have to clear the alarm condition to make the pin go back to
    // the high state.   I'm clearing both of them, because we are 
    // triggering every 30 seconds (in this example)
    RTC.alarm(ALARM_1) ; RTC.alarm(ALARM_2) ; 

    // Now, we can do whatever we want to do.  For now, we just get 
    // the current time and the temperature (which doesn't appear to 
    // work, and will be what I'm investigating next)

    printDateTime(RTC.get()+7UL*3600UL) ;
    Serial.print(" ") ;
    Serial.println(RTC.temperature()) ;
    delay(50) ; // allow serial to drain, otherwise we'll go to sleep
		// before the buffer empties.
    
}

void 
printDateTime(time_t t)
{
    Serial << ((day(t)<10) ? "0" : "") << _DEC(day(t)) << ' ';
    Serial << monthShortStr(month(t)) << ' ' << _DEC(year(t)) << ' ';
    Serial << ((hour(t)<10) ? "0" : "") << _DEC(hour(t)) << ':';
    Serial << ((minute(t)<10) ? "0" : "") << _DEC(minute(t)) << ':';
    Serial << ((second(t)<10) ? "0" : "") << _DEC(second(t));
}

If it turns into something more interesting, you all will be the first to know.

Baby steps with the DS3231…

Last night, I did a small amount of work on two projects.

I applied some Wood Bondo to a couple of bad defects in my garden bench project. I hadn’t used it in a while, and forgot what a pleasure it was. Sanding should be finished up shortly and I’ll definitely have it painted and finished this weekend.

The other thing I worked on was trying to prototype the main control loop of my IoT sensor project. Since I got the basic radio hookup done last night, I thought I would work on a slightly different issue.

My recent musing have been concentrating around reducing the power consumption of these boards. The way that is normally achieved is by putting the processor to sleep, effectively telling it to halt the main processor for a predefined amount of time. Then, either in response to a timer event or an external interrupt, the processor can wake up, read all its sensors, and log it to a local SD card or transmit the measurement wirelessly before going back to sleep.

Some microcontrollers have elaborate timing modules that can run while the main processor, but that is not the case for the Arduino. It just has a single “watchdog” timer that can serve to wake the processor back up, and it is relatively inflexible: it can only be set for a small set of intervals, the maximum length of which is eight seconds. It’s also not particularly accurate, since it is clocked by an RC based internal oscillator that operates at 128Khz. You definitely can try track time this way, but it seems a bit complicated and a little messy

As it happens, I have a fair number of DS3231 clock modules lying around. The specifications have an accuracy of ±2ppm from 0°C to +40°C , which translates to an accuracy of about one minute per year. I could live with that.

The DS3231 also includes an alarm module. This enables you to program two alarm registers with times that will trigger the alarm when matched. There are two registers (ALARM_1 and ALARM_2) and you can query whether either has been triggered, or you can configure the the chip to toggle the SQW output pin low when the alarm goes off. I found this primer to be pretty helpful. What was not helpful was that my cat Bailey kept on jumping into my lap while I was trying to concentrate, so while I managed to get the basic “polling” based code to work, I couldn’t quite wrap my head around getting the sleep mode and interrupt stuff to work. I think that tonight I’ll pull out my oscilloscope to ensure that the proper signals are being generated by the board, and then at least narrow my confusion to where I can hopefully eliminate it.

In the mean time, I’ve been spending some of my spare time reading all the tons of useful information on The Cave Pearl Project website, including this awesome collection showing how build their Pro Mini Data Logger. The accompanying video play list shows lots of tiny bits of cleverness in how they prep the various boards and attach them together in a clever way to build a nifty little datalogger for less than $10 in parts. Well worth your time to watch.