Introduction: Sleep Cycle Alarm Clock With Motion Sensor

About: Hey! I am an Electrical Engineer and I love making fun and useful electronics projects. I am also interested in crafts of all sorts. In particular, I enjoy baking and sewing. Check out what I've made!

Motivation:
I wanted to somehow improve the standard alarm clock, so I made one that wakes you up gently by only waking you up when you're not in your deep sleep. An added bonus is that It plays the Super Mario Theme Song as its alarm.

How it works:
It is more pleasant to wake up in the morning if you are not woken during your REM cycle of sleep.You tend to move around more when you're not in your REM cycle, so this alarm clock will only go off if a certain amount of motion is detected after a set alarm time. As a fail-safe, if the required amount of motion is not detected, the alarm will go off an hour after the set time.

Step 1: Materials

In order to create this clock, you'll need:

Arduino Uno
DS1307 I2C Real Time Clock Module+Board (SainSmart)
PIR Motion Sensor (Futurlec)
Button
Piezo Buzzer
RGB LCD Shield with keypad (adafruit)
9V Wall Adapter Power Supply
Custom Shield (helpful for hooking up different elements to the Arduino in addition to the LCD shield)
10kOhm Resistor
Additional wires and boards for connecting components
Enclosure Box

Step 2: Program the Real Time Clock

What it is:
The DS1307 RTC is a chip that keeps time. It keeps track of the hour, minute, second, day, month, and year. It runs off of it's own battery, so even if the alarm clock is not plugged in, the RTC will keep the current time. The lithium ion battery included with the DS1307 board should last a couple years.

I used the SainSmart I2C RTC DS1307 AT24C32 Real Time Clock Module+Board which can be bought on Amazon at:

http://www.amazon.com/SainSmart-DS1307-AT24C32-Clock-module/dp/B006J4FZW4/ref=sr_1_1?ie=UTF8&qid=1368083281&sr=8-1&keywords=ds1307

You can also use just the DS1307 chip without a board. However, you will have to attach your own crystal oscillator, battery, and resistors.

How to hook it up:
Solder wires to Gnd, Vcc, Sda, and Scl. Gnd should connect to ground, Vcc should connect to the Arduino's built in 5V supply, Sda should connect to A4 (analog pin 4), and Scl should connect to A5 (analog pin 5).

Programming the RTC:
You need to run a program to set the time on the RTC to initialize it. I used the DS1307RTC library and the Time library to program my DS1307.

The DS1307RTC library can be found at:
http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
The Time library can be found at:
http://www.pjrc.com/teensy/td_libs_Time.html

In the DS1307RTC library examples folder there should be a program called "SetTime". Run this program to set the time of your RTC to the current time on your computer. You can then run the ReadTest example to make sure that the time was set.

Step 3: Attach Components to the Arduino

Custom shield and LCD Shield

I got the custom shield from my school. The shield enables you to access pins from the Arduino even when the LCD shield is attached.
This shield from adafruit should also work: 
http://www.adafruit.com/products/196#Tutorials
You can buy the LCD Shield here:
http://www.adafruit.com/products/714

Attach the custom shield on top of the Arduino and the LCD Shield on top of that.


Real Time Clock

Attach the RTC to 5V, Ground, A4, and A5. On my custom shield, I attached the RTC to the I2CB set of pins.


PIR Motion Sensor

When the pin attached to the PIR goes HIGH, motion has been detected.

The Futurlec motion sensor has 5 pins on it. I only used the 3 adjacent pins in the front. From left to right, the pins are Vcc, Out, Ground. I connected Vcc to the 5V source from the Arduino (on the custom shield I used I2CA for 5V and ground), Ground to the ground on I2CA, and Out to pin 6 on the Arduino (on the custom shield, pin 6 corresponded to the third screw on the green screw terminal).


Button

When the pin attached to the Button goes HIGH, the button has been pressed.

One pin on the button should be attached to 5V, the other should be attached to a 10kOhm resistor and then to ground (see picture on how to hook it up) and to pin 5 on the Arduino (2nd terminal on the green screw terminal on my shield).


Buzzer

One wire should be attached to Ground, the other wire should be attached to Arduino pin 9 (screw terminal 4 on the custom shield).

Step 4: Program the Clock

How to program each component:

Real Time Clock
After running the initial SetTime, it is very easy to use the DS1307. Be sure to include the Time.h and the DS1307RTC.h files and include the statement "tmElements_t tm;". In order to read the time from the clock, create an if statement saying "if(RTC.read(tm))" to check if the RTC time can be read. Within that if statement you can obtain any potion of the time by writing any of the following:
tm.Hour
tm.Minute
tm.Day
tm.Month
tm.Year

Buzzer
Use the Arduino tone() function to play notes of different frequency and duration on my piezo buzzer. Pin 9 serves as an output to the buzzer. The tone function should be written as follows:
tone(pin, frequency, duration);
I found that the best way to play and entire song was to create 3 arrays that whose individual items were accessed in a for loop. One array was for the frequencies of each note, one was for the duration of each note, and the last was for the pauses between notes. I found all of this information for the Super Mario theme song from the post by ReCreate on this forum:
http://arduino.cc/forum/index.php/topic,8409.0.html
He or she uses a separate Tone library in his or her code, so I just copied the frequencies, durations, and pauses into my own arrays. I also added 100 milliseconds to each pause and duration because the melody was a bit fast.

LCD Display
Download the Adafruit RGB LCD Shield Library found at the following link:
 https://github.com/adafruit/Adafruit-RGB-LCD-Shield-Library
In your code, include Adafruit_MCP23017.h and Adafruit_RGBLCDShield.h in order to manipulate the LCD shield.


How the final product should work:

The initial display on the LCD should read "Press Select". Pressing the select button will switch the clock from normal clock mode Set Alarm mode or back to clock mode. Once in Set Alarm mode, pressing the RIGHT button will enable you to set the alarm to on or off (controlled by the LEFT button), set the hour of the alarm (controlled by the UP button), or set the minute of the alarm (controlled by the DOWN button). No worries if this doesn't quite make sense yet. Each step has instructions on which button to press along the way. 

Once the alarm is going off, you can turn it off by simply pressing the extra button (attached in the front of my clock).


The full code:

My loop is pretty complicated, but the clock works! If you have suggestions on how to simplify the code, let me know. I found it most difficult to program the button presses as separate actions and to get the clock to print the date and time so that it would continually update.
Hopefully you'll be able to understand it in case you want to make tweaks for your own to the clock!




#include <DS1307RTC.h>
#include <Time.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// Code for a sleep cycle alarm clock
//Uses PIR motion sensor, LCD shield, button, piezo buzzer, Real Time Clock

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7


// Frequencies of tones played in alarm melody
int melody[] = {660,660,510,660,770,380,510,380,320,440,480,450,
430,380,660,760,860,700,760,660,520,580,480,510,380,320,440,480,
450,430,380,660,760,860,700,760,660,520,580,480,500,760,720,680,
620,650,380,430,500,430,500,570,500,760,720};

//duration of tones played in alarm melody
int duration[] = {200,200,200,200,200,200,200,200,200,200,180,200,
200,200,180,150,200,180,150,180,180,180,180,200,200,200,200,180,
200,200,200,180,150,200,180,150,180,180,180,180,200,200,200,200,
250,250,200,200,200,200,200,200,200,200,200};

//delay between tones
int delayTime[] = {175,250,250,150,250,375,387,325,300,350,250,265,
175,250,200,200,175,250,175,275,250,175,175,275,375,300,350,250,265,
175,250,200,200,275,250,175,275,250,175,175,350,250,150,175,175,250,
250,175,175,250,175,150,210,250,150};

int switchPin = 5;
int pirPin = 6;
int buzzerPin = 9;

//Selects either alarm set or clock mode
int modeSelect;
//integer choosing which part of the alarm to set
int alarmSelect;
//Integer for setting the alarm to on or off
int alarmSetOnInt;
//Integer for setting hour for alarm
int alarmHourSet;
//Integer for setting minutes for alarm
int alarmMinSet;
//Integer indicating whether the button has been pressed
int buttonVal;
//Integer tracking detected motion
int motionDetected;


//Whether or not the alarm is on
boolean alarmOn = false;
//Whether or not the alam is currently going off
boolean alarmGoingOff = false;
// boolean that is true once the alarm time has passed
boolean alarmCanGoOff = false;

tmElements_t tm;
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();


void setup(){

  Wire.begin();
  Serial.begin(9600);
  lcd.begin(16,2);

  // set backlight and initial message
  lcd.setBacklight(VIOLET);
  lcd.setCursor(1,1);
  lcd.print("Press select");

  pinMode(pirPin, INPUT);
  modeSelect = 0;
  alarmSelect = 0;
  alarmSetOnInt = 0;
  alarmHourSet = -1;
  alarmMinSet = -1;
  motionDetected = 0;
  alarmGoingOff = false;

  uint8_t i=0;

}


void loop(){
uint8_t buttons = lcd.readButtons();

buttonVal = digitalRead(switchPin);
  if(buttonVal == HIGH){
    lcd.clear();     
    alarmOn = false;
          alarmCanGoOff = false;
          alarmGoingOff = false;
       }

//If the alarm is not going off, print the normal date and time
   if(!alarmGoingOff){
     printDateTime();
    }

//If the alarm is going off and it is not the alarm time, continue the alarm
   if(alarmGoingOff & tm.Minute != alarmMinSet){
      lcd.clear();
      alarmOn = !alarmOn;
      alarmGoingOff=!alarmGoingOff;
    }

   if(buttons){
   //If select button is pressed, choose a mode
     if(buttons & BUTTON_SELECT) {
        if(modeSelect>1){
          modeSelect = 0;
         }
        lcd.clear();
        modeSelect=modeSelect+1;
        alarmSelect = 0;
        delay(1000);
        if(modeSelect == 1){
          lcd.clear();
          lcd.setCursor(1,0);
          lcd.print("Set Alarm");
          lcd.setCursor(1,1);
          lcd.print("press RIGHT");
          delay(1000);
         }

      }
    //If mode selected, is alarm set, enable setting of alarm
    if(modeSelect == 1){
      if(buttons & BUTTON_RIGHT){
        if(alarmSelect>2){
          alarmSelect = 0;
         }
         alarmSelect = alarmSelect+1;
         delay(1000);
         //Show whether alarm is on or off
         if(alarmSelect == 1){
                lcd.clear();
                if(!alarmOn){
                    lcd.setCursor(1,0);
                    lcd.print("Alarm off");
                    lcd.setCursor(1,1);
                    lcd.print("press LEFT");
                    delay(1000);
                }else if(alarmOn){
                    lcd.setCursor(1,0);
                    lcd.print("Alarm On");
                    print2digits(alarmHourSet,10,0);
                    lcd.setCursor(12,0);
                    lcd.print(":");
                    print2digits(alarmMinSet,13,0);
                }
         }else if(alarmSelect == 2){
               lcd.clear();
               lcd.setCursor(1,0);
               lcd.print("Set Hour");
               lcd.setCursor(1,1);
               lcd.print("press UP");
         }else if(alarmSelect == 3){
               lcd.clear();
               lcd.setCursor(1,0);
               lcd.print("Set Minutes");
               lcd.setCursor(1,1);
               lcd.print("press DOWN");
          }
      }
    }

//Set whether alarm is on or off 
   if(alarmSelect == 1){
      if(buttons & BUTTON_LEFT){
        alarmOn = !alarmOn;
        lcd.clear();
        delay(1000);
        if(alarmOn){
          lcd.print("Alarm On");
        }else if(!alarmOn){
          lcd.print("Alarm Off");
        }
      }
    }

  //Set hours  
  if(alarmSelect == 2){
    if(buttons & BUTTON_UP){
    lcd.clear();
    alarmHourSet = alarmHourSet+1;
    if(alarmHourSet>24){
    alarmHourSet = 0;
    }
    delay(500);
    }
    print2digits(alarmHourSet,14,0);
  }

  //Set minutes
  if(alarmSelect == 3){
    if(buttons & BUTTON_DOWN){
        lcd.clear();
        alarmMinSet = alarmMinSet+1;
        if(alarmMinSet>59){
            alarmMinSet = 0;
        }
        delay(100);
    }
    print2digits(alarmMinSet,14,0);
  }

}

//Alarm can go off once the alarm time is equal to the current time
if(alarmOn & alarmMinSet == tm.Minute & alarmHourSet == tm.Hour & modeSelect!=1){
alarmCanGoOff = true;
Serial.println("alarm can go off");
}

//If the alarm can go off, and the clock is in clock mode, the alarm will
//go off after 3 consecutive detections of motion
if(alarmCanGoOff & modeSelect!=1){
   Serial.println(alarmCanGoOff);
      int pirVal = digitalRead(pirPin);
      if(pirVal ==  HIGH){
          motionDetected++;
          Serial.println("motion detected");
          delay(1000);
      }
      if(pirVal == LOW){
      motionDetected = 0;
      }
      if(alarmCanGoOff){
      if(motionDetected>2 || tm.Hour == alarmHourSet+1){
          alarm();
          motionDetected = 0;
       }
      }     

  }

}


//Method for alarm. Changes display and plays melody
void alarm(){
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("WAKE UPPPP!!!!!!!!");
  alarmGoingOff = true;
for(int i=0; i<54; i++){
  buttonVal =  digitalRead(switchPin);
   if(buttonVal!=HIGH){
delay(delayTime[i]);
tone(buzzerPin, melody[i],duration[i]);
}
}

}


// print the date and time
void printDateTime(){
if(RTC.read(tm)&modeSelect==2){
  print2digits(tm.Hour,1,0);
  lcd.setCursor(3,0);
  lcd.print(":");
  print2digits(tm.Minute,4,0);
  lcd.setCursor(6,0);
  lcd.print(":");
  print2digits(tm.Second,7,0);
  print2digits(tm.Month,1,1);
  lcd.setCursor(3,1);
  lcd.print("/");
  print2digits(tm.Day,4,1);
   Serial.println(tm.Day);
  }
}

//Print numbers in a location using two digits (for proper clock format)
void print2digits(int number,int column, int row) {
  if (number >= 0 && number < 10) {
    lcd.setCursor(column,row);
    lcd.print('0');
    lcd.setCursor(column+1,row);
    lcd.print(number);
  }
  else if(number>=10){
  lcd.setCursor(column,row);
  lcd.print(number);
  }
}

Step 5: Put It All Together

You can use pretty much any box as an enclosure for you clock. Just make sure to cut out holes for the LCD Display, the PIR, the button, and the power cord.

I attached some components to a separate board in order to keep everything organized and to include the resistor for the button.

Once everything is in the box, congrats! You're done! You now have your alarm clock. Enjoy waking up peacefully in the morning!