I wanted the clock to be easy to use and be flexible. The setting functions are menu-driven, you set each parameter one digit at a time (with live data validation) and you can abandon changes if you want. You can have 12 or 24-hour time. It uses a bright, legible VFD character display - you can choose even more readability with a 2x3 big-character mode.
Finally, VFDs are bright and readable, but sometimes you don't want them lighting up the room. So, you can set a schedule of when the display is bright, dim or off. You can turn the display on or off any time you want, as well.
I hope to go over key elements of the software and hardware design to help you in building a clock just like this one or to give you ideas for anything that needs menus, data validation, timekeeping and so on.
Step 1: Design Choices
Easy to use:
- menu-driven setting options
- digit-by-digit setting (who wants to go up or down to set something like longitude?)
- data validation to keep the user from inputting impossible time, date, location etc...
- buttons match the way it is used, eg. you want to look at it while setting, so the left button is really the right button
- most common functions on their own button, eg. display on/off, big/small digits, etc...
- must keep time accurately without using radio time; DS3231 RTC is accurate to 2 ppm/year or approximately +/- 1 minute per year
- no buttons visible anywhere except the back
- allow adjustment of DST start and end
- allow 12/24 time
- support display brightness schedule
I suppose you can add to "Simple" that it uses the Arduino platform. There isn't actually an Arduino board inside, though you could use one; I used a Modern Device RBBB Arduino clone and a Wicked Device RBBB shield board. I chose the RBBB because it's cheap, flexible and sports a power jack, which I needed anyway and is a pain to do properly on protoboard. I chose the Wicked Device RBBB shield as it supports the RBBB and because it gave me sufficient protoboard space to mount a 2032 coin cell holder for the DS3231 backup power and have a header for the 14-pin cable to the VFD display.
Step 2: Hardware Choices
One of the big reasons for going with the Wicked Device RBBB board was the 14-pin SO surface mount area. The DS3231 is a 16-pin SO part, but it turns out pins 8 & 9 are NC (not connected) so it was possible to shift the chip one leg to the right and mount it on the remaining 14 pads. Otherwise you have to buy and solder up a 16-pin SO to through-hole adapter, make your own PCB or buy something pre-made like a Chronodot. I needed some space to solder a 14-pin header for the VFD and the battery holder so the Wicked Device RBBB was the right choice. It was also the cheapest choice.
I chose the LCD-compatible VFD display because I wanted readability over all else. VFDs have some disadvantages, namely cost and power consumption. I got lucky and found an eBay seller with a lot of these at a good price. If I had to order directly at 1x quantity from Noritake I might reconsider. This project is wall-powered, so power consumption is not a problem. The VFD is a CU20029ECPB-W1J, if you're wondering.
Because we have a power-hungry VFD (~300ma @ 5v) I decided against a linear power regulator. I have tried it before with this VFD and it just gets too hot for a non-vented enclosure, even with a 7.5v power supply. A good 5v regulated switch-mode power adapter is cheap, reliable and keeps your project cool inside. The RBBB provides for a linear regulator but it is a simple matter of jumpering pins 1 & 3 where the regulator would go.
Finally, the case choice was a matter of finding one with a faceplate big enough to accommodate the VFD. It turned out that 1/8" plexiglass needed just a bit of sanding along the edges with coarse grit sandpaper to size it to fit the existing faceplate channel. The case is a Pro's Kit 203-115A. I got it from Alltronics.
Step 3: Software - Menu System
The real work was the setting system. I wanted to be able to set each value character by character. Some of the numbers, like longitude and latitude are 5-6 digits (ignoring the decimal) - I did not want to use a press and change method, even with a jump increment on button hold. Speaking of buttons, this project uses Button.h, which I had initially thought handled debouncing but actually does not. A better choice would be Bounce.h. It was enough to toss in a small delay in the button polling loops, since the CPU itself isn't doing anything else - the RTC keeps time on its own.
So anyhow, the first problem of a by-character setting system is knowing where the cursor is allowed to go, This is handled by a two-dimension array which stores a 1 or 0 for each position on the VFD to indicate if the cursor is allowed there or not. If not, the cursor skips to the next available spot or stays on the last spot. I made liberal use of defined constants so I don't have to memorize which index value goes with with setting array.
The second problem of a by-character setting system is handling data validation. You would think it is as simple as re-assembling your digits back into the value and then doing a comparison against the max value, but to block the user from going to the next value you have to see what that value + 1 would do to the number as a whole. That's not bad for a couple of digits but it's a mess for 5-6. I found it was easier to have a two-dimensional array like the position array to hold the max allowable value per character. This allows you to check the same thing based on index every time the user tries to increment, and coupled with a check for the max value as a whole, this captures most bounds violations. It will still be possible for the user to sometimes set an illegal value. It was either that or a lot more code.
Along the same lines, I didn't care if the user tried to set something like February 30. You can add that sorts of bounds checking if you want. I'll admit I got "project fatigue" and left that sort of thing out.
Step 4: Software - Storing Values
I know there are AVR libraries to handle storing whatever to EEPROM. I think I am using less RAM doing it simply the way I do it.
Step 5: Software - Solar Calculations
The bigger pain is DST conversion. It is not as simple as just adding or subtracting an hour, as you have to take into account what happens near midnight, and that depends on the month, whether or not it's a leap year, etc... Thankfully Timelord.h handles that too.
Lastly, the usual convention in the computer world for RTC (system) time is to store the time as UTC and then compute the time based on the timezone offset and DST conversion, if needed. I don't have a problem with that, but for non-technical people it might be a hinderance. I chose to store the the time in the RTC as standard local time. I think it's easier for most people to drop an hour if in DST when setting than think about what the time is in UTC. It would not be hard to change the clock to keep the time in UTC if that is what you wanted, as Timelord.h will handle that.
Step 6: Software - Talking to the DS3231
I've switched over to Bounce.h from Button.h. That's working well. Don't forget you not only have to set your button pins as inputs but then also set them HIGH. Otherwise the built-in pull-up resistors aren't activated, your pins will float and your buttons will act crazy. Button.h does this for you, Bounce.h does not.
Step 7: Software - Big Characters
This VFD has the advantage of a tight spacing of the 5x8 character cells. The 2x3 characters look pretty solid. The disadvantage is the bottom line is wired as a cursor, so it's either all on or all off. Hence the funny look at the bottom of some characters where you'd expect them to be rounded off.
Very, very occasionally you will see the display 'glitch' as it re-writes. It may be possible to improve this using 8-bit mode. I don't think it warrants the use of 2x the pins here. For highly speed-critical apps, like a sound spectrum display, 8-bit would probably offer an improvement. Then again, the glitching might be a controller artifact you would never notice on an LCD as they are just not as fast display-wise as the VFD.
Step 8: Hardware - Component Mounting
As for the mounting hardware, it's expensive to buy in small quantities and cheap in large, like most hardware. I bought a grab bag online which had what I needed. Taking apart discarded consumer hardware is a good way to build up a collection of screws and standoffs.
Step 9: The Code
Version 1.3 used Button.h
Version 1.4 uses Bounce.h
Step 10: Concluision
Well, knowing the sunrise, sunset, noon and moon is helpful for a number of things. You can use it for determining when the fishing it best. You can figure out your Muslim prayer times. You can figure out other things for lunar-based calendars. If you pay attention to what solar noon looks like, you can get a sense of what time it is without a watch.
I am not sure the ATMEGA328 has enough RAM to do it, but it would sure be cool to calculate when a lunar or solar eclipse might be expected for your area.
I like the idea of my clock being accurate without the help of outside signals. One minute loss or gain per year is pretty accurate, at least for human activities.