Feel free to vote for me in the microcontriller contest if you like my project. It would appear that contest might be specific to making things move. So what does this move? It moves the student who stayed up too late doing homework, it moves the parent with a new born child, it moves the Instructables author who was compelled to finish their project against all common sense. It moves what can often be the most immoveable of objects... you.
Direct link to this video http://www.youtube.com/watch?v=szoPO75u46s
Steps 9 and 10 have additional videos.
Some key features:
-Any button press will silence the alarm for 30 seconds so you’re not listening to the buzzing while playing Tetris.
-User can select how long the snooze interval is (with second granularity), how many times you’re allowed to hit snooze (0-255), and whether the snooze interval is from the time the alarm went off or the time the snooze button (any button) is pressed.
-Two separate alarms: One is a traditional alarm that will go off when that time is reached and then disabled for the following days. The other is what I call a “persistent” alarm that goes off at the same time Monday through Friday so isn’t disabled for the following days.
-It uses large numbers for which I was inspired by this Instructable and he got the idea from here .
-It gets power from a base that it sits on but has a rechargeable battery backup so you can tilt the clock without being encumbered by wires. The switching to and recharging of the NiMH battery happens automatically.
-It has a physical key to silence the alarm which is given to your spouse/roommate/parents so the alarm can be silenced with having to play Tetris
-The backlight brightness is user controllable through software and the backlight is turned off when not on the base to conserve power and indicate that external power is absent.
-You can cancel the alarm at any time, even before the snooze intervals have passed, by playing Tetris.
-Four different styles of Tetris, details Step 8.
-When setting the time and alarms instead of the typical adjusting of hours and minutes the user can adjust tens of hours, hours, tens of minutes, minutes, tens of seconds, seconds, tenths of seconds, hundredths of seconds (yes, really) and day of the week. This allows for quick time setting and ability to synchronize easily with another time source. See Step 9 for details.
Other points of possible interest:
-Use of interrupts, Step 4.
-The best (in my opinion) way to debounce switches, Step 5.
-Use of PROGMEM to store strings to save on memory, Step 7.
-Precise and simple instructions for using the Sparkfun FTDI Basic Breakout board and making a barebones Arduino board, Step 13.
-Keeping time with an Arduino without a separate real time clock, Step 2.
-How to deal with the fact that the Arduino function millis() wraps back to 0 every 50 days or so, Step 2.
This is my very first Arduino project and I have to say it’s a pretty slick environment.
Step 1: Parts
-An Arduino or an ATMega328 with the Arduino bootloader. Sparkfun sells them ready to go or save a buck and buy it from Digikey part number ATMEGA328-PU-ND . If you buy one without the bootloader you’ll have to program it yourself. I used a UsbTiny from Adafruit and the process was super easy thanks to the Arduino IDE. ($5.50)
-A way of programming the Arduino. I used Sparkfun’s FTDI Basic Breakout item number DEV-09873 . I had a surprisingly hard time figuring out how to hook up the programmer so I have instructions for that in Step 13. ($22.00)
-A crystal and accompanying capacitors. This and the above two items are all that’s required for a bare bones Arduino. A ceramic resonator won’t work in this application because we’re keeping track of time. I got mine from Digikey part number 300-8437-ND . ($0.63)
-A backlit standard (HD44780) 16x2 LCD display. I got mine from Sure Electronics part number DE-LM12111 . ($5.99)
-Four buttons. I used tactile ones from Digikey part number SW404-ND . ($0.35)
-A buzzer. I used one from Digikey that I got a long time ago for another project so I forget the part number.
-A rechargeable 9v NiMH battery. I got mine from harbor freight part number 97865 . ($9.99 although they have a cheaper, lesser capacity one that was out of stock the day I went)
-A 9v battery clip-style holder, I got mine from Digikey part number 71K-ND . ($0.44)
-A 9v battery contact, I had a couple on hand but I’m pretty sure I originally got them from Digikey.
-A ICL7673 backup battery IC, a wonderful device that automatically switches between two power supplies without an interruption in power to the device and even indicates which power source is currently being used. I got mine from Digikey part number ICL7673CPAZ-ND . ($2.08)
-A 5v voltage regulator, I used a Low Drop Out variety from Digikey part number MC7805CT-BPMS-ND . ($0.56)
-A couple diodes, one for recharging the batter and one to insure against reversing supply power. I used one from Digikey part number 1N4148FS-ND and one from my parts box. Two of the ones from Digikey should work just fine. ($0.14)
-A slew (6 or so) of 0.1uF ceramic disk capacitors. I got mine from Digikey part number 478-5741-ND . ($0.13)
-Various resistors, I got mine from Digikey and from my parts box which was originally stocked from a very worthwhile Radio Shack purchase part number 271-312 . ($9.99)
-A project enclosure from Radio Shack (6x3x2") part number 270-1805 . ($3.99)
-Veroboard (stripboard ), I got mine off Ebay, you can used your favorite board making material from etching copper clad to perfboard . Consider getting some stripboard and the tool for cutting the traces together off of eBay if you're going that route.
-I also used a breadboard for initial testing and cardboard to mock up the enclosure.
-Soldering iron, solder, wire strippers, a rotary tool to make the enclosure, typical stuff.
Step 2: Code - General
The code is attached to this step. The zip file is just the other three files zipped up. The "v1" on the zip file just indicates version 1 and is an easy way to check if there's a software update.
All code, with the minor exception of the small availableMemory() routine, was written completely from scratch by me.
I originally thought the Tetris part of the code would be more complicated than the time keeping part. I could not have been more wrong. Writing Tetris for Arduino was a cake walk compared to an alarm clock with all the features I wanted. Descriptions of some of the programming issues are in the next few steps.
Many parameters are adjustable in the code. They are all at the top of the main file and include:
-All the wire/pin assignments
-Default time, and both alarms
-Constants the define buzzer frequency (default 4 kHz)
-Time the alarm is muted each time a button is pressed (default 30 s)
-Default backlight brightness 0-128 (default 128)
Many parameters are adjustable through the clock itself:
-Whether thicker or smaller pieces are used
-If 2 custom characters are used for the pieces and 6 for the rubble below or 4 and 4 are used.
-Length of the snooze in minutes (default 7 min)
-Length of the snooze in seconds (default 30 sec for a total of 7 min 30 sec)
-Number of times snooze can be hit before you must wake up (default 2)
-Whether hitting the snooze is absolute or relative, if the snooze time is added to the original alarm time or the time the button is hit
-How many lines of Tetris must be cleared to be declared awake (default 4)
-Restore the clock to it's default settings
-Turn extras like am/pm, day of the week, and which alarm is set on and off
-Display current software version
-Save and restore EEPROM
Many parameters are saved in EEPROM to survive if power is ever lost completely. This is saved every day at midnight if any of the parameters have changed. They include:
-Current time (including day of the week), and both alarms
-Tetris mode of operation (see Step 3 for description)
-Whether the extras are on or off
-How many lines it takes to clear in Tetris to declare yourself awake
Time keeping is made easy thanks to the good folks at Arduino's excellent implementation of the millis() function. That function runs in an interrupt and is made so as not to miss a tick. All I have to do is periodically add the time elapsed since the last call of millis() to the current time. Easy as pie.
I pull an interesting maneuver to take care of the millis() roll-over problem. The millis() function uses an unsigned long variable and the 32 bits means it can go up to 4294967295. But since it's keeping track of milliseconds that means it will roll over every 50 days or so. This is usually not a problem but since this project runs continuously and relies on that millis() function we need to account for that overflow. What I do is monitor the millis() function and when it passes the halfway point take some action. When it's convenient in the code (nothing time critical is happening) I subtract half the total value of the millis() function both from the function itself and from my own time keeping variables. That way it can roll right past this halfway point and when it's safe subtract half-full time from everything and roll on. It's a bit hard to explain, examining the code might help, but it works like a charm.
Step 3: Modes of Operation
There are no less than 9 separate modes of operation. This makes things complicated and requires quite a number of switch/case statements.
0 - RUN_MODE – The standard running mode, large digit display of time along with smaller indicators: am/pm, whether we’re currently snoozed, which alarms are set, day of the week, and if the processor has been reset.
1 - TIME_SET_MODE – Mode to set the current time and day of the week.
2 - ONETIME_ALARM_SET_MODE – Mode to set the onetime alarm that gets cancelled after you’re awake.
3 - PERSISTENT_ALARM_SET_MODE – Mode to set the persistent alarm which goes off Monday through Friday.
4 - ALARM_SNOOZED – Snooze button has been hit, buzzer isn’t going off, we’re waiting for the snooze-number of snooze-intervals to elapse. Can enter alarm_snooze_turnoff mode to play Tetris to cancel the alarm.
5 - ALARM_TURNOFF_BUZZING – Buzzer is currently sounding, when a button is pressed enter alarm_turnoff mode.
6 - ALARM_SNOOZE_BUZZING – Buzzer is currently sounding, when a button is pressed consider it a snooze and enter alarm_snoozed mode
7 - ALARM_SNOOZE_TURNOFF – Hitting a button while snoozed enters this mode so you can play Tetris to cancel the alarm. If you stop playing for 30 seconds it returns to alarm_snoozed mode so you can sleep a bit more.
8 - ALARM_TURNOFF – The snooze-numbers have passes so you have to play Tetris and wake up. Pressing a button silences the alarm for 30 seconds but after that buzzing starts again. You have no choice but to wake up and cancel the alarm by playing Tetris.
Step 4: Interrupt Routine / Timer 1
This sketch provides an example of an interrupt routine. You have to be a little careful not to use an interrupt that the Arduino environment is already using. TIMER0_0VF_vect for example is used to count milliseconds returned by millis(). The way I go about using an interrupt is first I decide which one I want to use. For example, I needed some code to run every 5 – 10 ms for button debouncing (see more Step 5). The easiest way to do this is an overflow interrupt. A timer will run continuously and every time it gets to its maximum value and cycles to zero it causes an overflow interrupt. I went with Timer 1, a 16 bit timer. The speed of the processor is 16 MHz, 16 bits means it will overflow every 65,536 ticks. And 16,000,000/65,536 = 244 Hz which means a period of 4.1 ms. Perfect for my means, we don’t even need a prescaler, again see Step 5 for more. Translating an interrupt from the ATMega328 manual to what the Arduino environment wants is the next step. The manual calls it “TIMER1 OVF” and describes it as “Timer/Counter1 Overflow”. The file (on my system) “C:\arduino-0022\hardware\arduino\bootloaders\stk500v2\avrinterruptnames.h” has a handy list of all the possible interrupts. The one “TIMER1_OVF_vect” looks like a match and is what I went with. Now just search the Arduino source for usage of that interrupt (Notepad++ is what I use for this). You can check the code but I declare interrupt routines:
//interrupt service routine
All that’s needed are a couple lines in the setup() routine:
TCCR1B = bit(CS10); //no prescaling
TIMSK1 = bit(TOIE1); //overflow interrupt enable
Refer to the ATMega328 manual for register descriptions.
Step 5: Button Debouncing
My favorite document about debouncing switches is "A Guide to Debouncing " by Jack G. Ganssle. It has lots of great information but the important part (in my opinion) is that you should debounce switches for 50 ms. In other words a button has to be pressed for 50 ms before it’s declared pressed. This is long enough even for the roughest switches but short enough that the human brain can’t detect a delay. You want to check multiple times in that 50 ms period, not just once and then one more time after 50 ms.
The interrupt happens every 4.1 ms (see Step 4) and I wait for 8 consecutive reading of the button open or closed to declare the button pressed or not. So the button has to be pressed or released for 8 x 4.1 ms or 32.8 ms. I handle this by shifting an unsigned 1-byte variable to the left by 1 and putting the current state of the button in the least significant bit. If the resulting variable is 0xFF I declare it pressed and if it's 0x00 I declare it not pressed. Also, you don't want multiple button presses to register if the switch is held longer than the 32.8 ms so the state of the button has to be currently not pressed to declare it pressed and vice versa.
I also increment a variable to keep track of the total button presses and the number of particular button presses. These variables are decremented when the button is taken care of in the standard loop() function. This way button presses are recorded immediately but the code that actually does something useful is not in the interrupt routine bogging it down. An interrupt routine should be kept as short as possible.
There are other, perhaps simpler, ways to debounce switches but I have found this way quite robust, reliable and user friendly in a variety of applications.
Step 6: Buzzer / Timer 2
I use Timer 2 to generate the buzzer at 4 kHz. There's no grand reason I chose 4 kHz, it just sounded like a frequency that would wake me up. First the register TCCR2A has to be set to toggle OC2A, which is pin 11 and is connected directly to the buzzer. It also has to clear the timer on compare match. So it will count from 0 up to the compare value, toggle OC2A (pin 11), and start over at 0. This is explained in great detail in the ATMega328 manual but it boils down to the line of code:
TCCR2A = bit(COM2A0) | bit(WGM21); //toggle OC2A, Clear Timer on Compare Match
Now the frequency of 4 kHz is set by a prescaler and a compare value. A prescaler divides the clock by a certain number and the compare value determines the exact period. I used a prescaler value of 3 which divides the clock by 32, or makes it 500 kHz. So 500 kHz / 4 kHz is 125 but remember it toggles pin 11 so we need a value of 62. After 62 ticks the pin will go high and after 62 more ticks it will go back low which gives us a total of 124 ticks for the whole cycle. Which gives us a frequency of 4 kHz. If that's confusing just play with numbers until you like the pitch. A lower number in either of the register will raise the pitch. Here's the code:
TCCR2B = BUZZER_PRESCALER;//3, lower number higher pitch;0-mute,1-1,2-8,3-32,4-64,5-128,6-256,7-1024
OCR2A = BUZZER_COMPARE;//62, lower number higher pitch
To actually turn the timer on and off I simply make pin 11 input (buzzer off) or output (buzzer on).
pinMode(BUZZER_PIN,INPUT);//turn alarm off
pinMode(BUZZER_PIN,OUTPTU);//turn alarm on
I connect the buzzer directly between pin 11 and ground. Thanks to the fact that Atmel ATMega and ATTiny processors can source and sink considerable current no further amplification is required. It's actually louder than any other alarm clocks we have in the house.
Step 7: RAM Usage and PROGMEM
I first started this project using an ATMega168 that I had lying around. Between the smaller amount of RAM (1 k) and quite a few debug serial output statements, I quickly ran out of RAM. The worst thing about running out of RAM is that there's no definitive way to know that's what's wrong. Sometimes your program will just stop running, sometimes variables seem to change on their own, sometimes your program just starts acting very strange. If you've made a small change and it inexplicably stops working check your RAM usage. One thing you can do to keep an eye on RAM usage is the following routine.
int size = 2048;
while ((buf = (byte *) malloc(--size)) == NULL);
Call this routine in your setup() routine and display the available RAM on the serial port or LCD display. The amount of free RAM that you need is a judgment call but I would start getting nervous if it dropped below 200 bytes. Free ram is needed for the stack which is where all local variables declared in subroutines go. Also, avoid putting large variable arrays on the stack (as a variable in a subroutine). Something like
unsigned long MyArray
will completely consume all of your RAM.
One thing you can do to save a lot of RAM is to use PROGMEM for strings. It's unfortunate that strings go in RAM, they are much better suited for FLASH memory. You can force them in to FLASH with PROGMEM. Instructions can be found here and there are numerous examples in my code.
Step 8: Tetris Modes of Operation
The classic HD44780 LCD display has 8 custom characters. Originally I planned on using 4 to display the piece and 4 to display the rubble down below. I also originally planned on using 1 pixel for each block, 4 of which make up each piece. It was necessary to make it this small to fit the standard 10 columns across the width of the display. Each character is 8x5 pixels so a 16x2 display has 16 pixels from top to bottom or left to right while playing Tetris. This worked well but I thought it might be nice to have thicker pieces. Each block would be 2x2 pixels and the longest “I” piece would be 8 pixels wide or tall. This means that the display can only be 8 blocks wide (16 / 2) which actually works pretty well. Then I wondered if playing would be better if only 2 characters were used for the pieces and 6 characters were used for the rubble at the bottom. This also works well but the pieces get chopped off a lot.
All four modes of operation have their drawbacks. The smaller pieces are tougher to see, especially when you first wake up. The thicker pieces get chopped off a lot. Either the tops of the falling pieces, or the bottom of the pile of rubble. To this day I don't have a favorite which is why I made it easy to cycle through the modes.
You cycle through all four modes of operation by pressing the left and right buttons (as your holding it vertically) at the same time. I declare buttons pressed simultaneous if they happen within 200 ms of each other. It's just that simple.
Step 9: Setting the Time and Alarms
This video probably explains it better.
Direct link: http://www.youtube.com/watch?v=Z4nIZKB-5b8
Step 10: Menus
This video explains it well:
Direct link: http://www.youtube.com/watch?v=b0KFU6_44gc
Step 11: Error;
I like an easy and uniform way of displaying error conditions. I display the time of the error, an optional variable, and the line number where the error occurred; and then freeze forever. Usually I just include
wherever I want an error to occur. It's defined as
#define error print_error(250,__LINE__)
and print_error(var, line_number) does the actual displaying and freezing. So I use “error;” if I don't want to display a specific variable and “print_error()” if I do.
It gets more tricky because the Arduino environment adds a slew of function prototypes to the beginning of your file when it compiles. While it's nice not to have to write out function prototypes it does mess up my error; functionality. I make the line number correct by subtracting a constant I call ARDUINO_OFFSET. This is the number of functions other than setup() and loop(). It's often easiest to determine by intentionally causing an error and seeing how much the line number is off. I also have multiple files which the Arduino environment simply concatenates together. So I have the following at the end of my main file:
#define FILE_LENGTH 1153
It is simply the length of the file, the line number of its line. So my error routine prints out both the standard line number and the line number minus FILE_LENGTH. If the first line number is too big for the main file then look for the second line number in the second file.
Yes I know I said “easy” in the first sentence of this step and it's actually pretty convoluted. After you get it working the easy part is just adding “error;” wherever an error should occur. It saves a load of time and promotes robust code.
Step 12: LCD Backlight / PWM LED Control
Instead of using the standard resister to control the backlight LED I control it through software. This is made laughably easy thanks to the good folks who write the Arduino code. Just write a number from 0 (off) to 255 (fully on, bad idea, you'll fry your LED) with this command:
“ LCD_BACKLIGHT” is the pin connected to the LED and “ eeprom.lcd_backlight” is the value from 0-255.
By pressing up and down (with the clock horizontal, the normal way) the backlight increases and decreases. At first the amount was linear but I wanted finer adjustment at the lower end and larger adjustment at the higher end so I switched to a more logarithmic system. What I do is double the value when I press up and halve the value when I press down. Pretty simple. I never let it go above 128 to avoid toasting the LED.
The analogWrite() routine uses pulse width modulation which requires a timer. Since I'm using one timer for an interrupt routine, another for the buzzer, and the Arduino uses yet another for their millis() routine I had to be careful about which analog (PWM) output I used. I used the high tech technique of trying one pin and when that didn't work I tried another which did work. I ended up using pin 6 which is Timer 0 A.
Step 13: Sparkfun Programmer / Bare Bones Arduino Board
With all the Instructables and webpages about bare bones and breadboard Arduino boards I sure had a hard time wiring up the Sparkfun programmer. I used Sparkfun's FTDI Basic Breakout . I converted the programmer to 5v by cutting the connection to 3.3v and soldering the connection to 5v (see the image) because the LCD and all my projects operate on 5v. Power and ground on the programmer go to power and ground of your Arduino/ATMega328 just like you'd expect. The TX line goes to Arduino pin 0 RX (ATMega pin 2). The RX line goes to Arduino pin 1 TX (ATMega pin 3). The DTR line is the trickiest and nowhere seemed to just come out and explain this. It attaches to the RESET pin through a 0.1uF capacitor. Also, there is a 10k resister attached between the RESET pin and 5v. The CTS line is unconnected.
The first image for this step is my schematic zoomed in and shows the connections pretty well. On a side note, the labels of each line on the programmer is on the opposite side from the connections themselves. A fact that I missed for an embarrassing long time.
The only other part you need for a functioning Arduino is a crystal and two capacitors or a ceramic resonator. A ceramic resonator doesn't work for this project because exact time is needed. You can see this as well in the image of the schematic for this step.
Step 14: Hardware, Schematic and Board Layout
Attached to this step are the schematic and board layout files. They both use ExpressPCB’s excellent free software. If you don’t want to download the software you should be able to see the full schematic by looking at the full size image. The exact detailed steps for laying out the board is beyond the scope of this Instructable. Not everybody likes strip-board and this Instructable is already pretty long.
I used all 8 data, RS, R/W, and E pins on the LCD display. That's a total of 11 pins instead of the minimum of 6 (4 data, RS, and E). I did this to make the LCD operate as quickly as possible. Each and every time the Tetris screen is displayed, 8 new custom characters and all 32 characters have to be sent to the display. This is most quickly done if all pins are used.
The ICL7673 IC I used to switch between the battery and mains power is marvelous. Not only does it switch seamlessly without losing power to the higher of the two input voltages but it also indicates which input voltage is being used. Treat these pins as switches. That is, use the ATMega internal pullup resisters, it will pull the line to ground when that power source is being used. It should be noted the the datasheet for the part says, “The input supply voltage (VP or VS) slew rate should be limited to 2V per microsecond to avoid potential harm to the circuit.” This means that either supply voltage shouldn't go from 0 to whatever too fast or it could harm the device. To guard against this they recommend a “low-impedance capacitor such as a 0.047µF disc ceramic”. I threw a 0.1uF capacitor on each input because I have lots lying around. You can never have too many decoupling capacitors.
The keyed switch simply interrupts the wire to the buzzer so my wife can silence it without playing Tetris.
I used wire wrapping to make most of the connections. This is a double edged sword. While it makes wiring up a breeze, it's not the most robust connection. I haven't had any trouble except for the time I dropped it down the (hardwood) stairs. Which caused 3 of the wires to break. I recommend not dropping it down the stairs or throwing it at the wall if you don't want to wake up.
The circuit to recharge the battery comes directly from the ICL7673 datasheet . Refer to one of the images for this step. Deciding the resistor to use can be tricky. Start with the supply voltage, 12v, subtract 1v for the diode to prevent hooking up the power backwards and 1v for the diode needed in the charge circuit and we're down to 10v. The worst case scenario is that the battery will drop to 5v, it will likely never recover from such a discharge but we'll go with that number. So the total voltage drop across the resistor is at most 5v. We want no more than .01C charge rate, I used a 200mAh battery so we don't want more than 2mA. Ohm's law is V=I*R so the resistor = 5/.002 or 2500 ohms. I doubled that to 5600 ohms just to err on the side of caution. This means that if the battery gets low it might take forever to recharge. But since typically it spends 23 hours, 58 minutes charging and only 2 minutes on battery power while you play Tetris it should be fine. I really didn't want to fry (another hehe) battery.
Step 15: Enclosure / Base
The enclosure, everybody's least favorite step. I'm reminded of another Instructable that I referenced in a previous step which also happened to be an alarm clock. The user made a great project with neat features and put it in a cardboard box. I commiserate because I know the feeling. The enclosure is not much fun. But a project isn't done until it looks nice and is protected from the cat so suck it up and make an enclosure.
I started by creating a cardboard version of the box I intended to use from Radio Shack. This makes sure everything fits and looks nice before you start cutting up a plastic version. It's also a nice homage to my friend I mentioned earlier in this step.
I used a drill press, a rotary tool, and a steady hand to make the holes. For the hundreds of holes on the top which let the sound of the buzzer out I took a perfboard and some masking tape so all the holes were even. Then I used my drill press for the tedious and painstaking drilling. I have to say, I think it looks surprisingly good.
Mains power is supplied through two screws in the bottom of the box. A 12v wall-wort style supply goes to a wooden base I spray painted glossy black. The wires from the supply are soldered to a couple sheet metal contacts which are hot glued to the painted wood. The clock then sits on the wooden base with the screws in contact with the metal contacts. Wires come from the screws inside the box to provide power. A third screw is used in the rear of the bottom of the box to make sure the front two screws make good contact. The backlight of the LCD turns off after 30 seconds when on battery power to save on battery life and indicate that mains power isn't present.
Step 16: Conclusions
I am quite thrilled with how this turned out. And it even works like a charm to wake me up. It is never fun to wake up in the morning but I must say, there are worse ways than playing a little Tetris. I've been waking up to this clock for about a month now.
I am quite impressed with the Arduino environment. I can see why it's so popular. I will likely do more projects in the future. Not only do they make complicated tasks easy but the resulting project is available to a huge audience due to the popularity of Arduino.
Please ask any questions and make any suggestions or corrections (including things like spelling and grammar) in the comments. And vote for me in the microcontriller contest if you like my project.