Alarm clock with DS1302 RTC

 Posted by:   Posted on:   Updated on:  2018-12-24T10:27:15Z

Build an Arduino powered alarm clock with DS1302 RTC module and alphanumeric LCD

DS1302 is a timekeeping chip with battery backup and general purpose RAM. It has been replaced by newer variants like DS1307 or DS3231 which have improved accuracy. The newer ones have I2C interface, but DS1302 does not. It communicates with the host using a serial protocol that resembles SPI. Since I had a module with this chip, I decided to build and test an alarm clock. I added an Arduino, the alphanumeric LCD, an active buzzer and some buttons.

There are some great advantages when using an RTC (real time clock) module in your project. You don't have to do timekeeping in your code (if you read my previous post, you can see it's not quite easy) and, very important, time keeping is performed from backup battery when your development board is not powered. This means you don't have to set date everytime you plug in the Arduino. The advantage is obvious for projects that depend on accurate time to operate correctly.

Arduino alarm clock with DS1302 RTC module

To build this circuit, I used an ATmega328p Arduino Nano compatible board. I wired an alphanumeric LCD with 2 rows of 16 characters. Some buttons are needed to let the user set date and time and a buzzer can ring the alarm. I used an active buzzer (from a kit) because it can be wired directly to a digital pin and beeps when powered. No need to generate beep frequency in code, no need for additional parts.

DS1302 modules uses 3 Arduino pins for serial clock, serial data I/O and chip reset. The protocol resembles SPI, but it's a bit different. I chose to bitbang the serial protocol on any digital pins. The code is inspired from RTClib by NeiroNx. DS1302 also features integrated charger for the backup battery, but the module comes with CR2032 socket and the battery I used is not rechargeable. Anyway, the datasheet states DS1302 uses less than 300nA to keep the clock running. The code will not turn on the charger. Let's have a look at the schematic.

Schematic of Arduino alarm clock with DS1302 RTC module

Schematic of Arduino alarm clock with DS1302 RTC module

A preset resistor is needed to set LCD contrast and a current limiting resistor is required for backlight. To keep the number of additional parts to a minimum, the four push buttons pull low pins with internal pull-ups enabled. Even though those are analog pins, they can be used as well for digital I/O. I had some issues with the RTC module. I could not read valid data from it until I took it out from the socket and soldered it directly on the module PCB. Also I had to test different connection wires until I managed to get consistent data from DS1302.

DS1302 stores and updates time in registers, in BCD (binary coded decimal) format. Registers base address is 0x80. You have acces to a register's value by first sending its address to DS1302. If you send a specific register address, you can read or write all registers consecutively (burst mode). The address is 0xBF if you want to read the RTC and 0xBE when setting the date and time (write mode).

Overview of DS1302 RTC registers

Overview of DS1302 RTC registers (from datasheet)

My sketch reads all date and time variables in burst mode and displays them. But when you adjust time, each variable is sent to DS1302 as soon as you set it. It's easy to send a serial byte using the Arduino function shiftOut(). Reading a byte still needs to be performed one bit at a time.

void rtc_write(byte val) {
  pinMode(RTC_DAT, OUTPUT);
  shiftOut(RTC_DAT, RTC_CLK, LSBFIRST, val);
}

byte rtc_read() {
  pinMode(RTC_DAT, INPUT);
  byte value = 0;
  for (byte i = 0; i < 8; i++) {
    value |= (digitalRead(RTC_DAT) << i);
    digitalWrite(RTC_CLK, HIGH);
    digitalWrite(RTC_CLK, LOW);
  }
  return value;
}

The main loop() increments seconds (once per second, obviously) and when they reach 60, all date and time variables are set from RTC. I consider there is no need to poll the RTC once per second for all variables. After 300 lines of code I had a fully functional clock with date and time display and possibility for adjustment using three buttons.

The SET button cycles through time variables and alows you to adjust them using UP and DOWN buttons. Cycling is performed in the following order: year > month > day > day of week > hour > minute. When you push the SET button, the variable that you had displayed on LCD and you may have adjusted is sent to DS1302. The next one to adjust is displayed. Minutes are the last to be edited and when you press SET after adjusting them, the seconds are also reset to zero and clock halt is cleared just in case. Write protection is disabled once you enter edit mode and then it is restored. The clock remains forever in edit mode unless you push SET button to return to normal mode.

Let's have a look at some fragments of code. Bringing up variables for adjustment is done in a switch() statement. Here is the case for day:

    case 3: // day
      rtc_write_reg(0x88, bin2bcd(MM));

      lcd.setCursor(4, 0);
      lcd.print("Day");
      lcd.setCursor(6, 1);
      if (dd < 9) lcd.print('0');
      lcd.print(dd, DEC);
      break;

See the first thing that it is done is to send month to DS1302 because that's what you were editing before you entered day edit mode. Then the day is printed on LCD. The adjustment is performed when you press UP/DOWN buttons. UP calls a function with numeric argument set to 1, while DOWN calls the same function with -1. A switch() statement performs adjustment of selected variable.

    case 3: // day
      t = dd; t += a;
      if ((t > 0) && (t <= daysOfMonth[MM])) dd = t;
      if (dd < 10) lcd.print('0');
      lcd.print(dd, DEC);
      break;

A temporary (signed) variable t stores the current time variable that will get adjusted. This is adjusted and then checked for validity. If it remains between boundaries, it is copied to the local time variable and displayed. Only after you push SET it will be sent to DS1302 in the previously explained routine.

For the alarm setting I had two options. I can store it in the EEPROM of ATmega328p or I can use the RAM of DS1302. I chose the second option. There are three variables (alarm enabled, alarm hour and alarm minute) which are stored in the first three addresses of the RAM. Alarm is set with the ALARM button. When it is enabled you will see the * sign at the right of the first row. When the current time matches alarm setting, the alarm turns on and off for one minute unless you push the ALARM button to stop it.

In the end, this project needed more than 400 lines of code. The complete sketch can be downloaded from GitHub. DS1302 modules can be found here.

18 comments :

  1. excuse me sir if the alarm wants to go off for 5 minutes what do you think?

    ReplyDelete
  2. Dear Cornelius,

    we like your project very much. I have rebuild your project with my son (he is 8 Years). He enjoy your schematics and connected all parts together on a breadboard. Very clear and easy to understand schematic. Now the next step is build a housing and assemble all parts finally together without breadboard. Many thanks and greetings from Austria Maximilian and Ronald.

    ReplyDelete
  3. Hello Dear Cornelius,

    I did that project but i have a matter:
    When the second is 60+ the minute is not increasing and seconds keep counting from zero. Can you help me about solving the problem ?

    ReplyDelete
    Replies
    1. Seconds are incremented by Arduino. When it reaches 60, time is retrieved from RTC. If it doesn't update, there is something wrong with RTC communication.

      Delete
    2. Could the problem be caused by rtc not having battery?

      Delete
    3. I don't know. Better use the battery. And check RTC wiring to Arduino.

      Delete
  4. Dear sir, how can i change the time alarm from 1 minute to ?

    Its the only code thats works fine for me.. only it blinks for a minute, and i want it to go on for 0,5 seconds to pull a relay high

    or create a second alarm time to pull the relay low.

    i understand the code and make it work just fine.

    please help me sir . thank you in advance.

    ReplyDelete
    Replies
    1. Replace:

      if (current >= timeAlarm + 300) {
      if (alarmOn) {
      digitalWrite(AL_BUZZ, alarmSw);
      lcd.setCursor(0, 0);
      alarmSw ? lcd.print("<<<") : lcd.print(" ");
      lcd.setCursor(13, 0);
      alarmSw ? lcd.print(">>>") : lcd.print(" ");
      alarmSw = 1 - alarmSw;
      timeAlarm = current;
      }
      else
      digitalWrite(AL_BUZZ, LOW);


      with:

      if (current >= timeAlarm + 300) {
      if (alarmOn) {
      digitalWrite(AL_BUZZ, alarmSw);
      delay(500);
      digitalWrite(AL_BUZZ, LOW);

      timeAlarm = current + 60000;
      }

      Delete
    2. Thank you for your fast reaction.

      Can you explain me how you calculate it ? the 60000 are microseconds or milliseconds?
      if i know how to make the calculation i will try to play with these value's.
      why dont you use. by example, if alarmon the write digitalpin 2 HIGH delay(500); digitalpin 2 LOW

      I want to understand your code so i get it to work for me.

      everything else is working, alarm set etc etc..

      Thank you again.

      Delete
    3. if (current >= timeAlarm + 300) checks every 300 ms if the alarm time has come.
      if (alarmOn) if the alarm is enabled, the AL_BUZZ pin is toggled HIGH then LOW after a delay of 500 ms.
      Since alarmOn is true as long as current time matches alarm time, for an entire minute, timeAlarm = current + 60000; disables the alarm pin triggering for a minute.

      I believe this is a better way to do what you want: https://gist.github.com/onetransistor/a658a575ff5dde18dd3234e7462dd73e (give it a try - it is full code).

      Delete
    4. Thanks again :D.

      Thanks for your explaining, i have try it but no success.
      i think it won't work with this type of code if i try to delay the time to switch on off the time freeze up and wait till the delay is over and it then its go's further.

      Maby i to stupid to get it work pff its cost me almost a week to get it work. I will buy a new rtc1307 and a lcd1602 i2c there are all lot sketches I'm more just to it,

      The only thing i want it to have is 0800 clock power comes on for 10 minutes then switch off and at the same time 0800 clock will power on for say 500ms its imitating pressing a button.

      If you want to help me in advance i appreciate, if you don't have the time or mood for it its not a problem, and i will wait with this project till i received the new parts.

      Greets from the Netherlands

      Delete
  5. Just wanna ask can I change the arduino nano into arduino uno and would it still work?

    ReplyDelete
  6. hi, i have tried using my arduino UNO, but my LCD keeps flickering and it doesn't show the date and time as expected, it only shows black boxes on the first row and very dim. i don't know whether it is my wiring connections problem, there is no problem when uploading the codes tho. Also, when i connect to my computer, the buzzer keeps having a tickering sound, is that normal?

    ReplyDelete
    Replies
    1. No, it is not normal. Remove the buzzer from the circuit. It looks to me you used a passive buzzer that may have got shorted. Active buzzers beep when powered while passive buzzers reproduce the frequency of the input signal (more like a speaker).

      Delete
    2. Thank you for your reply, besides the buzzer, is there any possible reasons that causes the flickering? because i have changed the buzzer and the problem still remains.

      Delete
    3. I don't know. It could be bad wiring.

      Delete
  7. Thanks for this but the schematic is a little confusing because the pin arrangement shown is actually the bottom of the Nano. The processor and USB port would hidden.

    ReplyDelete

Please read the comments policy before publishing your comment.