Monday, 27 January 2014

Standalone Project: Multi functional Clock

I would like to share my project with you. It's an RTC clock with thermometer powered by atmega328p.

Clock, displaying current time, date & day of week.
It took me a while to finish this project since I wanted to polish the software. I wanted to implement a couple of features which will make my clock more interesting than any other similar device like that build before. I'll try to go through all the details now so, if you find this project interesting you can build it yourself (everything. including the software, is available for free).


The hardware itself is pretty simple. I used a ds18b20 digital One-Wire temperature sensor as a thermometer, a DS1307 RTC module which comes with a battery and a classic hd44780 LCD 16x2 display. Everything is pretty generic in that matter. There's a diode as well used to indicate that the clock is running and a couple of buttons. Besides that I use a couple of transistors in order to be able to control the LCD's brightness and contrast through software, using PWM timer. This is the schematics:

Clock schematics

I've taken some pictures during the building:

All elements populated, making the connections on the perf board.

Cutting the connections on the universal board.

View from the side.

Making the connections.

Completed device.


As usual, the software is written purely in C - WITHOUT any 3rd party libraries. The code is 100 % mine - that includes the I2C driver, LCD driver, Software One Wire driver - all available in the libpca library. The software itself is pretty complicated as for a project like this it contains two state machines; the main one and the temperature measurement dedicated, handles two interrupt sources (INT0 from RTC, as well as Timer overflow interrupt) a fully independent menu system which can be used in any other project and some other additional routines.

  • Event driven state machine - buttons, interrupts, FSM itself, generate events
  • PWM controlled LCD backlight brightness
  • PWM controlled LCD contrast
  • LCD backlight fade-in/fade-out on keypress (handled in interrupt)
  • Multiple information screens (scrolling from one to another)
  • Independent scrolling strings
  • Animated transitions between the "screens"
  • The clock maintains all the data like current time and temperature measurements (maximum temperature / minimum temperature) without the external power (stored in RTC)
  • Displays a proverbs for every day of year
  • Displays a unique nameday for every day of year
  • Software "integration" key de-bouncing algorithm
The software is 31 kB is size - it takes almost whole available FLASH space. The code itself is somewhere around 15 kB, but since I wanted to display an occasional proverbs and name-days information those additional assets consume the remaining space (it's quite a lot considering the fact that you need at least one (let's say 16 bytes long only) for every day of the year 16 * 365 ~ 6 kB !!!.

Software Functional Block Diagram.

Although the software may look over complicated it is acutally very simple. Let's talk about the interrupts first. I use the RTC's square wave output as an external interrupt (INT0) source. Thanks to that I don't have to constantly poll the RTC in order to update the time - since it will only change once every second. The rest of the time I can use the CPU for any different task. This 1 Hz interrupt is crutial to the system. It generates 1 Hz event in order to update the time, decrements the internal timers - so for example I can program a variable to a value and I know it will be decremented with every second - this can be used to generate timeouts and I use it to generate screen timeouts. For example, once the finite state machine enters Time Display mode the timer is programmed to i.e. 30 seconds - this timer is decremented in the 1 Hz interrupt handler. Once zero a TIMEOUT event will be generate which will force the FSM to transite into another screen (like temperature display or any other). It is used as well to control the backlight on time. If the backlight time is configured to a fixed value (like 30 seconds) the backlight timer is decremented in the 1 Hz interrupt handler if there is no keypress. The 1 Hz interrupt triggers the temperature measurement as well.

Temperature measurement

Temperature measurement is kind of tricky, since in order to get the results I need to wait for around 800 ms in for the sensors to finish the conversion. There were two solutions really - do it with every second - it will synchronous with the clock or do it asynchronously - I wanted to try the second one. This method is slightly more complicated and requires the second Timer interrupt to come into play. In order to synchronize two interrupts and a main loop a small finite state machine dedicated to temperature measurement had to be created. Bellow diagram depicts the process of triggering the temperature sensor and obtaining the results.

Temperature measurement.

Timer interrupt & Screen Transition

The timer interrupt is needed for other purposes. I use to for transitions between screens and for dimming in/out the backlight of the screen (change the PWM duty cycle from/to programmed one from/to zero).

In order to realize the transitions between screens I use a nice property of the hd44780 display. Although it is 16x2 characters it has a lot more DISPLAY RAM it is actually 2 x 40 characters. What I do is during the transition generate the content of for example time display screen to the display RAM at zeroth character and generate the second screen at character 17th (currently outside of the display frame), during the transition I simply move the display frame from 0 to 17 changing the screen content in result

Screen Transition

Menu system

Menu system has been implemented as a separate module. With a small adjustments it's possible to use it in any different project. The menu definition looks the following way:

struct menu_item {
const char *name;

// 1 - inactive/active
// 0 - cb/submenu
uint8_t config;

union {
menu_callback_t cb;
struct menu *submenu;
} ptr;

Each item has a name, and respective callback pointer called once the item has been selected. The menu definition for the clock looks the following way:

static struct menu_item items[] = {
{ "Set Time", MENU_ITEM_OWNER, { menu_set_time } }, 
{ "Set Date", MENU_ITEM_OWNER, { menu_set_date } }, 
{ "Time Mode",MENU_ITEM_DEFAULT, { menu_set_time_mode } }, 
{ "LCD Brightness", MENU_ITEM_DEFAULT, { menu_set_lcd_brightness } }, 
{ "LCD Contrast", MENU_ITEM_DEFAULT, { menu_set_lcd_contrast } },
{ "LCD Backlight Time", MENU_ITEM_DEFAULT, { menu_set_lcd_backlight } }, 
{ "Reset temperature", MENU_ITEM_DEFAULT, { menu_reset_temperature } },
{ "Temperature Display Time", MENU_ITEM_DEFAULT, { menu_set_temp_disp_time } },
{ "Time Display Time", MENU_ITEM_DEFAULT, { menu_set_time_disp_time } },
{ "Nameday Display Time", MENU_ITEM_DEFAULT, { menu_set_nameday_disp_time } },
{ "Words Of Wisdom Display Time", MENU_ITEM_DEFAULT, { menu_set_wow_disp_time } },
{ "Save Settings to EEPROM", MENU_ITEM_DEFAULT, { menu_save_settings } }

Of course if any string is longer than the the display line, the string will be scrolled. The implementation for that is generic and modular as well so can be taken straight away.


Clock displaying nameday.

Picture bellow shows the transition in progress, from the Time Screen to the Settings Menu. It also depicts how long does it take to switch a character in this LCD, since the new character is overlaying on the remnants of the old one.

Transition from Menu Screen to Time Screen

Menu Screen

More options in the Menu Screen

Long Option name is being scrolled in the menu screen.

Transition in progress.

Simple progress bar implemented for the menu.

Brightness regulation.

Video demonstration:

As usual, the source code as well as all the documentation is available on my github account:

git clone pca
git clone projects

Look for subdirectory projects/clk.

1 comment: