Overview

Summary

My project was an alarm clock that forces the user to get up and get active – the alarm will only stop ringing when the user goes to certain locations in their room in a specific, randomized order.

 

I noticed that I can only really wake up if I’m forced to get out of my bed and get moving. A lot of people also feel similarly, so it isn’t uncommon to place a phone alarm across the room to force one to get up and turn it off. But after that, it quite easy to slip back into bed and drift into sleep again. What if you had to go to multiple locations in your room, maybe in hard to reach nooks and crevices, in a specific order that is generated by the alarm? That way, you have to really get moving in order to truly turn it off. By that point, ideally, you won’t feel as strong of an urge to slip into bed.

A front view of the alarm

A close up of the LCD that will show the commands

A close up of the button and dial the user will use to set the alarm

A look inside at all the electronics

Demo of setting the time I want the alarm to go off

A demo of how the alarm looks when it is set off and prompts the user to scan specific tags

Process

How I decided to represent locations in my room – simple tags on cards

Getting a prototype “dial” and button to work

Getting the RFID scanner to work was the first hurdle I needed to pass. At first, I merely used female to male jumper cables to connect the scanner to the Arduino, which I expected to work perfectly. However, I was constantly getting error messages that no scanner was actually connected. Sometimes I got lucky and I would get a stray couple of bytes to show that something is hooked up before the same error messages would pop up again. I checked the wiring over and over again and replaced the scanner module multiple times. After a couple of hours, I was at my wit’s end and finally decided to solder the wires instead of just using jumper cables. After soldering, it worked perfectly. I still don’t know what the problem was, but if I didn’t decide to take the soldering approach, perhaps I would have opted to not use an RFID scanner at all. I’m really glad that it ended up working because the scanner ended up being a key part of this project’s appeal.

RFID lit up and working – This guy gave me a lot of trouble

Testing how I would mount the pieces onto the front, I ended up cutting out the wrong dimensions for the LCD, so I would have to redo this box

I didn’t wire my components with the understanding that I would later be putting in some kind of housing, I just wired it in the way that seemed the easiest and fastest at the moment. When it came to actually placing it in the box I cut out, I realized I would have to do a huge overhaul of the way I had everything organized, essentially tearing out all the wires and starting all over again. In future projects, I’ll keep in mind the final presentation while I design the hardware layout to save myself a lot of pain.

Getting everything into the box proved to be a big hassle, lots of wires came undone in the process. I pretty much had to rewire the entire project. Good learning experience.

Discussion 

Response

The LED strips look a little messy on the outside. Maybe put something on top of them to diffuse the light?

I totally agree. Currently, my LED strip is held on by its own stickiness, extra double-sided tape, and prayers. I tried to make it look as straight as possible, but it definitely could be a lot straighter and neater. My original hope was that I could use an LED strip that was wrapped in white/clear silicone,  so it would nicely diffuse the lights while maintaining brightness, but I didn’t know if we had this in the Physical Computing Lab. Of course, it is an alarm so we do want the lights to be a little jarring, so maybe diffusing may not necessarily be the way to go.  However, I think maybe it could be in a sort of housing so that the LED strip isn’t just nakedly sitting on the box.

 

If the tags are placed permanently in [a] different location, maybe you could put the RFID scanner on the opposite side of your interface so the buttons don’t interfere with you holding the box against the tags.

I also agree with this critique. I realized as I was putting all the wires and components into the box that maybe it would be hard to scan the tags from the front, given there are buttons and knobs. I tried it out by sticking a tag on the wall and trying to scan it, and it did work after I tilted the box a little bit. This is definitely a design flaw that I should have kept in mind. This is not something I actively thought about while building because I simulated the walls by just bringing the tags to the RFID scanner location.  Maybe it would have been better to put the RFID facing the back, which is flat and can be brought up to the wall.

Self-Critique

I’m pretty happy with how the project turned out. Visually, it looks very similar to my original drawings/concepts. However, I would probably not have a hole for the RFID sensor in a more refined project. I put it in this prototype because I thought that I would want to see the exact place of the sensor for debugging purposes, and I was unsure if it could scan through the wood. Now that I know it actually has quite a considerable range, I would not put a hole there and would merely etch a symbol to let the user know that this is the location to scan at. This would look a lot cleaner than the version I have now.  I also think that some of my code is a little hacky, for lack of a better word, and that its efficiency can be improved considerably.  If given more time, I would consider cleaning up my code so that maybe the project would run smoother.

What I Learned

There were points in the project that if I decided to fully pivot there, I would have gotten it to a functioning state much faster. An example is when I noticed that the RTC wasn’t showing the correct time, and in fact, would generate random times at bootup. For almost a week, this issue didn’t improve because the library I was using simply wasn’t capable of properly flashing the correct time to the RTC. It wasn’t until after I finally used another library did it fix itself. However, if I was just more willing to switch libraries earlier on, I could have fixed this and focused on other stuff. So advice to both past and future self: don’t be afraid to have to redo things to get something to function, sometimes you’re going to have to pivot from what you are currently using to a totally new tool, and you’re going to have to be okay with that.

Next Steps

My original vision for this project was that the RFID would just be one component of many. I’d also have to play with other sensors to turn off the alarm as well. For example, the alarm would need a photoresistor to read values above a certain threshold, either by bringing the alarm to the light or by binging my phone flashlight up to it. This would end up as almost like a “bop-it” alarm clock, as you have to complete certain commands in rapid succession to turn it off. I still think this is a fun idea and is probably the approach I would take if I decide to keep working on it. Also, currently, the only way to “cancel” an alarm a user has already set up is by ripping out the battery. I would definitely make a reset/off/cancel button in an improvement of this project.

I would also like to integrate this with a way to measure and display information sleep as well, perhaps with a mobile/web app. It would be great to record the number of hours slept, probably by adding another button to set when the user goes to sleep and then tracking until the alarm is turned off. I think displaying this information on a graph and running some analytics on it could be extremely useful to the user when deciding how to change their sleeping habits.

Schematic

Code

/* Active Alarm
 * Neeharika Vogety (nvogety)
 *
 * 
 * Summary: An alarm will ring, vibrate, and light up at a time that a user sets 
 * to wake up at. The only way to turn off the alarm is to scan RFID tags set around
 * the room in a specific, randomly-generated order. The idea is to get the user up 
 * and moving to turn off the alarm, rather than just hitting a button and going back to
 * sleep.
 * 
 *  Inputs: 
 *  
 *  Arduino Pin | Input
 *     5          LED Strip Pin      
 *     2          Button Pin
 *     9          RFID Pin
 *     A1         Potentiometer Pin
 *  
 *  Outputs:
 *  
 *  Arduino pin | Output
 *      7         Vibrating Motor Pin
 *      4         Piezo Buzzer Pin
 */


#include <Wire.h>
#include <Time.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include <SPI.h>
#include <MFRC522.h>
#include <FastLED.h>

// Define important pins for RFID
#define RST_PIN         9          
#define SS_PIN          10 
#define TASKSLEN        5 // Define length of tasks array, used throughout code

// Define important constants for LED strip
#define LED_PIN     5
#define NUM_LEDS    45
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100

CRGBPalette16 currentPalette;
TBlendType    currentBlending;


typedef struct{
  char *tagName;
  char *tagUID;
} Tag;

// An array of Tag structs that links together
// the name of the tag and the ID of the tag.
// If this is changed, must change TASKLEN at
// the top!
Tag tasks[TASKSLEN] = {
  {"A", "437B172845C81"},
  {"B", "450B072845C80"},
  {"C", "42FB172845C81"},
  {"D", "461DF72845C80"},
  {"E", "49ED972845C80"},
};

int nums[TASKSLEN]; // Create nums array
int numOrder[TASKSLEN]; // Create num Order array
int nOindex = 0; // Index to traverse num Order

// Bools that we will use to execute certain parts of the code
bool startGeneration = false; 
bool readRFID = false;
bool setAlarm = true;
bool alarmSet = false;
// Hour and Minute, will set with RTC
int alarmHour;
int alarmMin;

LiquidCrystal_I2C screen(0x27, 16, 2); //Create Screen object
MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance
DS1307 RTC; // Create RTC object

// Initialize constant i/o pins
const int BUTTONPIN = 2;
const int POTPIN = A1;
const int VIBRPIN = 7;
const int BUZZERPIN = 4;

void setup() {
  Serial.begin(9600);
  
  // Set up INPUTS AND OUTPUTS
  pinMode(BUTTONPIN, INPUT_PULLUP);
  pinMode(POTPIN, INPUT);
  pinMode(VIBRPIN, OUTPUT);
  pinMode(BUZZERPIN, OUTPUT);

  // Set up RFID
  SPI.begin();      // Init SPI bus
  mfrc522.PCD_Init();   // Init MFRC522
  delay(4); 
  mfrc522.PCD_DumpVersionToSerial(); // Make sure RFID is up and running

  // Start RTC
  RTC.begin();

  //Set up LED String
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(  BRIGHTNESS );
  currentPalette = RainbowStripeColors_p;
  currentBlending = NOBLEND;
  clearLEDs();

  // Clear LCD screen and signal set up is over
  screen.init();
  screen.backlight();
  screen.home();
  screen.print("Booting up..");
  delay(1000);
  screen.clear();
}

void loop() {
  
  // If we still need to set an alarm time,
  //   display the current time and wait for button press
  //   to set the hour, then the minute of the alarm, and
  //   set the alarm
  if(setAlarm){

    // Display time until button is pressed
    while(digitalRead(BUTTONPIN) == LOW){
      DateTime now = RTC.now();
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("Current Time:");
      screen.setCursor(0, 1);
      if(now.hour() <= 9){
        screen.print("0");
      }
      screen.print(now.hour());
      screen.print(":");
      if(now.minute() <= 9){
        screen.print("0");
      }
      screen.print(now.minute());
      screen.print(":");
      if(now.second() <= 9){
        screen.print("0");
      }
      screen.print(now.second());
      delay(1000);
    }
    delay(1000);

    // Allow user to set up hour of alarm until button is pressed again
    while(digitalRead(BUTTONPIN) == LOW){
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("Set Alarm Hour:");
      // Set alarm hour with potentiometer
      alarmHour = map(analogRead(POTPIN), 0, 1023, 0, 24);
      screen.setCursor(0, 1);
      if(alarmHour <= 9){
         screen.print("0");
      }
      screen.print(alarmHour);
      delay(250);
    }
    delay(1000);

    // Allow user to set up minute of alarm until button is pressed again
    while(digitalRead(BUTTONPIN) == LOW){
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("Set Alarm Minute:");
      // Set alarm minute with potentiometer
      alarmMin = map(analogRead(POTPIN), 0, 1023, 0, 60);
      screen.setCursor(0, 1);
      if(alarmMin <= 9){
         screen.print("0");
      }
      screen.print(alarmMin);
      delay(250);
    }
    delay(1000);
    
    // Show when alarm is set for
    screen.clear();
    screen.setCursor(0, 0);
    screen.print("Alarm set for:");
    screen.setCursor(0, 1);
    if(alarmHour <= 9){
      screen.print("0");
    }
    screen.print(alarmHour);
    screen.print(":");
    if(alarmMin <= 9){
      screen.print("0");
    }
    screen.print(alarmMin);
    setAlarm = false;
    alarmSet = true;
    
  }

  // Keep checking if current hour and minute is what we set for the alarm
  if(alarmSet){
    DateTime now = RTC.now();
    if(now.hour() == alarmHour && now.minute() == alarmMin){
      startGeneration = true;
      alarmSet = false;
      screen.clear();
      screen.print("ALARM STARTED");
      // Set off buzzer, will make a humming sound
      tone(BUZZERPIN, 100);
      vibrate();
      vibrate();
      vibrate();
      Serial.print("ALARM STARTED!!!!!");
    }
  }

  // If alarm went off, start to create the random list of Tags
  if(startGeneration){
    // Fill nums in with just numbers counting from 0 up
    for(int i = 0; i < TASKSLEN; i++){
      nums[i] = i;
    }

    // Form new numOrder array by grabbing different parts of numArray
    for(int currlen = TASKSLEN; currlen > 0; currlen--){
      // Seed differently everytime
      randomSeed(analogRead(A0));
      // Get random number, which will be the index for the number to grab from num
      int randi = random(currlen);
      numOrder[nOindex] = nums[randi];
      nOindex++;
      // "Remove" the number we just picked from num by shifting over the other numbers
      //  This will guaruntee we don't pick that number again
      for(int k = randi; k < currlen; k++){
        nums[k] = nums[k+1];
      }
    }

    // Loop through the tasks in the order that we generated
    for(int i = 0; i < TASKSLEN; i++){
      screen.clear();
      screen.print("GO TO ");
      screen.print(tasks[numOrder[i]].tagName);
      readRFID = true;
      while(readRFID){
        
        // Keeping sounding the alarm, even while user is still scanning tags
        soundAlarm();
        
        // Keep checking if a new tag has been shown
        if ( ! mfrc522.PICC_IsNewCardPresent()) {
          continue;
        }

        if ( ! mfrc522.PICC_ReadCardSerial()) {
          continue;
        }

         // Print the tag info scanned for debugging purposes
         Serial.print("CARD SCANNED!!!!!: ");
         String readtagID = "";
         // The MIFARE PICCs that we use have 4 byte UID
         for ( uint8_t i = 0; i < 7; i++) { 
           readtagID.concat(String(mfrc522.uid.uidByte[i], HEX)); // Adds the 4 bytes in a single String variable
         }
         readtagID.toUpperCase();
         mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
         
         // If the tag scanned is the correct one in the order specified.
         //    move on to checking for the next tag
         if(readtagID == tasks[numOrder[i]].tagUID){
            screen.clear();
            if(i < TASKSLEN - 1){
            screen.print("Good! Next one..");
            vibrate();
            screen.clear();
            }
            readRFID = false;
         }
          
      }
    }

    // Show that user has finished
    screen.print("FINISHED");
    noTone(BUZZERPIN);
    vibrate();
    screen.clear();
    
    
    clearLEDs();
    startGeneration = false;
    setAlarm = true;
    loopCounter++;
  }
}

// What the user should see while alarm is going off,
//   start filling the LEDs with color
void soundAlarm(){
  static uint8_t startIndex = 0;
  startIndex = startIndex + 1; /* motion speed */
    
  FillLEDsFromPaletteColors( startIndex);
  FastLED.show();
  FastLED.delay(1000 / UPDATES_PER_SECOND);
}

// Fill LED strip based off of variables set at the beginning
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
    uint8_t brightness = 255;
    
    for( int i = 0; i < NUM_LEDS; i++) {
        leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
        colorIndex += 3;
    }
}

// Clear all the LEDs
void clearLEDs(){
  for( int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CRGB::Black;
  }
  FastLED.show();
}

// Pattern of vibration when alarm first goes off
void vibrate(){
  digitalWrite(VIBRPIN, HIGH);
  delay(1000);
  digitalWrite(VIBRPIN, LOW);
  delay(500);
}