Skip to main content

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).

Hardware 

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.

Software

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.

Features:
  • 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.

Overview

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 git@github.com:dagon666/avr_Libpca pca
git clone git@github.com:dagon666/avr_ArduinoProjects projects

Look for subdirectory projects/clk.

Comments

Post a Comment

Popular posts from this blog

RTC without RTC ? How good is my crystal ?

Clock - I think that it's the most popular idea for a project with the MCU . It's not that hard from both software and hardware side to realize but not that trivial as well, not mentioning the fact that once realized it sits somewhere in the house, constantly reminding about itself and brings a kind of satisfaction :). There are a lot of options really to implement, a buzzer alarm, perhaps a thermometer, display and buttons servicing, perhaps maybe even serial port usage for NTP synchronization. But ... The most challenging thing - like in every clock is the time reference source. There are a couple of options: well calibrated frequency source acting as an interrupt source Real Time Clock module with external crystal ( < 20 ppm ) internal clock These days RTC chips are so cheap and widely available that they are really the only reasonable choice, but if we're only planning to play around a bit with the software without paying much attention to accura...

Arduino R-2R ladder Audio DAC

There is a lot of projects out there which use  R-2R ladder and an Arduino to recreate sounds from either SD card or short audio clips programmed directly to MCU's flash memory. Although using SD card is fairly reasonable and provides a lot of flexibility it's not very challenging (from the software point of view) and it requires some additional hardware. Believe it or not we already have all the needed hardware in the Arduino itself. Assumptions In this project I'll play mp3 or any other multimedia files from the PC using Arduino. Bellow are the details: Play PCM 8kHz 8bit Audio with Arduino Audio samples will be transfered via USART from the PC in binary format using SLIP Audio files will be decoded on the PC side and only the RAW data will be send to Arduino Timing Arduino is a powerful machine, powerful enough that it's possible to play audio with even higher sampling rates and bit-resolutions than assumed above, the bottleneck in this...

Simple Serial Port Command Line Interface (CLI)

It's often very useful to have some sort of interface through which a basic management can be done of our application. Whether you're designing a giant Marquee with a LED display or a plotter or you simply need to get some diagnostics from your device occasionally, a simple CLI system can come very handy.  Of course, designing something similar to "bash" or any other unix shell is completely out of scope since those applications are huge and are simply an overkill for our needs. It's pretty simple though to create some basic, yet flexible & easilly extensible CLI system. First thing needed is a command type definition. This will bind a keyword with an actual underlying routine executed for that keyword typed in the CLI . typedef struct _t_cmd { const char *cmd; void (*fh)(void*); } t_cmd; The command type is pretty simple. There's the CLI command/keyword pointer, it holds a pointer to the execution function and that's it. OK, so...