Pages: [1]   Go Down
Author Topic: Yet-Another DS1337 RTC library  (Read 4835 times)
0 Members and 1 Guest are viewing this topic.
Boston, MA
Offline Offline
Full Member
***
Karma: 0
Posts: 129
Batteries? We don't need no steenking batteries!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,
As part of a low-power Arduino-based project I'm working on, I wrote a DS1337 realtime clock library based on an example I found floating around on the internet ... looks like it was probably an old version of mattt's or xSmurf's libraries here.

Available here - includes an example sketch which shows and tests all the most important functions.

Mainly, I wrote in some routines to interchange easily between calendar date/times and epoch seconds (# of seconds elapsed since 1/1/2000, correctly accounting for leap years). Working in epoch seconds makes it much easier to work with relative times, e.g. "wakeup 15 seconds from now", add/subtract times and set alarms without worrying about what happens if the seconds/minutes/etc. roll over in between.

Now, it turns out xSmurf's library has now had this feature for quite some time, under the name UTS (unix time stamp) - noticed when I went to post this one - so this turns out to be mostly just a learning project for me after all ;-) Still, it's an alternate approach to the calculation that's probably a bit more straightforward to understand, and includes a complete set of examples, so hopefully someone finds it useful :-)

Public functions:
DS1337();

unsigned char time_is_set(); // set TRUE when writeTime() is called; set FALSE by power loss or other clock stoppage / oscillator failure
unsigned char alarm_is_set();

unsigned char enable_interrupt();
unsigned char disable_interrupt();
unsigned char clear_interrupt();

void    readTime();
void    readAlarm();
void    writeTime();
void    writeTime(unsigned long);
void    writeAlarm();
void    writeAlarm(unsigned long);
void    setAlarmRepeat(byte repeat);

unsigned char getSeconds();
unsigned char getMinutes();
unsigned char getHours();
unsigned char getDays();
unsigned char getDayOfWeek();
unsigned char getMonths();
unsigned int getYears();
unsigned long date_to_epoch_seconds(unsigned int year, byte month, byte day, byte hour, byte minute, byte second);
unsigned long date_to_epoch_seconds();
void epoch_seconds_to_date(unsigned long);

void setSeconds(unsigned char);
void setMinutes(unsigned char);
void setHours(unsigned char);
void setDays(unsigned char);
void setDayOfWeek(unsigned char);
void setMonths(unsigned char);
void setYears(unsigned int);


void      start(void);
void      stop(void);
unsigned char getRegister(unsigned char registerNumber);
void      setRegister(unsigned char registerNumber, unsigned char registerValue);
Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 471
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Great Drmn4ea!

I was trying other libraries and i was coming crazy.. for the moment, i tested it under arduino duemilanove and arduino IDE 017, and it compiles...

Cheers!

[edit]And it also works. I used the example sketch changing the initial pin definitions for arduino Duemilanove, and runs perfectly!

Good job Drmn4ea1[/edit]
« Last Edit: November 25, 2010, 05:53:12 pm by madepablo » Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 471
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Drmn4ea,

I try to use your library in my project. I studied your library, but i still have some questions

In my project, i want to send the arduino to sleep, and get it up once per hour (at o'clock time), and read different sensors and send it to sleep again. I have some questions about how to use the alarm in that case. I thin that this is the steps that i must follow, but may be you could confirm that:
1) set the alarm time to minute 0 and second 0 (not to set hours, days, DoW, or year)
2) set the alarm type to Every_hour
3) Setup the alarm under this conditions...

Is it true?

An, what happend if i want to getup the arduino every 5 minutes (at minutes 5, 10, 15.... 55, 00)?

Setting the alarm to EVERY-Mimute is so much, and for EVERY_Hour, there is not way to save all those minutes...

And my last question, is it possible to set more than one alarm at the same time. I mean... for example i want to get up the arduino once per week and an specific moment to do something, and everyday, at a different moment to do something different... Is it possible by the use of your library? I know that latter i must check by code if the alarms correspond to the daily alarm or to the weekly alarm, ¿but is it possible to set different alarms at the same time?

Thanks so much for your time and patience, but also for your efforts to develop the new library.

Cheers,
Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 471
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am trying to learn how to use this library. I have a lot of head pain, but form the moment i was able to have a simple example to trigger an alarm once per minute (when time rise second 30).

Code:
#include <Wire.h>
#include <DS1337.h>                          
#include <avr/power.h>
#include <avr/sleep.h>                      

#define alarm 2                            
#define led 13                              

DS1337 RTC = DS1337();                      

unsigned int year = 0000;                          
unsigned int month = 00;                            
unsigned int day = 00;                              
unsigned int hora = 00;                              
unsigned int minuto = 00;                            
unsigned int segundo = 00;                            
float innerVcc;                            
float innertemp;

void setup(){
  // Inicializacion RTC
  RTC.start();
  // Configuración de la alarma
  RTC.enable_interrupt();
  RTC.setSeconds(30);
  //RTC.setMinutes(52);
  RTC.setAlarmRepeat(EVERY_MINUTE);
  RTC.writeAlarm();
  pinMode(alarm, INPUT);                   // Receptor de alarmas
  digitalWrite(alarm, HIGH);
  // Configuración de pins
  pinMode(led, OUTPUT);                     // Led de vida
  // Inicializacion del puerto serie
  Serial.begin(9600);                        // Inicia comunicaciones
  Serial.println("Testing alarms RTC DS1337.... ");
  Serial.println();
}

void loop(){
  digitalWrite(led, HIGH);                // Enciende el led mientras realiza las lecturas
  time();                                // Toma la hora
  delay(20);
  readVcc();                               // Lee el voltaje interior
  delay(20);
  readTemp();  
  delay(20);
  digitalWrite(led, LOW);                 // Apaga el led al acabar el proceso
  Show();
  delay(500);
  SleepNow();
}

void WakeUpNow(){
}

void SleepNow(){
  attachInterrupt(0, WakeUpNow, FALLING);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  RTC.enable_interrupt();
  sleep_mode();
  // -->SLEEPING PERIOD
  sleep_disable();
  RTC.disable_interrupt();
  detachInterrupt(0);
}

// Lee el Reloj de Tiempo Real (DS1337)
void time(){
  RTC.readTime();
  day = RTC.getDays();
  month = RTC.getMonths();
  year = RTC.getYears();
  hora = RTC.getHours();
  minuto = RTC.getMinutes();
  segundo = RTC.getSeconds();
  return;
}

//Lee el voltaje interno
long readVcc() {
  long readVcc=0;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  readVcc = ADCL;
  readVcc |= ADCH<<8;
  innerVcc = 1126400L / readVcc; // Back-calculate AVcc in mV
  innerVcc = innerVcc / 1000;
  return innerVcc;
}

// Obtiene la temperatura interna de dispositivo Arduino
long readTemp() {
  long readTemp=0;
  // Read temperature sensor against 1.1V reference
  ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
  delay(20); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  readTemp = ADCL;
  readTemp |= ADCH<<8;
  innertemp = (readTemp - 125) * 1075; // temp in x10^-4 degC
  innertemp = innertemp / 10000; // temp in degC
  return innertemp;
}

// Pantalla de inicio
void SplashScreen(){
   Serial.println("Alarms examples RTC DS1337");
   Serial.println();
   time();
   Serial.print(day);
   Serial.print("/");
   Serial.print(month);
   Serial.print("/");
   Serial.print(year);
   Serial.print("   -   ");
   Serial.print(hora);
   Serial.print(":");
   Serial.print(minuto);
   Serial.print(":");
   Serial.println(segundo);
   Serial.println();
   Serial.println("# ; Date ; Time ; Vcc ; Tint ");
   return;
}


// Muestra las mediciones a través del puerto serie
void Show(){
  Serial.print(day);
  Serial.print("/");
  Serial.print(month);
  Serial.print("/");
  Serial.print(year);
  Serial.print(" ; ");
  Serial.print(hora);
  Serial.print(":");
  Serial.print(minuto);
  Serial.print(":");
  Serial.print(segundo);
  Serial.print(" ; ");
  Serial.print(innerVcc);
  Serial.print(" ; ");
  Serial.println(innertemp);
  return;
}

It only require a led connedted to pin 13 and GND, and the DS1337 RTC correctly wired. The example read innerVCC and innertemp from MCU.


I will try other options of this library and to post here the results.
Improvements and corrections to this example are welcome, since it was developed by a try-error methodology...

Enjoy it!
« Last Edit: November 30, 2010, 04:48:48 am by madepablo » Logged

Boston, MA
Offline Offline
Full Member
***
Karma: 0
Posts: 129
Batteries? We don't need no steenking batteries!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
In my project, i want to send the arduino to sleep, and get it up once per hour (at o'clock time), and read different sensors and send it to sleep again. I have some questions about how to use the alarm in that case. I thin that this is the steps that i must follow, but may be you could confirm that:
1) set the alarm time to minute 0 and second 0 (not to set hours, days, DoW, or year)
2) set the alarm type to Every_hour
3) Setup the alarm under this conditions...

Is it true?

Sounds right to me! Doing as you described will alarm every hour on the hour (0 minutes 0 seconds). Setting minutes to 15 before writing the alarm would set it off every hour at 15 minutes after the hour, etc.

Quote
An, what happend if i want to getup the arduino every 5 minutes (at minutes 5, 10, 15.... 55, 00)?

Setting the alarm to EVERY-Mimute is so much, and for EVERY_Hour, there is not way to save all those minutes...

The RTC hardware itself can only alarm at times when one or more alarm fields match the time fields - that is, when the seconds match (EVERY_MINUTE) or when the seconds and minutes both match (EVERY_HOUR), etc. But there are a couple ways to alarm every 5 minutes. One way is to get the current time in epoch seconds, add 5 minutes (300 seconds) to it, and write this as the new alarm every time you wake up. The second way is to set the alarm for every minute and count how many times you woke up, do something only every 5 time (else just go back to sleep).


Quote
And my last question, is it possible to set more than one alarm at the same time.

In the current version above, no. To keep things simple I only implemented one alarm and interrupt pin - using both alarms complicates things a bit because they have different resolutions (the 2nd alarm cannot support seconds) and decoding of the mask bits is different. It'll need another variable for ALARM2 repeats and a way to check which alarm woke it up (and/or steer the ALARM2 interrupt to the INTA or INTB pin). Maybe in the next release :-) (unless someone wants to beat me to it, and I'll just wrap their changes in!)
Logged

Offline Offline
Sr. Member
****
Karma: 0
Posts: 471
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Drmn4ea!

I see now how it runs... thanks you. About the second alarm, Iit is possible do have a way to have it just checking time or date when it gets up by the first alarm. So, i could go ahead without the second alarm.

I had a lot of problems testing the library with my own code, because the normal sleep code does not use sleep_cpu command... so i was coming crazy. An you can see that i also needed to include "RTC.enable_interrupts and RTC.disable_interrupts in the SleepNow section.Otherwise, ones the RTC trigerred the alarm, the arduino was even up... without to go to sleep anymore...

But now, the example code runs perfect, and i have your library more or less (more less than more) under control. But i will ask you more things in a nearest future.

Thanks for you work!
Logged

Cologne
Offline Offline
God Member
*****
Karma: 11
Posts: 506
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Drmn4ea,
thanks for this library.
Only one question: why did you choose 01/01/2000 as basis for the epoch time? The unix time stamp is based on 01/01/1970. It took me some time to catch this. I will have a look at the sources and try to change the base. If I succeed I will share the results.
Reinhard
Logged

Boston, MA
Offline Offline
Full Member
***
Karma: 0
Posts: 129
Batteries? We don't need no steenking batteries!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

This is because the DS1337 itself uses 1/1/2000 as its epoch. If your project needs to interoperate with something that takes UNIX time, the easiest way is to add or subtract the # of seconds between the two (946684800 seconds).  smiley

Logged

Cologne
Offline Offline
God Member
*****
Karma: 11
Posts: 506
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Drmn4ea,

Quote
the easiest way is to add or subtract the # of seconds between the two (946684800 seconds)
this is exactly the way I solved the problem. Additionaly you have to take care of the timezone, because unixtime is always UTC.

Another question according alarms:
I found out that I have to use PIN3 of the ds1337 for alarm output. I wrote a sketch (without sleep mode) to alarm every minute, but the alarm happens only once. I found that the level at PIN3 stays LOW after the first alarm.
How can I get repeating alarms?
« Last Edit: December 27, 2010, 02:21:30 pm by erni-berni » Logged

Boston, MA
Offline Offline
Full Member
***
Karma: 0
Posts: 129
Batteries? We don't need no steenking batteries!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The alarm going off sets an interrupt flag on the DS1337, causing it to hold the line down until acknowledged. After each alarm occurs, make sure to call clear_interrupt() to acknowledge (clear the interrupt flag) so the DS1337 releases the line.
Logged

Cologne
Offline Offline
God Member
*****
Karma: 11
Posts: 506
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes that's right. But it is not possible to call RTC.clear_interrupt() in the interrupt handler. If I do so the program hangs.
Logged

Boston, MA
Offline Offline
Full Member
***
Karma: 0
Posts: 129
Batteries? We don't need no steenking batteries!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah, I noticed that too. My guess is the Wire library itself uses interrupts, so any I2C access via Wire when interrupts are disabled (including already inside an interrupt handler) will hang. My workaround for this was to place the clear_interrupt() call in user code immediately following the cpu_sleep() and attach an empty handler to the alarm interrupt, just to give it somewhere to wake up to. If I/O to the RTC is really needed inside an interrupt handler, it will probably be necessary to use a different (interrupt-free) I2C library in place of Wire.
Logged

Pages: [1]   Go Up
Jump to: