DS3231 RTC with EEP...
 
Notifications
Clear all

DS3231 RTC with EEPROM, Square wave and alarms.  

Page 1 / 2
  RSS

dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-11 12:26 am  

My intro outlined my project goals. So far, I've been able to set the system time using the RTC and also use the Square Wave function (at 1Hz) to create an interrupt-driven display on the OLED (note that using a delay call in the main loop isn't that smooth and sometimes skips seconds etc). 

Next, I'll be testing the alarms. An alarm will raise the SQW pin HIGH (on the RTC) at the designated alarm time .. .so I can't use the 1Hz signal and the alarm signal together (they use the same pin on the RTC). No biggy, since I intend to put the micro-controller to sleep after setting the alarm and letting the alarm wake the micro-controller with an interrupt). The intent of the push button (see diagram) is to add 1 minute to the current time to set the alarm after turning off the 1Hz square wave and shutting down the micro-controller.

Perhaps my current confusion is pullup vs. pulldown. The interupt pins (two of them on the Nano) have built in resistors so INPUT_PULLUP for mode and "FALLING" for trigger seem to work fine for the RTC signals, but I added a resistor to the push button to stabilize it. It works as wired in the diagram, but I'm not sure it's correct to drain 5V+ to ground through the resister.

clock display v1

Quote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-11 3:31 am  
download 20200210 210710

ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-13 11:00 pm  

I was able to put the Nano into a deep sleep state (so-called power off) and use the external RTC to wake it back up.  Victory! I initiate the sleep using the push-button, which sets the alarm on the RTC to go off at the top of the next minute. The OLED display shows the time sleep started (S) and scheduled wake-up (W). See attached photo.

I have a pretty good idea that the Nano actually went to sleep (despite the power LED staying on and voltage continued to flow from pins 3v3 and 5V) because when it woke-up, it lost time.  I now reset system time after wake-up by polling the RTC.

I also rewired the RTC and OLED to run off the 3V3 pin. The specs on both these devices indicate that I2C works with either 5V or 3V without a logic converter.  Nice! See attached wiring diagram. 

Next up is storing/retrieving a task schedule from the EEPROM - i.e. a series of date/time to start/stop. I'm exploring JSON or MessagePack for storage format as these seem fairly easy to convert to C data types.

I'll post my sketch once I clean up some of the code I got from various examples. I'm discovering that many examples are poorly written (IMHO). Bill gives good advice when he says "read the [library] code". 

20200213 150457
clock display wiring v2

 

References:
----------------------------
JSON/MessagePack - https://arduinojson.org/
Arduino DS3232RTC Library - https://github.com/JChristensen/DS3232RTC
DS3231 device specs:

RockScream Low Power library - https://github.com/rocketscream/Low-Power
Adafruit OLED Library for SSD1306 - https://github.com/adafruit/Adafruit_SSD1306
SD1306 I2C device specs:

This post was modified 5 months ago 2 times by dxj

ZeFerby liked
ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-16 12:37 am  

I cleaned up the code a bit before publishing here.

It was difficult to find useful and complete examples for the DS3231, particularly on how to setup alarms that invoke an interrupt rather than a constant polling from the main loop() . The use of an interrupt made it possible to wake up a sleeping micro-controller when the alarm went off.

Sketch uses 10872 bytes (35%) of program storage space.
Global variables use 612 bytes (29%) of dynamic memory.

Hopefully, anyone coming along later will find my code and wiring diagram useful.

/* Setting system time using RTC, display date/time and clock temp.
Breadboard tested on Nano and Uno R3 with DS3231/AT24C32 Clock Module
Author: David X Johnson - 2020 */

/* ---

Arduino DS3232RTC Library
Device specs:>




Library source:> https://github.com/JChristensen/DS3232RTC
Pinout (I2C): SDA/A4, SCL/A5, 5V, GRD */
#include <DS3232RTC.h>

/* ---
RockScream Low Power library
Library source:> https://github.com/rocketscream/Low-Power <*/
#include "LowPower.h"

/* ---
Arduino Wire Library allows you to communicate with I2C / TWI devices.
Source: https://www.arduino.cc/en/Reference/Wire */
#include <Wire.h>

#define RTC_SQW_PIN 2 // RTC provides a 1Hz interrupt signal - also known as D2 on the Nano
#define SLEEP_BUTTON_PIN 4 // button push interupt pin - also known as D4 on Nano
#define SLEEP_BUTTON_PAUSE 1000 // 1 second pause before another button push is accepted */

/* global vars for pushbutton logic and clock interrupt */
volatile uint8_t sqwState; /* value set by interrupt handler clockTick() */
uint8_t lastSqwState;
unsigned long lastSleepButtonPush = millis();

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

/* setup interupt pin for the clock, input pin for the button */
pinMode(SLEEP_BUTTON_PIN, INPUT);
pinMode(RTC_SQW_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(RTC_SQW_PIN), clockTick, HIGH);
/* enable clock square wave interrupts */
setSqw(true);
}

/* main loop time-share across scensors and interrupts */
void loop() {
showTimeTemp();
sleepButtonPush();
}

/* 1Hz interrupt handler */
void clockTick() {
sqwState = digitalRead(RTC_SQW_PIN);
}

/* reset alarms, disable alarm interrupts */
void clearAlarms() {
RTC.alarm(ALARM_1); /* Returns true/false for given alarm if it has been triggered; resets the alarm flag bit. */
RTC.alarm(ALARM_2);
RTC.alarmInterrupt(ALARM_1, false); /* Enable or disable an alarm "interrupt" which asserts the INT pin on the RTC. */
RTC.alarmInterrupt(ALARM_2, false);
}

/* enable/disable 1Hz square wave signal from RTC */
void setSqw(bool enable) {
if (enable) RTC.squareWave(SQWAVE_1_HZ);
else RTC.squareWave(SQWAVE_NONE);
}

/* sleep button-push handler
set an alarm for ~1 minutes in the future
put the micro-controller to sleep
wait for clock interrupt when alarm fires */
void sleepButtonPush() {
/* only accept first button push and filter out button bounce for a second (or more) */
if ((digitalRead(SLEEP_BUTTON_PIN) == HIGH) && (millis() - lastSleepButtonPush > SLEEP_BUTTON_PAUSE)) {
lastSleepButtonPush = millis();
setSqw(false); /* disable clock square wave interrupts */
clearAlarms(); /* prep before setting new alarm */

/* calculate both current and wakeup time */
time_t sleepTime = RTC.get();
time_t wakeTime = addTime(sleepTime, 60); /* add 60 seconds to sleepTime */

/* display alarm test notice */
showSetAlarmNotice(sleepTime, wakeTime);
delay(100); */ allow serial monitor to catch up */


/* set the alarm and put micro-controller to sleep */
RTC.setAlarm(ALM1_MATCH_HOURS, minute(wakeTime), hour(wakeTime), 1);

RTC.alarmInterrupt(ALARM_1, true);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

/* interrupt takes micro-controller out of sleep */
clearAlarms();
setSyncProvider(RTC.get); /* reset system time to match RTC */
setSqw(true); /* enable clock square wave interrupts */
}
}

/* add number of seconds to given time to get wake-up time*/
time_t addTime( time_t sleepTime, int seconds) {
tmElements_t scratch;
breakTime(sleepTime + seconds, scratch);
scratch.Second = 0; /* clock alarm ignores seconds */
return makeTime(scratch);
}

/* show alarm test message */
void showSetAlarmNotice( time_t sleepTime, time_t wakeTime ) {
char buff[50];
snprintf(buff, sizeof buff, "Sleep: %02d:%02d:%02d - Wake:%02d:%02d:%02d", hour(sleepTime), minute(sleepTime), second(sleepTime), hour(wakeTime), minute(wakeTime), second(wakeTime));
Serial.println(buff);
}

/* show the current date/time & clock temp */
void showTimeTemp() {
if (sqwState == HIGH && lastSqwState != sqwState) { // only print time once a second
char buff[50], tempString[10]; /* used to convert float to string */
/* convert clock temp from float to 3.1 format in degrees Fahrenheit */
dtostrf((RTC.temperature() / 4.0 * 9.0 / 5.0 + 32.0), 3, 1, tempString);
/* Get date/time/temp into a readable format and output to the serial monitor */
snprintf(buff, sizeof buff, "Time: %02d:%02d:%02d %3s %02d/%02d/%4d - Temp: %s (F)", hour(), minute(), second(), dayShortStr(weekday()), month(), day(), year(), tempString);
Serial.println(buff);
}
lastSqwState = sqwState;
}

 

This post was modified 5 months ago 3 times by dxj

ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-16 2:49 pm  

@dxj

Code looks good to me, but in my opinion (whatever that's worth :-)), I would for a start, break up the function "showTimeTemp()" into three functions - Date, Time and Temp to promote loose coupling and re-usability, as a function should only do one thing, but do one thing good. 🙂

Cheers!


dxj liked
ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-16 6:07 pm  

@frogandtoad

Thanks for the feedback. Discussions on code readability, maintainability, efficiency and re-usability are generally welcome and well received by me. There is always room for improvement.

In my experience, the re-usability - for the sake de-coupling - argument comes up a lot, but is often opinionated and misses the point. Don't get me wrong ... I strongly agree with the principal if I were writing library functions for general consumption.

Often, a more salient argument for re-usability is general utility.  If nobody wants to use your re-usable de-coupled functions - because they are too narrow in their utility - then are they actually re-usable?

I'm happy to engage in code quality discussions - perhaps elsewhere in the forum - but it is my sincere intent for this topic/message to provide more complete and useful examples of how to incorporate a DS323x RTC into a project. It has some unique and compelling features over other RTC packages.

So far, I've covered the time retrieval, square wave function, alarms, both 3.3/5V on the I2C and the external interrupt. I have yet to play with the 32K oscillator and EEPROM. Those are next.

Maybe we should start a separate forum on code best practice (if there isn't one already). I've injected comments about code quality in other topics. In retrospect, I wasn't adding much value with my comments at a time when the original poster was still trying to figure out how to make a piece of hardware work for them. ?

Regards

This post was modified 5 months ago 2 times by dxj

ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-16 9:23 pm  

Console output:

DS3231 Basics Monitor

ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-17 3:01 am  

@dxj

Posted by: @dxj

Thanks for the feedback. Discussions on code readability, maintainability, efficiency and re-usability are generally welcome and well received by me. There is always room for improvement

Thanks for responding, and glad that you didn't view my comments in a negative way.

Posted by: @dxj

In my experience, the re-usability - for the sake de-coupling - argument comes up a lot, but is often opinionated and misses the point. Don't get me wrong ... I strongly agree with the principal if I were writing library functions for general consumption.

Indeed, I agree with this view in general, as most people here are just happy to just get their device working, and not delve into the structure of their code too deeply.  I personally like to have my functions as small as possible, which being new to electronics help's me debug my code a lot easier.

Posted by: @dxj

Often, a more salient argument for re-usability is general utility.  If nobody wants to use your re-usable de-coupled functions - because they are too narrow in their utility - then are they actually re-usable?

I guess this depends on what "too narrow in their utility" really means?  I do believe that a function should only do one thing and do it well, which becomes a loosely coupled and reusable piece of code by default.  For example, we shouldn't write a sqrt() math function that also performed a count of something and maintained the time... the sqrt() function should only perform the square root of a number and nothing more.  This kind of function is less about loose coupling and more about performing and focusing on it's intended task.

Posted by: @dxj

I'm happy to engage in code quality discussions - perhaps elsewhere in the forum - but it is my sincere intent for this topic/message to provide more complete and useful examples of how to incorporate a DS323x RTC into a project. It has some unique and compelling features over other RTC packages.

Me too, and please don't get me wrong... I'm not questioning your sincerity at all, just passionate about coding clarity and design.  There is a programming area where we can post these types of discussions, so maybe I will start a topic there for us passionate programmers to exchange some views and ideas in the near future.

Looking forward to your next update, on how you incorporate the 32K oscillator and EEPROM 🙂

Cheers!


dxj liked
ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-19 3:26 am  
Posted by: @frogandtoad

I do believe that a function should only do one thing and do it well, which becomes a loosely coupled and reusable piece of code by default. 

Agree whole-hearted. In my mind, my function does one thing ... display a line of data for illustration purposes. ? 

Actually, the showTimeTemp() func was once an OLED display function ... and if you've dealt with the SSD1306 library, you'd like it, I'm guessing. You call seperate functions to clear the buffer, set a font scale, set the color, set the x, y coordinates, draw a box around it and finally paint the display with the buffer. Every function does one thing.

I end up creating an aggregate function to display the clock screen (gets called every sec) and it in turn repeatedly called a wrapper function I created: printAt(x,y,text,scale,color). It saved me a lot of lines of code and made the code a lot easier to read and maintain.

I stripped out all the OLED stuff before publishing my code (to make it simpler and about the RTC and alarm interrupts).

I suppose my pet peeves have to do with readability, maintainability and reducing redundant code.

Regards.


ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-19 3:42 am  
Posted by: @frogandtoad

Looking forward to your next update, on how you incorporate the 32K oscillator and EEPROM

I haven't found a good use for the 32K oscillator yet. I can turn it off the save power, however. ? 

For the EEPROM, I've gotten overly ambitious, I'm afraid. I'm looking at a library named littleFS that turns any block storage device into a file system. It's designed to be atomic; meaning all operations either happen or they don't. No corruption if you lose power or receive an interrupt in the middle of an operation. You supply the four functions for block operations and it provides the file level functionality.


ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-19 3:46 am  
Posted by: @frogandtoad

Looking forward to your next update, on how you incorporate the 32K oscillator and EEPROM

I haven't found a good use for the 32K oscillator yet. I can turn it off the save power, however. ? 

For the EEPROM, I've gotten overly ambitious, I'm afraid. I'm looking at a library named littleFS that turns any block storage device into a file system. It's designed to be atomic; meaning all operations either happen or they don't. No corruption if you lose power or receive an interrupt in the middle of an operation. You supply the four functions for block operations and it provides the file level functionality.

https://github.com/ARMmbed/littlefs


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-19 10:36 am  

@dxj

Posted by: @dxj

I suppose my pet peeves have to do with readability, maintainability and reducing redundant code.

My passion... I love to program, and challenging myself to refactor code to oblivion is good fun! 🙂

Cheers!


ReplyQuote
frogandtoad
(@frogandtoad)
Reputable Member
Joined: 1 year ago
Posts: 455
2020-02-19 10:38 am  

@dxj

Posted by: @dxj

I haven't found a good use for the 32K oscillator yet. I can turn it off the save power, however. ? 

Wish I knew much more about electronics than I currently so, but I'm slowly getting there!  Again, looking forward to your progression on this project.

Cheers.


ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-02-20 4:43 pm  
Posted by: @frogandtoad

Wish I knew much more about electronics than I currently so, but I'm slowly getting there!  

We're in the same boat ... which is why I asked to join the forum.


ReplyQuote
dxj
 dxj
(@dxj)
Eminent Member
Joined: 5 months ago
Posts: 25
2020-03-03 12:28 am  

I put this project away for a while as life happened. I discovered a few things in the process:

1) The coin battery on the RTC drains if sharing the same power supply as the Nano. I measured voltage on the coin battery after it recharged - it's good. I attempted a remedy for the drain by putting the RTC on it's own power supply and using a diode to connect the nano to the common ground (effectively isolating the RTC, in theory).

2) The RTC retains alarm data if power off (using the coin battery for backup). If you turn the RTC off after the alarm is set, it triggers an interrupt when turned back on, even if the alarm time has passed. This is not a problem, it just demonstrates that the RTC retains both time and alarm when powered off.

I'll leave it powered down overnight to see if it drains completely again. 

This post was modified 4 months ago 2 times by dxj

ReplyQuote
Page 1 / 2