January 1, 2010

Alarm Clock Overkill

I don't get up at the same time every day, so I thought it'd be nice to have an alarm clock that would drag me out of bed at different times on different days. That was the initial idea: things got out of hand, of course, and this is the result.

What it does:
1) It displays the time, day, and date. It also keeps track of the year, leap years, and adjusts automatically for daylight savings time.
2) It displays the temperature.
3) It has seven independent alarms, each of which can be set for different days of the week. For example, one alarm can go off at 5am on Tuesday, Wednesday, and Friday while another can go at 6am Monday and Thursday and a third can allow me to sleep until 7 on weekends.
4) In the event of power failure, the clock keeps time anyway. The alarms and display don't work when the power's off of course, but when power comes back on the time will be correct and it remembers the alarm settings.
5) It announces special events. Today is New Year's Day, so the bottom line of the display alternates between day/date (shown above) and the message shown below.

There are over 150 of these special events programmed into the clock, including family birthdays, oddball holidays (Squirrel Appreciation Day, anyone?) and so on. On my kids' birthdays it even tells me the age of the kid. (A useful bit of information that sometimes escapes the geek mind, as you probably have noticed —or forgotten— already.)

How it does it:

The brains of the unit are supplied by an ATMega328 Boarduino  (Arduino clone). This keeps the display up to date, turns alarms on and off, and runs the user interface. It also stores and displays the various special events for the year.

Accurate timekeeping is provided by a DS1307 real-time-clock chip. This chip also has 56 bytes of spare EEPROM which I use to store the alarm information in the event of power loss. Communication with the microcontroller is via I2C bus.

The alarm beeper itself is a simple piezo buzzer from Radio Shack, driven off the 9V supply line with a 3904 NPN transistor. The Boarduino can make the buzzer sound without the help of the transistor, but the goal is to not just sound the buzzer but get me out of bed!

Temperature sensing is via an LM35 temperature-sensitive zener diode. This is a great little component! Put in 5V, and the signal line puts out 10mV/°C. It's that easy. I use these, or the similar LM335, in my physics lab quite often.

User input is via a pushbutton/quadrature knob. The user presses the knob to select an action or set a number, and turns the knob to dial in the option of his choosing. Much better than the usual 4-button combination on the top of most alarm clocks.

Here's a cut-down version of the code. I trimmed the getMessage() function in this sample code to just show an example of how the "special events" code works — you'll have to put in the special events of your own choosing. (Or you can email me and I'll send the uncut version.)

Notes about the hardware:
1) The 2x16 character LCD uses the standard Hitachi HD44780 driver and the LiquidCrystal.h library. With some modification you could make it work with other LCDs, although this code is optimized for 16-character lines.
2) I put 0.1µF caps to ground on each of the three switch lines on the rotary switch, in addition to the usual 10k pull-up resistors. This combination nicely debounces the quadrature signal.
3) The DS1307 clock chip requires 3V on the "battery backup" line. It will not work without this.
4) I soldered the crystal directly to the DS1307 chip to minimize any clock error due to crappy breadboard connections.
5) As written, the code requires an ATMega328 or larger microcontroller. The "stock" ATMega168 does not have enough memory to run this program unless you get rid of the alarms or most of the daily specials.

It needs an appropriate case of some kind. I'm planning to etch a small batch of boards for these, driven with bare ATMega328's instead of the Boarduino I used for this prototype, and I'm thinking some sort of retro-steampunk case. I'll post pictures when it's done.

Want to build one yourself?
Download the code here. The hardware is very straightforward, here's an electrical diagram. I don't recommend this as a first Arduino project, though!


  1. Looks great!

    Did you just pole the timer IC from the Arduino? I hear getting a reliable human-clock from the Arduino is a bit fiddly.

    Look forward to seeing the enclosure too.

  2. I have been looking for something like this to help my mother (who has Alzheimers) keep track of the day and what she needs to do that day.

  3. Great project.
    To eliminate the case when the alarm is not triggered because of a power failure, you can use batteries.
    Since the consumption is not more than 100mA, you should be able to power it from batteries (4xAA rechargeables, at 2800mAh, which would provide about a month of power).

    I made a similar (based on DS1307) clock, check it out here:

  4. Nice clock. It's a little more feature complete than any of the ones I've built as of late, though using the same chip.
    A few notes though: The DS1307 doesn't have any EEPROM in it. It has 56 bytes of SRAM, not EEPROM, so it still depends on the battery backup. The ATMega has plenty of EEPROM, if you want to not even depend on the batttery. The DS1307 also doesn't require 3V. I've used it with Vbat tied to ground for plenty of things. The problem is that if you leave it floating, it will drift all over the place and disable the I2C interface, since Vcc MUST be 1.25 times Vbat.

  5. Just need to add a radio reciever to it so it syncs itself to the atomic clocks...

  6. In the drawing you have the sda connection from the 1307 to the scl arduino, and the scl to the sda. Is this a mistake or was it done for a reason.

  7. Following up on my earlier email I was able to build this clock using the 3231 chronodot RTC. I had to hook up the 10K pullup resisters to the scl and sda lines though to make the clock work.

    I have a problem with the temp. I'm using a TMP36 sensor supposed to be equivalent to the LM35 but I get(') and (() characters in place of the temperature. When I touch the snsor these charcters chang , suggesting the sensor is responding.
    I'm no sure I understand the folowing lines:
    TopLine[11] = (char)Temp/10 + 48;
    TopLine[12] = (char)Temp%10 + 48;
    what is Temp% and the meaning of the 48 adder.
    As mentioned in my email I am a newbie catching up and would appreciate any guigance here.

    PBC (chebipb@comcast.net)

    1. Hello,

      The '+48' changes the digit to its ASCII equivalent. For example, if the number you want to put on the LCD is a '9', and you send out a '9', the screen prints an ASCII 9 which is a tab character. The actual digits 0-9 are ASCII codes 48-57, so adding 48 to the 9 gives you a '9' character. This is not a beautiful solution, but it works.

      The /10 and %10 give you the 10's place and the 1's place in the number you're trying to print. if the temperature is 72 degrees, dividing by 10 gives you 72/10 = 7 which is the first digit of the temperature. The % operator gives the remainder: 72%10 = 2, which is the second digit of the temperature.

      Hope this helps,

  8. Sure did thank you. It worked beautifully and sorry could not attach a pic.
    A couple of hints for the 3231 RTC and TMP36 users:
    As mentioned above the 3231 needs 10k pull up resistors at scl and sda to work.
    The tmk36 is similar to the lm35 but needs a subtract of 50 from the float tconvert formula to compensate for the -50-140 scaling.
    I initially used two cr2032 batteries in series for vcc and noticed that the temperature reading creeps up as battery voltage drops with usage. Do not know if this applies to lm35. To compensate I installed a lm7805 voltage regulator circuit with a 9v battery, and that stabilized things.
    I had to tweek the 500/1024 formula to 490/1024 to calibrate the reading with a thermometer.
    Overall it is a fascimating gadget and the "calendar" functions and notifications of events adds a whole new dimension to the use of the LCD.
    Thanks for sharing this project.


  9. Hi EA

    Enjoing my clock since last posts.Definitely a 7805 is needed to stabilize temperatures which is dependent on Vcc thro the tconvert formula. I have added a second Temp sensor for outside temp wired thro a 3 pin connector to A1 and located its reading instead of #F on the LCD. Also added a 6 pin connector to be able to update the sketch directly thro an FTDI friend USB conection to the computer.

    Still wresling with programming alarms on/off and days of week. Your sample code for programming special alerts such as New Year, Father Day Birthdays, etc were very helpful setting my own. I would appreciate if you could post a couple of days worth of alarm sample code including alarm #, day, hour on/of buzzer on/off etc to guide me thro this proces.

    Thanks a lot again


  10. Very Cool Thanks for sharing! I set this up as a study even though it is a bit beyond my skill at the moment. Only issue i found for me is that the rotary encoder seems to skip around a little. I will have to figure out how to set up the encoder steps which I think from my reading will rectify the issue.


  11. I'm hoping that this thread is still alive. I really like your build and the fact that it uses a rotary encoder. It's elegant and saves on input pins. I've also used an I2C backpack for the LCD so that less pins are used. Now... I'm trying to do a mod so that I can use this clock to control the lighting system on my aquarium. I'd like the alarm to only look at the time of day, and when it reaches that time, it modifies outputs on 8 pins to go high/low depending on what I select in the alarm set screen. So instead of selecting the days Sun-Saturday, I'd like to be able to select between 1 and 0, in 8 slots on the LCD. So if I have 11110000 set for 5pm, then at 5pm, output pins 1-4 go high and 5-8 go low. So dear Doctor, any chance you an help me out with the code? Not sure where to begin... many thanks in advance.

  12. This will take a few changes, but none of them should be too hard. Functions DisplayAlarmInfo() and SetAlarm() will have to be changed to replace days with bits, of course... I recommend creating a byte variable "AlarmBits" that would hold the 8 bits, this could replace AlarmDays[] in SaveStatus() and LoadStatus().

    Alarm status is handled with a boolean called "AlarmRinging" during the "these things happen every minute" portion of loop(). That's the part that does the time-checking. The actual on/off of the alarm is managed by the "if (AlarmRinging)" statement in the "these things happen every cycle" part of loop(). That if statement is where you'll need to put the code that reads AlarrmBits and turns the appropriate output pins on or off.

    I hope this helps; it looks like an interesting and useful modification to my clock.


  13. Besides the real time clock little battery, I'd add a battery backup for the whole thing so it;ll go off if the blackout is in progress at the moment. To add this important feature, add a relay and a battery pack with the power supply.

  14. What do I have to remove if I dont want any alarms or temperature. I just need the clock and date

    1. Remove the alarm variables (AlarmOn, AlarmMinute, etc).
      Remove functions DisplayAlarmInfo(), SetAlarm(), GetTemperature(), UpdateTemperatureDisplay(),
      Change DealWithButton() and loop() to eliminate dealing with alarm. Change loop() to remove calls to deleted functions.
      Try compiling it, use the resulting errors to find what else I should have mentioned. :-)

  15. with the 9 volt supply, all you need is a relay, capacitor, the wall wart and a bank of 6 D batteries. You could have a clock that is fully blackout-proof where the alarm still works.

  16. i have problem on rotary encoder....when i press knob, the voltage will cut-off.....then when released the push button, it will restart back.....someone can help me?

  17. My first guess would be that your switch is miswired so that the button is shorting +5V to ground. Check the circuit diagram more closely, perhaps? On the diagram two of the wires cross between PB and 10, but that's just the necessity of the diagram not an actual connection. Connections are indicated by the dot at the intersection.

    If that's the error, be sure to check the rest of the connections (and non-connections) also.