Fight Alzheimer’s Disease with Karaoke

Problem

Alzheimer’s Disease is an irreversible, progressive brain disorder that slowly destroys memory and thinking skills, and, eventually, the ability to carry out the simplest tasks. Experts suggest that more than 5.5 million Americans, most of them age 65 or older, may have dementia caused by Alzheimer’s. Recent estimates indicate that the disorder is ranked third in the leading cause of death in U.S. With longer life expectancy and larger population, prevention of Alzheimer’s Disease is no doubt challenging but also necessary.

So far little is known about the cause of Alzheimer’s Disease, and no evidence has proven any treatment that works for sure. However, the National Academies of Sciences, Engineering, and Medicine (NASEM),  found “encouraging but inconclusive” evidence for three types of interventions:

  •  Increased physical activity
  • Blood Pressure Control
  • Cognitive training.

Further study shows that informal cognitively stimulating activities, such as reading or playing games, may lower risk of Alzheimer’s-related cognitive impairment and dementia. In addition, social activity and physical activities also have positive impact for prevention.

Solution

I was trying to come up with a device that can encourage the person to engage in social activity, cognitively stimulating activity, and physical activity. Then I thought about the Pokemon Go which you get rewarded by going to different places; I also thought about video games where items can be shared across different devices/accounts when they are connected, which encourages players to play together.

The device I made is basically a karaoke device that can display lyrics to the screen. An optional microphone is connected with the device through Bluetooth.

The layout of the device
The optional Microphone gives the vibe of Karaoke.

Learning the song and memorizing lyrics is also an informal cognitively stimulating activity. When the reader has learnt the song and does not need the lyrics anymore, the lyrics can be switched off by the top button.

However, there is no song to play initially. The user can select a music genre and time period once per day to obtain a random song that is in the selected category; to earn more songs, the user has to do some physical exercise such as walking and running for a period . The physical activity is detected by an accelerometer placed in the users’ shoes.

To encourage social activity, songs can be “shared” between two users. For example, if I find out that my friend has a song that I would like to have on my device, he can share the song with me. However, it is limited to one shared song per day. There is also a PK feature that allows the user to challenge each other; the two users will sing the same song, and whoever look at the lyrics for the least amount of time wins. The purpose of these mechanism is to make users meet more with their families and friends.

Proof of Logic

Layout

The layout of the components of the prototype

For this prototype, two Arduinos are used; The one on the left in the photo above is the prototype of the device, and the one on the right is a “dummy device” that only sends information through the transceiver. The “dummy device” is needed to show the interaction between two device; it is not made into another full prototype because of the lack of materials.

The Arduino of the full prototype connects to a accelerometer, a potentiometer, a mini-mp3 player, 4 buttons, a LCD screen, and a transceiver. The accelerometer detects motion for physical exercise. The potentiometer is for volume control. The mini-mp3 player works with the transducer speaker to play song. The buttons are for the user interface. The LCD screen is to show the user interface and display lyrics when songs are playing. The Transceiver allows communication between the prototype and the “dummy” Arduino.

The “dummy” Arduino is connected to a button and a transceiver; pressing the button triggers the Arduino to send information through the transceiver. The “dummy” Arduino can be powered by an external  of a 9V battery. However, it is plugged into PC during testing for convenience of uploading codes.

Interaction

Interface

The user interface of the prototype includes a LCD screen and four buttons. The first button from the top represents rolling the rotary encoder up, and the second button represents rolling the rotary encoder down. The rotary encoder was labeled “rotator” in the sketch of the layout of the device. The third button is the “Confirm” button of the rotary encoder, and the fourth button is the “Back/Lyrics” button.

In the following video, the functions of the four buttons are shown by going through the menu. In menu, there are 3 modules to choose: “Unlocked Songs”, “Be Connected”, and “Jackpot”.  The first and second button switches the current option.  The “Confirm” button select the selects option. And the “Back” button goes back to the previous stage. Notice that when option “Unlocked Songs” was selected in the video, it said empty. It was because there was no unlocked songs yet.

Jackpot

The next video shows the option “Jackpot”. The user has to select the desired genre and times of the song, then a random song from the selected category will be unlocked. For demonstration purpose, there are only 3 genres (Movie soundtrack, Pop, Jazz) and 2 period (50s, 2010s). Again, the user can only unlock one song per day through jackpot.

Physical Exercise

If the user wants to obtain more songs in a day, he/she can do so by doing physical exercise. An accelerometer can be placed in the user’s shoe or on user’s wrist as a fit-band; the physical activity will be detected by the accelerometer. I shook the accelerometer in the video to stimulate the physical activity. After exercising, the user obtains a chance to unlock a song again.

Playing Unlocked Song

With songs unlocked from the “Jackpot”, the user can play the unlocked songs and sing along them. The potentiometer represents the volume control of the device. When the song is being played, the “Confirm” button pauses the song and the “Back/ Lyrics” button toggles the lyrics. The lyrics are coded into the prototype because there are better applications to do that.  In addition, the potentiometer used in the demonstration does not always have a stable contact with the breadboard, which overwhelms the Arduino with data and makes the buttons less responsive.

Receive Song

If the user stays in the module of “Be Connected” and switches to “Share Song” as the current option (but not selecting it), the device can receive a new song from a different device. However, each device can only receive a song from another device once per day. After a receipt, no new song will be added anymore.

Share Song

In the “Be Connected” module, if the user selects the option of “Share Song” as shown in the video, the user can then choose a n unlocked song to share to a friend. There is no limits on how many songs a device can share per day.

Compete

In the module of “Be Connected”, the user can select an unlocked song to challenge his/her friend. When the song is being played, the device keeps track of how long the lyrics is been looked at. When the song finished, the devices will compare the time of showing the lyrics and decide a winner. In the video, the dummy Arduino sends its time of displaying the lyrics after the button was pressed.

Final Notes

  • When I started to work on the transceiver, I found out that I did not have enough female-to-male jumper wires. I bought some immediately but they only came the night before the crit, which makes it more stressful than it should be. Next time when I am preparing the components I have to not only think about major devices but all the materials including wires.
  • There are definitely more rooms for improvement. First of all, the device is designed as a device that downloads music from the internet instead of storing music in a micro-SD card. Some wifi or bluetooth parts should be able to connect the device to the internet. Secondly, the code can be futher optimized. It is cumbersome and inefficient at the point. Thirdly, more games such as guessing the name of the song can be added in addition to the competition of sing without looking at the lyrics. Fourthly, a vibration motor can be added to the device to notify the user when the opponent started to look at lyrics in competition. In addition, the interface can be better with lively sound effects added. For example, before a new song is unlocked, the device can play a little bit of drum rolling. Finally, the device can only became a real karaoke device when a microphone is incorporated.

Schematics

Schematics for the full prototype
Schematics for the dummy Arduino

Code

Code for the full prototype:

//Library
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//Set up of hardwares
//Mp3 player
SoftwareSerial mySoftwareSerial(6, 7); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
//LCD
LiquidCrystal_I2C screen(0x27, 16, 2);
//Transceiver
RF24 radio(8, 9); // CE, CSN



//Global Variables
//Constant variables
const int ROTARY_UP = 5;
const int ROTARY_DOWN = 4;
const int ROTARY_CONFRIM = 3;
const int BACK_LYRICS = 2;
const int VOLUME_CONTROL = A0;
const int XPIN = A1;
const int YPIN = A2;
const int ZPIN = A3;

//Chaning Variables
//Button
int ButtonState_rotaryUp;
int ButtonState_rotaryDown;
int ButtonState_Confirm;
int ButtonState_Back_Lyrics;

int lastButtonState_rotaryUp = HIGH;
int lastButtonState_rotaryDown = HIGH;
int lastButtonState_Confirm = HIGH;
int lastButtonState_Back_Lyrics = HIGH;

//Mp3
int lastVolume = 15;

//Debounce Varibale
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

//The variables related to display the lyrics
bool lyrics = false;
//This is for counting the time when lyrics are displayed throughout the song.
unsigned long lyrics_timer_Start;
unsigned long lyrics_time;
unsigned long lyrics_time_oponent;
bool playing = false;

//Accelerometer
bool timer = false;
unsigned long accelerometer_timer_Start;
unsigned long accelerometer_time;
//Exercise for 10 seconds to get a chance for jackpot
const unsigned long accelerometer_time_jackpot = 10000;

//The variables used in building the interface
String interFace = "Menu";
String interFace_Secondary;
int interFace_Option = 1;

String selected_Genre;
String selected_Period;

int Jackpot_Chance = 1;
unsigned long Jackpot_Clock;
const int OneDay = 60000;         //For demo purpose 1_Day is 1 minute.

unsigned long Receive_Clock;
bool song_received = false;

//Can't read the number of file yet in the global scope so make the array as big as the capacity of the SD card. Or assume the information is already given when the songs are downloaded from internet.
//The sizes of the array are set for the demo purpose
//Arrary that includes all the unlocked songs
int unlocked_Song[5];
int input_indice_unlocked;
//Array taht includes the unlocked 2010s movie soundtrack
int Unlocked_2010s_Movie[1];
int input_indice_movie;
int Unlocked_2010s_Pop[1];
int input_indice_pop;
int Unlocked_50s_Jazz[3];
int input_indice_jazz;

//Transceiver's addresses
//One for writing and one for reading
const byte addresses[][6] = {"00001", "00002"};


//Function
//Functions related to the MP3 player
void read_information() {
  Serial.println(myDFPlayer.readState()); //read mp3 state
  Serial.println(myDFPlayer.readVolume()); //read current volume
  Serial.println(myDFPlayer.readEQ()); //read EQ setting
  Serial.println(myDFPlayer.readFileCounts()); //read all file counts in SD card
  Serial.println(myDFPlayer.readCurrentFileNumber()); //read current play file number
  //Serial.println(myDFPlayer.readFileCountsInFolder(3)); //read fill counts in folder SD:/03
}

//Function related to the transveriver
void share_song(int shared_song) {
  radio.stopListening();
  radio.write(&shared_song, sizeof(shared_song));
}

//Function that receives song from  "Be connected"
void receive_song() {
  radio.startListening();
  if (radio.available() && song_received == false ) {
    int shared_song;
    radio.read(&shared_song, sizeof(shared_song));
    //Write the number of the number into the array for unlocked_songs
    unlocked_Song[input_indice_unlocked] = shared_song;
    input_indice_unlocked += 1;
    song_received = true;
    screen.clear();
    screen.setCursor(4, 0);
    screen.print("New Song");
    screen.setCursor(5, 1);
    screen.print("Added!");
    delay(3000);
    screen.clear();
    screen.setCursor(6, 0);
    screen.print("Menu");
    screen.setCursor(1, 1);
    screen.print("Unlocked Songs");
    interFace = "Menu";
    interFace_Option = 1;
    radio.stopListening();
  }
}



//Function related to the accelerometer
void read_accelerometer() {
  if (timer == false) {
    if (analogRead(ZPIN) > 321) {
      timer = true;
      accelerometer_timer_Start = millis();
    }
  }
  else {
    if (analogRead(ZPIN) <= 321 && analogRead(ZPIN) >= 317) {
      accelerometer_time += millis() - accelerometer_timer_Start;
      timer = false;
    }
  }
}

//Functions related to the interface of the device
//Function that checks whether there is an zero in the array, which means there is still song for that category to unlock. (supplemental to function jackpot)
bool CheckforZero(String Genre, String Period) {
  if (Genre == "Movie" && Period == "2010s") {
    for (int i = 0; i < (sizeof(Unlocked_2010s_Movie) / sizeof(Unlocked_2010s_Movie[0])); i++) {
      if (Unlocked_2010s_Movie[i] == 0) {
        return true;
      }
    }

  }


  else if (Genre == "Pop" && Period == "2010s") {
    for (int i = 0; i < (sizeof(Unlocked_2010s_Pop) / sizeof(Unlocked_2010s_Pop[0])); i++) {
      if (Unlocked_2010s_Pop[i] == 0) {
        return true;
      }
    }
  }

  else if (Genre == "Jazz" && Period == "50s") {
    for (int i = 0; i < (sizeof(Unlocked_50s_Jazz) / sizeof(Unlocked_50s_Jazz[0])); i++) {
      if (Unlocked_50s_Jazz[i] == 0) {
        return true;
      }
    }
  }

  return false;
  //return false when there is no more 0 in the array


}


//Function that check whether a number is already in an array. (supplemental to function jackpot)
bool checkNumberinList(int numb, String Genre, String Period) {
  if (Genre == "Movie" && Period == "2010s") {
    //Serial.println((sizeof(Unlocked_2010s_Movie) / sizeof(Unlocked_2010s_Movie[0])));
    //Serial.println(Unlocked_2010s_Movie[0]);
    for (int i = 0; i < (sizeof(Unlocked_2010s_Movie) / sizeof(Unlocked_2010s_Movie[0])); i++) {
      if (Unlocked_2010s_Movie[i] == numb) {
        //Serial.println("Here");
        return true;
      }
    }
  }

  else if (Genre == "Pop" && Period == "2010s") {
    for (int i = 0; i < (sizeof(Unlocked_2010s_Pop) / sizeof(Unlocked_2010s_Pop[0])); i++) {
      if (numb == Unlocked_2010s_Pop[i]) {
        return true;
      }
    }
  }

  else if (Genre == "Jazz" && Period == "50s") {
    for (int i = 0; i < (sizeof(Unlocked_50s_Jazz) / sizeof(Unlocked_50s_Jazz[0])); i++) {
      if (numb == Unlocked_50s_Jazz[i]) {
        return true;
      }
    }
  }
}



//Function that select a random song to unlock in Jackpot. (supplemental to function button pressed)
int jackpot(String Genre, String Period) {
  //Check if the selected category still has songs in the songbank to unlock
  if (CheckforZero(Genre, Period) != true) {
    //Print the information on the lcd screen
    screen.clear();
    screen.setCursor(1, 0);
    screen.print(String("No more " + Genre));
    screen.setCursor(3, 1);
    screen.print(String("in " + Period));
    delay(5000);
    screen.setCursor(1, 0);
    screen.print("Select another");
    screen.setCursor(2, 1);
    screen.print("genre/period");
    delay(5000);
    screen.clear();

    return 0;
  }

  else {

    if (Genre == "Movie" && Period == "2010s") {
      //Number 1 to number 1 represents Movie Soundtracks in 2010s
      int randomPick = random(1, 2);

      while (checkNumberinList(randomPick, Genre, Period) == true) {
        randomPick = random(1, 2);
        //Serial.println(randomPick);
      }
      //Add the song into the array of unlocked song
      unlocked_Song[input_indice_unlocked] = randomPick;
      input_indice_unlocked += 1;

      //Add the song into the array of unlcoked movie soundtrack array
      Unlocked_2010s_Movie[input_indice_movie] = randomPick;
      input_indice_movie += 1;


      return randomPick;
    }


    else if (Genre == "Pop" && Period == "2010s") {
      //Number 1 to number 1 represents Movie Soundtracks in 2010s
      int randomPick = random(2, 3);
      while (checkNumberinList(randomPick, Genre, Period) == true) {
        randomPick = random(2, 3);
      }
      //Add the song into the array of unlocked song
      unlocked_Song[input_indice_unlocked] = randomPick;
      input_indice_unlocked += 1;

      //Add the song into the array of unlcoked movie soundtrack array
      Unlocked_2010s_Pop[input_indice_pop] = randomPick;
      input_indice_pop += 1;

      return randomPick;
    }

    else if (Genre == "Jazz" && Period == "50s") {
      //Number 1 to number 1 represents Movie Soundtracks in 2010s
      int randomPick = random(3, 6);
      while (checkNumberinList(randomPick, Genre, Period) == true) {
        randomPick = random(3, 6);
      }
      //Add the song into the array of unlocked song
      unlocked_Song[input_indice_unlocked] = randomPick;
      input_indice_unlocked += 1;

      //Add the song into the array of unlcoked movie soundtrack array
      Unlocked_50s_Jazz[input_indice_jazz] = randomPick;
      input_indice_jazz += 1;

      return randomPick;
    }
  }
}


//Function thats returns the name of the song given the interface_option. (supplemental to function button pressed)
String SongName(int song) {
  if (song == 1) {
    return "City of Star";
  }
  else if (song == 2) {
    return "Stay";
  }
  else if (song == 3) {
    return "Fly Me to the Moon";
  }
  else if (song == 4) {
    return "All of You";
  }
  else if (song == 5) {
    return "The Best is yet to Come";
  }
}

//Function that determines what happens when the rotary enocoder is rolled up or down. (supplemental to function button pressed)
void Rotary(int input, String pressed_button, int min, int max) {
  if (pressed_button == "ROTARY_UP") {
    if (input == 1) {
      interFace_Option = max;
    }

    else {
      interFace_Option = input - 1;
    }
  }

  else if (pressed_button == "ROTARY_DOWN") {
    if (input == max) {
      interFace_Option = 1;
    }

    else {
      interFace_Option = input + 1;
    }
  }
}


//Function that decide what to do when buttons are pressed. (supplemental to function Read Button)
void button_pressed(String pressed_button) {
  //When buttons are pressed at the menu
  if (interFace == "Menu") {
    Rotary(interFace_Option, pressed_button, 1, 3);

    if (pressed_button == "ROTARY_CONFIRM") {
      if (interFace_Option == 1) {
        interFace = "Unlocked Songs";
        interFace_Secondary = "Select_Song";
        interFace_Option = 1;

      }

      else if (interFace_Option == 2) {
        interFace = "Jackpot";
        //Check if the user has earned a chance for jackpot through exercising
        if (accelerometer_time >= accelerometer_time_jackpot) {
          Serial.println(accelerometer_time);
          Jackpot_Chance += 1;
          accelerometer_time = 0;
        }
        if (Jackpot_Chance == 0) {
          //No more chance to get a new song for the day through Jackpot
          interFace_Secondary = "NoMore";
        }
        else {
          interFace_Secondary = "Genre";
        }
        interFace_Option = 1;

      }

      else if (interFace_Option == 3) {
        interFace = "Be Connected";
        interFace_Option = 1;
        interFace_Secondary = "Receive";
      }
    }

    //Display the options in Menu to the LCD screen
    //Clear the screen first
    screen.clear();
    if (interFace == "Menu") {
      screen.setCursor(6, 0);
      screen.print("Menu");
      if (interFace_Option == 1) {
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");
      }
      else if (interFace_Option == 2) {
        screen.setCursor(4, 1);
        screen.print("Jackpot");
      }
      else if (interFace_Option == 3) {
        screen.setCursor(2, 1);
        screen.print("Be Connected");
      }
    }
    else if (interFace == "Unlocked Songs") {
      screen.setCursor(1, 0);
      screen.print("Unlocked Songs");
      if (unlocked_Song[interFace_Option - 1] == 0) {
        screen.setCursor(5, 1);
        screen.print("Empty");
        delay(3000);
        screen.clear();
        screen.setCursor(6, 0);
        screen.print("Menu");
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");
        interFace = "Menu";
        interFace_Option = 1;
      }

      else {
        int song = unlocked_Song[interFace_Option - 1];
        screen.setCursor((16 - SongName(song).length()) / 2, 1);
        screen.print(SongName(song));
      }
    }
    else if (interFace == "Jackpot") {
      if (interFace_Secondary == "NoMore") {
        screen.setCursor(2, 0);
        screen.print("Unavailable");
        screen.setCursor(3, 1);
        screen.print("Right now");
        delay(3000);
        screen.clear();
        screen.setCursor(1, 0);
        screen.print("Earn More Song");
        screen.setCursor(1, 1);
        screen.print("by exercising");
        delay(3000);
        screen.clear();
        screen.setCursor(2, 0);
        screen.print("and meeting");
        screen.setCursor(4, 1);
        screen.print("friends!");
        delay(3000);
        //Goes back the menu page
        screen.clear();
        interFace = "Menu";
        interFace_Option = 1;
        screen.setCursor(6, 0);
        screen.print("Menu");
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");

      }


      else  {
        screen.setCursor(5, 0);
        screen.print("Genre");
        screen.setCursor(5, 1);
        screen.print("Movie");
      }
    }

    else if (interFace == "Be Connected") {
      screen.setCursor(2, 0);
      screen.print("Be Connected");
      screen.setCursor(3, 1);
      screen.print("Share Song");
    }
  }

  //When buttons are pressed at Unlocked Songs
  else if (interFace == "Unlocked Songs") {
    if (interFace_Secondary == "Select_Song") {
      Rotary(interFace_Option, pressed_button, 1, input_indice_unlocked);


      if (pressed_button == "ROTARY_CONFIRM") {
        myDFPlayer.play(unlocked_Song[interFace_Option - 1]);

        interFace_Secondary = "Playing_song";
        playing = true;
        screen.clear();
      }

      else if (pressed_button == "BACK_LYRICS") {
        interFace = "Menu";
        interFace_Option = 1;
        screen.clear();
        screen.setCursor(6, 0);
        screen.print("Menu");
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");

      }
    }

    else if (interFace_Secondary == "Playing_song") {
      if (pressed_button == "ROTARY_CONFIRM") {
        playing = !playing;
        if (playing == false) {
          Serial.println("pause");
          myDFPlayer.pause();
        }
        else {
          myDFPlayer.start();
        }
      }

      else if (pressed_button == "BACK_LYRICS") {
        //Serial.println(myDFPlayer.readState());
        //This means the song is finished
        if (myDFPlayer.readState() == 512) {
          if (lyrics == true) {
            lyrics = false;
          }

          interFace = "Menu";
          interFace_Option = 1;
          screen.clear();
          interFace = "Menu";
          interFace_Option = 1;
          screen.setCursor(6, 0);
          screen.print("Menu");
          screen.setCursor(1, 1);
          screen.print("Unlocked Songs");
        }
        else {
          lyrics = !lyrics;
          //Serial.println(lyrics);
          if (lyrics == true) {

            screen.setCursor(5, 0);
            screen.print("Lyrics");
            screen.setCursor(5, 1);
            screen.print("Lyrics");
          }
          else {
            screen.clear();
            //Add the lyrics-displaying time to the sum

          }
        }
      }
    }

    //Display the options in Menu to the LCD screen
    //Clear the screen first

    if (interFace == "Unlocked Songs") {
      if (unlocked_Song[0] == 0) {
        screen.clear();
        screen.setCursor(1, 0);
        screen.print("Unlocked Songs");
        screen.setCursor(5, 1);
        screen.print("Empty");
      }

      else if (interFace_Secondary == "Select_Song") {
        int song = unlocked_Song[interFace_Option - 1];
        //Serial.println("Hello");
        screen.clear();
        screen.setCursor(1, 0);
        screen.print("Unlocked Songs");
        screen.setCursor((16 - SongName(song).length()) / 2, 1);
        screen.print(SongName(song));
      }
    }
    else if (interFace == "Menu") {
      screen.clear();
      screen.setCursor(6, 0);
      screen.print("Menu");
      if (interFace_Option == 1) {
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");
      }
    }
  }

  //When buttons are pressed at Jackpot
  else if (interFace == "Jackpot") {
    //Selecting the Genre of the music of jackpot
    if (interFace_Secondary == "Genre") {
      Rotary(interFace_Option, pressed_button, 1, 3);


      if (pressed_button == "ROTARY_CONFIRM") {
        if (interFace_Option == 1) {
          selected_Genre = "Movie";
        }

        else if (interFace_Option == 2) {
          selected_Genre = "Jazz";
        }

        else if (interFace_Option == 3) {
          selected_Genre = "Pop";
        }

        interFace_Secondary = "Period";
        interFace_Option = 1;
        //Serial.println(selected_Genre);

      }

      else if (pressed_button == "BACK_LYRICS") {
        interFace = "Menu";
        interFace_Option = 1;
      }

      //Display the options in Menu to the LCD screen
      //Clear the screen first
      screen.clear();
      if (interFace == "Menu") {
        screen.setCursor(6, 0);
        screen.print("Menu");
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");
      }
      else if (interFace_Secondary == "Period") {
        screen.setCursor(5, 0);
        screen.print("Period");
        screen.setCursor(5, 1);
        screen.print("2010s");
      }

      else {
        screen.setCursor(5, 0);
        screen.print("Genre");
        if (interFace_Option == 1) {
          screen.setCursor(5, 1);
          screen.print("Movie");
        }

        else if (interFace_Option == 2) {
          screen.setCursor(6, 1);
          screen.print("Jazz");
        }

        else if (interFace_Option == 3) {
          screen.setCursor(6, 1);
          screen.print("Pop");
        }
      }
    }

    //Selecting the Period
    else if (interFace_Secondary == "Period") {
      Rotary(interFace_Option, pressed_button, 1, 2);


      if (pressed_button == "ROTARY_CONFIRM") {
        if (interFace_Option == 1) {
          selected_Period = "2010s";
          //When there is no more songs in the selected category to unlock
        }
        else if (interFace_Option == 2) {
          selected_Period = "50s";
        }

        int song = jackpot(selected_Genre, selected_Period);
        if (song == 0) {
          //Go back to choose another genre
          interFace_Secondary = "Genre";
          interFace_Option = 1;
          screen.setCursor(5, 0);
          screen.print("Genre");
          screen.setCursor(5, 1);
          screen.print("Movie");
          return;
        }
        else {
          //Serial.println(jackpot(selected_Genre, selected_Period));
          Jackpot_Chance -= 1;
          screen.clear();
          screen.setCursor(4, 0);
          screen.print("Jackpot!");
          String Acquired_Song = SongName(song);
          screen.setCursor((16 - Acquired_Song.length()) / 2, 1);
          screen.print(Acquired_Song);
          delay(2000);
          //Returns to the Menu page
          interFace = "Menu";
          interFace_Option = 1;
          screen.clear();
          screen.setCursor(6, 0);
          screen.print("Menu");
          screen.setCursor(1, 1);
          screen.print("Unlocked Songs");
          return;
        }
      }

      else if (pressed_button == "BACK_LYRICS") {
        interFace_Secondary = "Genre";
        interFace_Option = 1;
      }

      //Display the options in Menu to the LCD screen
      //Clear the screen first
      screen.clear();
      if (interFace_Secondary == "Genre") {
        screen.setCursor(5, 0);
        screen.print("Genre");
        screen.setCursor(5, 1);
        screen.print("Movie");
      }
      else {
        screen.setCursor(5, 0);
        screen.print("Period");
        if (interFace_Option == 1) {
          screen.setCursor(5, 1);
          screen.print("2010s");
        }

        else if (interFace_Option == 2) {
          screen.setCursor(6, 1);
          screen.print("50s");
        }
      }
    }
  }

  //When the interface is Be Connected
  else if (interFace == "Be Connected") {
    if (interFace_Secondary == "Receive") {
      Rotary(interFace_Option, pressed_button, 1, 2);

      if (pressed_button == "ROTARY_CONFIRM") {
        if (interFace_Option == 1) {
          interFace_Secondary = "share_song";
          interFace_Option = 1;
        }

        else if (interFace_Option == 2) {
          interFace_Secondary = "compete";
          interFace_Option = 1;
        }
      }


      else if (pressed_button == "BACK_LYRICS") {
        interFace = "Menu";
        interFace_Option = 1;
      }

      //lcd display
      screen.clear();
      if (interFace == "Menu") {
        screen.clear();
        screen.setCursor(6, 0);
        screen.print("Menu");
        screen.setCursor(1, 1);
        screen.print("Unlocked Songs");
      }
      else if (interFace_Secondary == "Receive") {
        screen.setCursor(2, 0);
        screen.print("Be Connected");
        if (interFace_Option == 1) {
          screen.setCursor(3, 1);
          screen.print("Share Song");
        }
        else if (interFace_Option == 2) {
          screen.setCursor(4, 1);
          screen.print("Compete");
        }
      }

      else if (interFace_Secondary == "share_song") {
        screen.setCursor(3, 0);
        screen.print("Share Song");
        if (unlocked_Song[0] == 0) {
          screen.setCursor(5, 1);
          screen.print("Empty");
          delay(2000);
          interFace_Secondary = "Receive";
          interFace_Option = 1;
          screen.setCursor(2, 0);
          screen.print("Be Connected");
          screen.setCursor(3, 1);
          screen.print("Share Song");
        }

        else {
          int song = unlocked_Song[interFace_Option - 1];
          //Serial.println("Hello");
          screen.setCursor((16 - SongName(song).length()) / 2, 1);
          screen.print(SongName(song));
        }
      }

      else if (interFace_Secondary == "compete") {
        screen.setCursor(4, 0);
        screen.print("Compete");
        if (unlocked_Song[0] == 0) {
          screen.setCursor(5, 1);
          screen.print("Empty");
          delay(2000);
          interFace_Secondary = "Receive";
          interFace_Option = 1;
          screen.setCursor(2, 0);
          screen.print("Be Connected");
          screen.setCursor(3, 1);
          screen.print("Share Song");
        }

        else {
          int song = unlocked_Song[interFace_Option - 1];
          //Serial.println("Hello");
          screen.setCursor((16 - SongName(song).length()) / 2, 1);
          screen.print(SongName(song));
        }
      }







    }

    else if (interFace_Secondary == "Playing_song") {
      if (pressed_button == "ROTARY_CONFIRM") {
        playing = !playing;
        if (playing == false) {
          Serial.println("pause");
          myDFPlayer.pause();
        }
        else {
          myDFPlayer.start();
        }
      }

      else if (pressed_button == "BACK_LYRICS") {
        //Serial.println(myDFPlayer.readState());
        //This means the song is finished
        if (myDFPlayer.readState() == 512) {
          if (lyrics == true) {
            lyrics_time += millis() - lyrics_timer_Start;
            lyrics = false;
          }

          //Send and receive the lyrics_time, then determines who wins
          radio.stopListening();
          radio.write(&lyrics_time, sizeof(lyrics_time));
          radio.startListening();
          while (!radio.available()); //wait until the lyrics_time of the opponenet was received
          radio.read(&lyrics_time_oponent , sizeof(lyrics_time_oponent));

          if (lyrics_time <= lyrics_time_oponent) {
            screen.setCursor(4, 0);
            screen.print("You Win!");
            screen.setCursor(2, 1);
            screen.print("Great Work!");
          }
          else {
            screen.setCursor(4, 0);
            screen.print("You Lose");
          }
          delay(5000);
          lyrics_time = 0;
          lyrics_time_oponent = 0;

          //Goes back to the menu
          interFace = "Menu";
          interFace_Option = 1;
          screen.clear();
          interFace = "Menu";
          interFace_Option = 1;
          screen.setCursor(6, 0);
          screen.print("Menu");
          screen.setCursor(1, 1);
          screen.print("Unlocked Songs");
        }
        else {
          Serial.println("Lyrics");
          lyrics = !lyrics;
          //Serial.println(lyrics);
          if (lyrics == true) {
            lyrics_timer_Start = millis();
            screen.setCursor(5, 0);
            screen.print("Lyrics");
            screen.setCursor(5, 1);
            screen.print("Lyrics");
          }
          else {
            screen.clear();
            //Add the lyrics-displaying time to the sum
            lyrics_time += millis() - lyrics_timer_Start;
          }
        }
      }
    }
    //Instead of receiving information, either sharing song or competing
    else {
      Rotary(interFace_Option, pressed_button, 1, input_indice_unlocked);


      if (pressed_button == "ROTARY_CONFIRM") {
        //Sending the number of the song through transceiver
        if (interFace_Secondary == "share_song") {
          int song = unlocked_Song[interFace_Option - 1];
          share_song(song);
          //lcd displays the song shared confirmation
          //Serial.println("Here");
          screen.clear();
          screen.setCursor(6, 0);
          screen.print("Song");
          screen.setCursor(4, 1);
          screen.print("Shared!");
          delay(2000);
          interFace_Secondary = "Receive";
          interFace_Option = 1;
        }

        //For competing
        else if (interFace_Secondary == "compete") {
          int song = unlocked_Song[interFace_Option - 1];
          share_song(song);
          myDFPlayer.play(unlocked_Song[interFace_Option - 1]);

          interFace_Secondary = "Playing_song";
          playing = true;

          screen.clear();
        }
      }

      else if (pressed_button == "BACK_LYRICS") {
        interFace_Secondary = "Receive";
        interFace_Option = 1;
      }

      //lcd display
      screen.clear();
      if (interFace_Secondary == "Receive") {
        screen.setCursor(2, 0);
        screen.print("Be Connected");
        screen.setCursor(3, 1);
        screen.print("Share Song");
      }

      else if (interFace_Secondary != "Playing_song") {
        if (interFace_Secondary == "share_song") {
          screen.setCursor(3, 0);
          screen.print("Share Song");
        }
        else if (interFace_Secondary == "compete") {
          screen.setCursor(4, 0);
          screen.print("Compete");
        }
        int song = unlocked_Song[interFace_Option - 1];
        screen.setCursor((16 - SongName(song).length()) / 2, 1);
        screen.print(SongName(song));
      }
    }
  }
}


//Function that read the pressing of the buttons
void read_buttons() {
  //Read Potentiometer and adjust the volume
  int potVal = analogRead(VOLUME_CONTROL);
  int currentVolume = map(potVal, 0, 1023, 0, 30);

  if (currentVolume != lastVolume) {
    //Serial.println(currentVolume);
    myDFPlayer.volume(currentVolume);
    lastVolume = currentVolume;
  }

  //Reading the buttons (with debounce)
  int reading_Up = digitalRead(ROTARY_UP);
  int reading_Down = digitalRead(ROTARY_DOWN);
  int reading_Confirm = digitalRead(ROTARY_CONFRIM);
  int reading_back_Lyrics = digitalRead(BACK_LYRICS);

  if (reading_Up != lastButtonState_rotaryUp || reading_Down != lastButtonState_rotaryDown || reading_Confirm != lastButtonState_Confirm || reading_back_Lyrics != lastButtonState_Back_Lyrics) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    //Rotary up button
    // if the button state has changed:
    if (reading_Up != ButtonState_rotaryUp) {
      ButtonState_rotaryUp = reading_Up;

      if (ButtonState_rotaryUp == LOW) {
        //Serial.println(interFace_Option);
        Serial.println("Rotary Up");
        button_pressed("ROTARY_UP");
        Serial.println(interFace_Option);
      }
    }

    //Rotary Down Button
    if (reading_Down != ButtonState_rotaryDown) {
      ButtonState_rotaryDown = reading_Down;

      if (ButtonState_rotaryDown == LOW) {
        Serial.println("Rotary Down");
        button_pressed("ROTARY_DOWN");
        Serial.println(interFace_Option);
      }
    }

    //Rotary Confirm Button
    if (reading_Confirm != ButtonState_Confirm) {
      ButtonState_Confirm = reading_Confirm;

      if (ButtonState_Confirm == LOW) {
        Serial.println("Confirm");
        button_pressed("ROTARY_CONFIRM");
        Serial.println(interFace);
      }
    }

    //Back or show lyric Button
    if (reading_back_Lyrics != ButtonState_Back_Lyrics) {
      ButtonState_Back_Lyrics = reading_back_Lyrics;

      if (ButtonState_Back_Lyrics == LOW) {
        Serial.println("Back or show lyrics");
        button_pressed("BACK_LYRICS");
        Serial.println(interFace);
      }
    }
  }


  // save the readinga. Next time through the loop, they'll be the lastButtonState:
  lastButtonState_rotaryUp = reading_Up;
  lastButtonState_rotaryDown = reading_Down;
  lastButtonState_Confirm = reading_Confirm;
  lastButtonState_Back_Lyrics = reading_back_Lyrics;
}


//Setup Function
void setup()
{
  //For the mp3 player
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);


  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true);
  }
  Serial.println(F("DFPlayer Mini online."));

  //For the LCD screen
  screen.init();
  screen.backlight();


  //Set serial communictaion time out 500ms
  myDFPlayer.setTimeOut(500);
  //----Set volume----
  myDFPlayer.volume(15);  //Set volume value (0~30).
  //----Set different EQ----
  myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
  //----Set device we use SD as default----
  myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);


  //Setting up the pins (buttons)
  pinMode(ROTARY_UP, INPUT_PULLUP);
  pinMode(ROTARY_DOWN, INPUT_PULLUP);
  pinMode(ROTARY_CONFRIM, INPUT_PULLUP);
  pinMode(BACK_LYRICS, INPUT_PULLUP);
  pinMode(XPIN, INPUT);
  pinMode(YPIN, INPUT);
  pinMode(ZPIN, INPUT);

  //Initial Display on the lcd for the menu
  screen.setCursor(4, 0);
  screen.print("Welcome");
  delay(1500);
  screen.clear();
  screen.setCursor(6, 0);
  screen.print("Menu");
  screen.setCursor(1, 1);
  screen.print("Unlocked Songs");

  //Set up the transceiver
  radio.begin();
  radio.openWritingPipe(addresses[1]); // 00002
  radio.openReadingPipe(1, addresses[0]); // 00001
  radio.setPALevel(RF24_PA_MIN);



  //Test ground
  //Serial.println(Unlocked_2010s_Movie[0]);
  //myDFPlayer.play(1);
  //read_information();
}

void loop()
{
  //Check if it is already 1 day after the last Jackpot. If so, add 1 chance to jackpot.
  if (millis() - Jackpot_Clock > OneDay) {
    accelerometer_time = 0;
    Jackpot_Chance += 1;
    Jackpot_Clock = millis();
  }

  //Check if it is already 1 day after the last reception of song. If so, allow reception again.
  if (millis() - Receive_Clock > OneDay) {
    song_received = false;
    Receive_Clock = millis();
  }


  //Update the interface according to the inputs
  read_accelerometer();
  read_buttons();

  //Check if there is song being shared to receive
  if (interFace == "Be Connected") {
    //Song_received is the bool variable that check whether a reception has already occured 
    if (interFace_Secondary == "Receive" && song_received == false) {
      if (interFace_Option == 1) {
        receive_song();
      }
    }
  }
}

Code for the “Dummy Arduino”:

//Library
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(8, 9); // CE, CSN

//Addresses for transceriver
const byte addresses[][6] = {"00001", "00002"};

//Variables for the button 
const int BUTTONPIN = 7;
int buttonState;             
int lastButtonState = HIGH; 

//For debouncing
unsigned long lastDebounceTime = 0;  
unsigned long debounceDelay = 50; 

//Change this to 1-5 for sharing songs. Any number for stimulating the competition.
int shared_song = 100000;

//Function to send a integer representing a song through transceiver. It can be used to send int for any purpose. 
void share_song() {
  radio.stopListening();
  radio.write(&shared_song, sizeof(shared_song));
  shared_song += 1;
  Serial.println("Shared");
}

void setup() {
  radio.begin();
  radio.openWritingPipe(addresses[0]); // 00002
  radio.openReadingPipe(1,addresses[1]); // 00001
  radio.setPALevel(RF24_PA_MIN);
  
  pinMode(BUTTONPIN, INPUT_PULLUP);
}


void loop() {
  /*
  int song = 1;
  radio.write(&song, sizeof(song));
  delay(1000);
  */

   int reading = digitalRead(BUTTONPIN);


  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      
      if (buttonState == LOW) {
        share_song();
      }
    }
  }
  lastButtonState = reading;
}

 

Audible Cooking Thermometer

Problem

I always find tracking the internal temperature of the things being cooked tedious . Sometimes I have to stop what I am doing to check on it, sometimes I have to look at it the whole time if the food can easily be overcooked. If I forget to check, there is a risk of overcooking and therefore bad texture and taste.

Solution

A device that actively tracks the internal temperature of the food and reminds the user when needed can solve this problem. The device is composed of a temperature sensor, a transducer speaker, and a screen. The temperature sensor of the device will be stuck into the middle of the meat or broth to measure the rate of rise in temperature.

The components of the device

If the rising rate is too large that might lead to overcooking, the device plays “Mi Re Do” to remind the user to turn down the gas. Text will be displayed on the screen if the user needs further information. In the same fashion, if the rising rate is too small, the device plays “Do Re Mi”. The user can stop the sound by pressing the device. In addition, specific instructions can be set for different foods. For the demo, an reminder to flip the fish for stir-frying salmon comes up when the temperature reaches a specific value. The device will play a melody when the food is cooked when the temperature reaches its ideal value. However, the device will only be successful if the algorithm of the cooking temperature for different foods is well developed.

Proof of Logic

Because I am not sure if the sensor can be used on real meat, and because it will take a longer time, I changed the rate of temperature rise in each cooking stage and did a demonstration with my hand instead. I stimulate the cooking process by holding the temperature sensor and raises its temperature from room temperature (about 23 Celcius) to 32 Celcius.

Overall setup of the prototype
Schematics
//libary
#include <OneWire.h>
#include <DallasTemperature.h>
#include "pitches.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>


//definition
#define ONE_WIRE_BUS 5
#define SPEAKER 7
#define CONFIRM_BUTTON 2

//Attachment
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
LiquidCrystal_I2C screen(0x27, 16, 2);



//Global variable
float Celcius = 0;
float Fahrenheit = 0;
float room_temp = 0;
int start_time = 0;

const bool isInterrupt = true;
bool confirmbuttonState = false;
bool isDone = false;

static const float salmon_flip_over_temp = 28;  //This is the default temperature for flipping salmon
static const float done_temp = 32;  //This is the temperature when the salmon is "cooked".
bool flip = false;

float last_read = 0;
float current_read = 0;
float rise_over_time = 0;


//Varibles for the timer of recording data, record data every 2 seconds
unsigned long clock_data = 0; // variable for timing
const int INTERVAL1 = 2000; // milliseconds between updates

//Functions
void read_temperature() {
  sensors.requestTemperatures();
  Celcius = sensors.getTempCByIndex(0);
  Fahrenheit = sensors.toFahrenheit(Celcius);
  Serial.print(" C  ");
  Serial.print(Celcius);
  Serial.print(" F  ");
  Serial.println(Fahrenheit);
}

void ConfirmButtonPressed()
{
  if (digitalRead(CONFIRM_BUTTON) == LOW) {
   confirmbuttonState = true;
  }
}



void temp_rise(float rise_over_time, float lowerbound, float upperbound) {
  if (rise_over_time > upperbound) {
    screen.clear();
    screen.setCursor(3, 0);
    screen.print("Turn Down");
    while (confirmbuttonState == false) {
      tone(SPEAKER, NOTE_E4, 200);
      delay(350);
      tone(SPEAKER, NOTE_D4, 200);
      delay(350);
      tone(SPEAKER, NOTE_C4, 200);
      delay(1000);

    }

    confirmbuttonState = false;
    screen.clear();
  }

  if (rise_over_time < lowerbound) {
    screen.clear();
    screen.setCursor(4, 0);
    screen.print("Turn Up");
    while (confirmbuttonState == false) {
      tone(SPEAKER, NOTE_C4, 200);
      delay(350);
      tone(SPEAKER, NOTE_D4, 200);
      delay(350);
      tone(SPEAKER, NOTE_E4, 200);
      delay(1000);

    }

    confirmbuttonState = false;
    screen.clear();
  }
}

//The song to play when cooking is finished
void fly_me_to_the_moon() {
  tone(SPEAKER, NOTE_C5);
  delay(500);
  tone(SPEAKER, NOTE_B4);
  delay(500);
  tone(SPEAKER, NOTE_A4);
  delay(500);
  tone(SPEAKER, NOTE_G4);
  delay(250);
  tone(SPEAKER, NOTE_F4);
  delay(1000);
  tone(SPEAKER, NOTE_G4);
  delay(250);
  tone(SPEAKER, NOTE_A4);
  delay(500);
  tone(SPEAKER, NOTE_C5);
  delay(500);
  tone(SPEAKER, NOTE_B4);
  delay(500);
  tone(SPEAKER, NOTE_A4);
  delay(500);
  tone(SPEAKER, NOTE_G4);
  delay(500);
  tone(SPEAKER, NOTE_F4);
  delay(250);
  tone(SPEAKER, NOTE_E4);
  delay(1250);
  if (confirmbuttonState == true){
    return;
  }
  
  noTone(SPEAKER);
  delay(250);
  tone(SPEAKER, NOTE_A4);
  delay(250);
  tone(SPEAKER, NOTE_G4);
  delay(500);
  tone(SPEAKER, NOTE_F4);
  delay(500);
  tone(SPEAKER, NOTE_E4);
  delay(250);
  tone(SPEAKER, NOTE_D4);
  delay(1000);
  tone(SPEAKER, NOTE_E4);
  delay(250);
  tone(SPEAKER, NOTE_F4);
  delay(500);
  tone(SPEAKER, NOTE_A4);
  delay(250);
  tone(SPEAKER, NOTE_F4);
  delay(250);
  tone(SPEAKER, NOTE_GS4);
  delay(750);
  tone(SPEAKER, NOTE_F4);
  delay(500);
  tone(SPEAKER, NOTE_E4);
  delay(500);
  tone(SPEAKER, NOTE_D4);
  delay(250);
  tone(SPEAKER, NOTE_C4);
  delay(1750);


  noTone(SPEAKER);
  delay(1000);
}

//This function is called when the "salmon" option is selected
void stirfry_salmon() {
  //check the rate of temperature rise every 2 seconds
  if (millis() >=  clock_data) {
    sensors.requestTemperatures();
    Celcius = sensors.getTempCByIndex(0);
   

    current_read = Celcius;
    rise_over_time = (current_read - last_read) / 2;

    //Print information onto the lcd screen
    int minute = (millis() - start_time) / 60000;
    int second = (millis() - 60000 * minute) / 1000;

    //Serial.print(minute);
    //Serial.println(second);


    screen.setCursor(0, 0);
    screen.print("Salmon");
    screen.setCursor(0, 1);
    screen.print((String)"Temp:" + current_read + "C");
    screen.setCursor(7, 0);
    screen.print((String)"Time:" + minute + ":" + second);




    //Check whether it is time to flip the fish
    if (current_read >= salmon_flip_over_temp && flip == false) {
      screen.clear();
      screen.setCursor(6, 0);
      screen.print("Flip");
      screen.setCursor(7, 1);
      screen.print("it");
      while (confirmbuttonState == false) {
        tone(SPEAKER, NOTE_C5, 200);
        delay(350);
        tone(SPEAKER, NOTE_B4, 200);
        delay(350);
        tone(SPEAKER, NOTE_A4, 200);
        delay(350);
        tone(SPEAKER, NOTE_G4, 200);
        delay(350);
        tone(SPEAKER, NOTE_A4, 200);
        delay(350);
        tone(SPEAKER, NOTE_B4, 200);
        delay(350);
        tone(SPEAKER, NOTE_C5, 200);
        delay(1000);
      }

      confirmbuttonState = false;
      screen.clear();
      flip = true;
    }

    //Checked if the fish is already cooked
    if (current_read >= done_temp) {
      screen.clear();
      screen.setCursor(5, 0);
      screen.print("Cooked");
      while (confirmbuttonState == false) {
        fly_me_to_the_moon();
      }

      confirmbuttonState = false;
      screen.clear();
      isDone = true;
      return;
    }

    //Every cooking stage has a different "standard" rate of temperature rise
    //0-10s The first stage of cooking
    if (millis() - start_time <= 10000) {
      temp_rise(rise_over_time, 0.12, 0.7);
    }

    //10-16s The second stage of cooking
    if (millis() - start_time > 10000 && millis() - start_time <= 16000 ) {
      temp_rise(rise_over_time, 0.12, 0.19);
    }

    //16-24s The third stage of cooking
    if (millis() - start_time > 16000 && millis() - start_time <= 24000 ) {
      temp_rise(rise_over_time, 0.6, 0.9);
    }

    //>24s The fourth stage of cooking
    if (millis() - start_time > 10000 && millis() - start_time <= 16000 ) {
      temp_rise(rise_over_time, 0, 0.3);
    }



    last_read = current_read;
    clock_data = millis() + INTERVAL1 ;
    //Serial.print(Celcius);
    //Serial.print("     ");
    Serial.print(current_read);
    Serial.print("     ");
    Serial.println(rise_over_time);
  }
}

void setup()
{
  //Initialization
  Serial.begin(9600);
  sensors.begin();
  screen.init();
  screen.backlight();

  //Setting the pinMode
  pinMode(CONFIRM_BUTTON, INPUT_PULLUP);

  //Attach interrupt
  if (isInterrupt) {
    attachInterrupt (digitalPinToInterrupt(CONFIRM_BUTTON), ConfirmButtonPressed, FALLING);
  }


  //Record the room temperature
  sensors.requestTemperatures();
  Celcius = sensors.getTempCByIndex(0);
  room_temp = Celcius;
  last_read = room_temp;

  //test-field
  //tone(SPEAKER, NOTE_A4,200);
  //screen.setCursor(0, 1);
  //screen.print("2nd row text");
  start_time = millis();

 

}

void loop(void)
{
  if (isDone == false) {
  stirfry_salmon();
  }
  
}

 

Mini Assignment 9

  • a song clothes washer/dryer plays when the cycles are finished. They are in a closet so sometimes they go unnoticed even though the cycles are finished.
  • Some sound effect that plays before I head out to let me know if it is raining or snowing outside. It is very frustrating to realize I need to go back up to grab an umbrella when I already went all the way down.
  • Sound effect that reminds me that the defrost is finished in the sink. It usually takes about 90 minutes and I can forget about it from time to time.
  • Plays a very happy song when package arrives. Sometimes I don’t know they are already here because they are in the leasing office.
  • A voice line to remind me to head out 5 minutes before the bus arrives. There is a bus stop near my apartment and it would be nice if I can get to the stop right before the bus comes.

Shadow Boxing Partner

Problem

I have just started practicing boxing a few weeks ago and every week we work on a new drill. During class I can work with a partner and get familiar with the hits and rhythm of the drill. After I am familiar with drill, I will have to react to the movement made by my partner and execute the drill accordingly. When I shadow-box in home and without a partner, however, there is no hint or reminder on the next hit as I try to get familiar with the drill again. In addition, shadow-boxing also lacks the reacting aspect of s?m.parring. Therefore, a device that acts as the shadow boxing partner is designed.

Solution

The design of the devices

The device is made up by six speakers that sticks to the wall. Each square in the diagram above represents a speaker. The diagram shows the default arrangement of the speakers. The number written near the speakers  represents the number for the hit the speakers are responsible for. For example, when a jab needs to be thrown, the speaker on the top left-corner will play the sound effect because the number of jab is 1. The middle speakers are responsible to upper cut to chin and body shots to the middle. The device will connect to phone, and the sound effect player by the speakers depending on what drill is being practiced. The one of the navy drills, for example, includes a left hook (3), a straight punch (2), and then a left hook (3). The speaker that is responsible for left hook will play the sound effect for left hook, and the speaker responsible for straight punch will play the sound effect for straight punch. The sound effects of each type of punches are recorded from actual punches. They might not be that distinct comparing to each other but the position of the speaker already gives a big hint on the next hit and the user should at least know what hits are included in the drill. When the user gets comfortable with the drill, the device can be switched to difficult mode. Only the first punch or the “hit” from the partner is given and the user has to react to it. The user can also change the drill through the smart phone.

Proof of Logic

The demo of the device

Because of the lack of materials, I cannot make the full device with 6 speakers. Instead, I will use speaker, solenoid, and vibration motor to stimulate the sound effects for hook, straight punch/ jab, and body shoots. The first button on the right hand side is to switch between drills and the second button is to switch between normal mode and difficult mode.

Code:

//libary
#include "pitches.h"


//Defining the pins
#define solenoide 12
#define motor 13
#define speaker 7

#define drill_pin 3
#define hint_pin 2

//Global varibales
//For interrupt
const bool isInterrupt = true;
int drill;
int drill_number = 1;
bool hint_status = true;
bool solenoid = true;
int delay_in_between = 350;

//Functions
void move_solenoid() {
  if (solenoid == true) {
    digitalWrite(solenoide, HIGH);
    solenoid = false;
  }

  else {
    digitalWrite(solenoide, LOW);
    solenoid = true;
  }
}

void vibrate_motor() {
  digitalWrite(motor, HIGH);
  delay(300);
  digitalWrite(motor, LOW);
}

void DrillSwitchPressed() {
  if (digitalRead(drill_pin) == LOW) {
    drill == drill_number ? drill = 0 : drill ++;
  }
}


void HintSwitchPressed() {
  if (digitalRead(hint_pin) == LOW) {
    hint_status = !hint_status;
  }
}

//Functions for the drill
void Navy_drill(int random_numb) {
  if (hint_status == true) {
    if (random_numb == 1) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4,200);
      delay(delay_in_between);
      move_solenoid();
      delay(delay_in_between);
      tone(speaker, NOTE_A4,200);
     
      
    }

    if (random_numb == 2) {
      move_solenoid();
      delay(delay_in_between);
      tone(speaker, NOTE_A4,200);
      delay(delay_in_between);
      move_solenoid();
    }

    if (random_numb == 3) {
      //Upper cut
      vibrate_motor();
      delay(delay_in_between);
      move_solenoid();
      delay(delay_in_between);
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4,200);
      
    }

    if (random_numb == 4) {
      //Upper cut
      //tone(speaker, NOTE_C4);
      //delay(200);
      //tone(speaker, NOTE_D4);
      //delay(100);
      //tone(speaker, NOTE_E4, 100);
      vibrate_motor();
      delay(delay_in_between);
      //hook
      tone(speaker, NOTE_A4, 200);
      delay(delay_in_between);
      move_solenoid();
    }
  }

  else {
    if (random_numb == 1) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4, 200);
      
    }

    if (random_numb == 2) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4, 200);
    }

    if (random_numb == 3) {
      //Body shot coming in
      vibrate_motor();
    }

    if (random_numb == 4) {
      //Body shot coming in
      vibrate_motor();
    }
  }   
}


void Dodge_drill(int random_numb) {
  if (hint_status == true) {
    if (random_numb == 1) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4,200);
      delay(delay_in_between);
      move_solenoid();
    }

    if (random_numb == 2) {
      tone(speaker, NOTE_A4,200);
      delay(delay_in_between);
      move_solenoid();
      
    }
  }

  else {
    delay(random(0,2000));
    if (random_numb == 1) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4, 200);
      
    }

    if (random_numb == 2) {
      //NOTE_A4 is the note for hook
      tone(speaker, NOTE_A4, 200);
    }
  }   
}
void setup() {
  // Setup the pin modes
  pinMode(solenoide, OUTPUT);
  pinMode(motor, OUTPUT);

  //When the buttons are pushed, digitalRead shows 0

  pinMode(drill_pin, INPUT_PULLUP);
  pinMode(hint_pin, INPUT_PULLUP);


  //Initiate the serial monitor
  Serial.begin(9600);


  // attach the interrupt pin to a method
  if (isInterrupt) {
    attachInterrupt (digitalPinToInterrupt(drill_pin), DrillSwitchPressed, FALLING);
    attachInterrupt (digitalPinToInterrupt(hint_pin), HintSwitchPressed, FALLING);
  }



 
  //vibrate_motor();
  //tone(speaker, NOTE_C4);
  //delay(200);
  //tone(speaker, NOTE_D4);
  //delay(100);
  //tone(speaker, NOTE_E4, 100);
}






void loop() {
  if (drill == 0) {
    //randomSeed(analogRead(A0));
    int random_numb = random(1,4);
    Navy_drill(random_numb);
    delay(3000);
  }

  if (drill == 1) {
    //randomSeed(analogRead(A0));
    int random_numb = random(1,2);
    Dodge_drill(random_numb);
    delay(4000);
  }
}

 

Assignment 8

The effects of sounds on me physically and emotionally are closely related to the experience I had when I heard the sound before. For example, when I hear the ringing of large bell my breath slows down and my shoulder tends to relax. Emotionally I feel relax, calm, and gradually proceeds to “empty”. This is because I associate the sound of the ringing of bell with my previous experience in temples I have been to. For another example, when I hear the sound of stir frying, I have the impulse to smell whatever that is being cooked because smell indicates the status of the food and whether it is time to add seasoning. Emotionally I also feel a little uptight because I have to watch the food and make the right decision at right time before the food is over-cooked.

New meaning can be attached to sounds that are previously meaningless when a new experience  is acquired. For example, the sounds of boxing glove hitting boxing mitts seem identical and are hard to tell if no context is given. However, the more boxing a person does, the more he or she can tell if the hit is powerful and at the right angle from the sound of the hit.

I have listened to the “Suspend 1″in the star trek sounds and I guessed its meaning to be uncertain and dangerous. I then listened to “Red alert” and this time I guessed alert. In many movies, TV-series, and video games alert is often portraited by some combination of sounds from lower pitch rising to higher pitch. In terms of car sounds, I think the sounds of gears, tires, and honks have more meanings than other sounds made by the cars. When I hear a new special sound effect, I first compare it to sound effects I heard before. If there are similar ones I heard before, I will have a similar emotional feel as if I am hearing the similar ones. If it is nothing like anything I have heard before, then I will respond to the volume, rhyme, and texture of the sound effect.

Use of Sound in Physical Interaction

Akousmaflore: The Sound of Plant

The interaction installation is composed of plants that react to gentle contact by producing a computer-synthesized sound. The texture and volume of the sound depends on movements and gestures made by the person.

Taiko no  Tatsujin

This is a arcade/video game that based on music and rhyme. Players can recognize if they are doing a good job by listening to the rhyme of the music, the rhyme made by the drum, and feedback lines like “50 strikes”.

Augmented Drilling Machine

The device makes pulsing sound of different pitches if the drill is deviated from the ideal angle to different angles.

Braille Translator

Problem

It can be very difficult for people with seeing difficulty to orient themselves. In fact, it is already difficult enough to locate the braille signs, which is supposed to help them. In addition, there are situations where visual clues are necessary, such as deciding which elevator goes up when two arrive at the same time. Here is a video that mentions such difficulty.

As a result, people with visual difficulty have to memorize routes. It is also very challenging for them to travel through a new district or locate a room in an unfamiliar building.

Solution

The key that causes this problem is the lost of visual clues. When we walk into a new building, we can look for signs for directions. When we travel through a new place, we can use google map as a guide.  However, for people with seeing difficulty, the same clues are either impossible to get, like the road sings for example, or very inaccessible like the braille sign that hides behind a door.

To solve this problem, I would like to translate the visual clues into braille on a device that is accessible. In other words, instead of looking for physical sign of braille, the user can read the signs in braille on a device in his or her hand. This device, however, can not only be used as a navigator but also a “seeing” tool to read the visuals in a space or environment.

The design of the prototype

As shown in the figure above, the device have 12 digits for displaying letters and punctuations. Each digit is made of 6 solenoids and will display alphabet in braille. The rotary encode on the right is for flipping to the next 12 letters in the sentences. There are also a confirm button and a back button. This device can be connected to smart phone and indoor devices through Bluetooth or wi-fi.

Suppose a person with difficulty wants to go to new place, he or she can use app on  his or her smartphone to determine the route. The person can then use the device to navigate while walking. When there are road signs or alerts that need attention, the device will vibrate and display the message. The device will go back to the route when the alert was read and confirm button pressed. The person can also use the back button to check the last direction or instruction.

Of course, the effectiveness of this device depends heavily on how small we can make it to be. With the current material of mini-solenoid, I can make it to have a length of 18 cm and a width of 7.2 cm. This cannot work effective as a navigation tool because it is too big and cumbersome. However, it can still be used while stationary such as in a restaurant, museum, and a room.

For future design

If we have the interactive surface technology in the future, this is the design I will give to the device. It wrap around the arm of the user so that the user can hold a white cane in one hand, and read the information using another hand. No more buttons and rotary encoder are needed because everything can be displayed and interacted with through the surface.

Proof of Logic

The components of the demonstration

The demo is made up of a solenoid, a Bluetooth module, 6 LED, 4 buttons, and a vibration motor. The LEDs represent the 6-dot grid of the braille. The corresponding grid is shown below.

LED braille grid

Because the demo-device is relatively complex, it will be explained with video of demonstration.

This shows the normal function of the solenoid. In the later demonstration it does not function correctly due to the insufficient current arduino UNO supplies.

The two button pressed in the video represents the rotary encode that allows the user to flip to the next or previous 12 letters.

The buttons pressed in the video are confirm button and back button, which allows the user to display the next or previous message instruction.

Finally, in the video alerts are sent from the PC to the device through Bluetooth. The device then vibrate and display the alert with the corresponding braille letter.

Schematic:

Code:

//Defining the pins
#define solenoide 12
#define motor 13
#define rotary_up 5
#define rotary_down 4
#define confirm 3
#define back 2
#define LED1 11
#define LED2 10
#define LED3 9
#define LED4 8
#define LED5 7
#define LED6 6

//Variables
//constant variables
char *INPUT_STRINGS[] = {"information destk is at first floor","elevator is to the right by ten meters" ,"this is room b ten"};

//Message is like the route and direction
int message;
int message_length;
String string;
int start_letter;

//Alert is like the signs or alerts on the street or in buildings
String incoming_alert; //string that stores the incoming message
String alert[5];
int alert_index;
bool has_alert;
bool vibration_on;

//variable for interrupt, note that message is also a ISR variable
const bool isInterrupt = true;
int message_number = 3;

//timer
//Clock 1 is the timer for checking the rotary encoder
unsigned long clock1 = 0; // variable for timing
const int INTERVAL1 = 300; // milliseconds between updates

//Clock 2 is for serial printing for illustration
unsigned long clock2 = 0; // variable for timing
const int INTERVAL2 = 2000; // milliseconds between updates

//Clock 3 is for serial printing for illustration
unsigned long clock3 = 0; // variable for timing
const int INTERVAL3 = 1500; // milliseconds between updates


// the interrupt method
// NOTE:  we shouldn't use Serial.prinln(), delay(), and many
// other functions in an interrupt method
void ConfirmSwitchPressed()
{
  if (digitalRead(confirm) == LOW) {
    if (has_alert == true) {
      //alert index also show how many alerts are in the alert array
      if (alert_index == 1) {
        has_alert = false;
      }

      for (int i; i < alert_index; i++) {
        alert[i] = alert[i + 1];
      }

      if (alert_index != 0) {
        alert_index -= 1;
      }

      Serial.println("Alert read");
      return;
    }


    if (has_alert == false) {
      message == message_number ? message = 0 : message++;
      start_letter = 0;

      Serial.println("Next Message");
    }
  }
}

void BackSwitchPressed()
{
  if (digitalRead(back) == LOW) {

    if (has_alert == false) {
      message == 0 ? message = message_number : message--;
      start_letter = 0;

      Serial.println("Previous Message");
    }
  }
}

//Functions
//demonstration function that shows how to move solenoid
void move_solenoid() {
  digitalWrite(solenoide, HIGH);
  delay(1000);
  digitalWrite(solenoide, LOW);
  delay(1000);
}

//Demonstration function
void vibrate_motor() {
  digitalWrite(motor, HIGH);
  delay(2000);
  digitalWrite(motor, LOW);
  delay(2000);
}


void show_letters() {
  if (has_alert == true) {
    braille(alert[0][start_letter]);
  }

  if (has_alert == false) {
    braille(INPUT_STRINGS[message][start_letter]);
  }

  //if there are actually 12 digits, each made of 6 solenoid
  //int digit;
  //for (i=start_letter, i< start_letter +13,i++) {
  //     braille(digit, INPUT_STRINGS[message][start_letter + i])
  //     digit += 1;
  //    }
}

//This function put the incoming alert into the alert array
void insert_alert() {
  //-1 indicates that there is no \ in the alert message
  int null_index = incoming_alert.indexOf('.');
  if (null_index != -1 and alert_index < 4) {
    alert[alert_index] = incoming_alert.substring(0, null_index);
    alert_index += 1;
    //Tell the system there are alerts
    has_alert = true;

    //Turn the vibration and its corresponding timer on
    digitalWrite(motor, HIGH);
    vibration_on = true;
    clock3 = millis() + INTERVAL3;

    //Clear the string for the next incoming alert
    incoming_alert = "";

    Serial.print("Alert received, ");
    Serial.println("vibration on.");
  }
}



//In the actual desgin instead of this demo, the 6 LEDs are 6 solenoides
void braille(char input) {
  digitalWrite(LED1, LOW);
  digitalWrite(LED2, LOW);
  digitalWrite(LED3, LOW);
  digitalWrite(LED4, LOW);
  digitalWrite(LED5, LOW);
  digitalWrite(LED6, LOW);
  digitalWrite(solenoide, LOW);


  if (input == 'a') {
    digitalWrite(LED1, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'b') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'c') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED4, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'd') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'e') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'f') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'g') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'h') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'i') {

    digitalWrite(LED2, HIGH);
    digitalWrite(LED4, HIGH);

  }

  if (input == 'j') {
    digitalWrite(LED2, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);
  }

  if (input == 'k') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED3, HIGH);

    digitalWrite(solenoide, HIGH);

  }

  if (input == 'l') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'm') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'n') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED5, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'o') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED5, HIGH);
    digitalWrite(LED3, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'p') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);

    digitalWrite(solenoide, HIGH);
  }

  if (input == 'q') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }


  if (input == 'r') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }



  if (input == 's') {
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);

  }


  if (input == 't') {
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);
  }


  if (input == 'u') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED6, HIGH);

    digitalWrite(solenoide, HIGH);
  }


  if (input == 'v') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED6, HIGH);

    digitalWrite(solenoide, HIGH);

  }


  if (input == 'w') {

    digitalWrite(LED2, HIGH);
    digitalWrite(LED6, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);
  }


  if (input == 'x') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED6, HIGH);

    digitalWrite(solenoide, HIGH);
  }



  if (input == 'y') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED6, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED4, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }


  if (input == 'z') {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED6, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED5, HIGH);

    digitalWrite(solenoide, HIGH);
  }


  if (input == ',') {
    digitalWrite(LED2, HIGH);
  }


  if (input == '.') {
    digitalWrite(LED2, HIGH);
    digitalWrite(LED6, HIGH);
    digitalWrite(LED5, HIGH);
  }

  if (input == '!') {
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    digitalWrite(LED5, HIGH);
  }
}



void setup() {
  // Setup the pin modes
  pinMode(solenoide, OUTPUT);
  pinMode(motor, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(LED6, OUTPUT);


  //When the buttons are pushed, digitalRead shows 0
  pinMode(rotary_up, INPUT_PULLUP);
  pinMode(rotary_down, INPUT_PULLUP);
  pinMode(confirm, INPUT_PULLUP);
  pinMode(back, INPUT_PULLUP);


  //Initiate the serial monitor
  Serial.begin(9600);

  // attach the interrupt pin to a method
  if (isInterrupt) {
    attachInterrupt (digitalPinToInterrupt(confirm), ConfirmSwitchPressed, FALLING);
    attachInterrupt (digitalPinToInterrupt(back), BackSwitchPressed, FALLING);
  }


}


void loop() {
  //Determining the number of messages/alerts and the length of each message

  if (has_alert == true) {
    string = alert[0];
    message_length = string.length();
  }

  if (has_alert == false) {
    string = INPUT_STRINGS[message];
    message_length = string.length();
  }

  //Receiving the characters of incoming alerts
  while (Serial.available()) {
    //while there is data available on the serial monitor
    //Serial.println(Serial.read());
    incoming_alert += char(Serial.read()); //store string from serial command
  }

  insert_alert();

  //Check if any movement has been made on the rotary encoder
  if (millis() >=  clock1) {

    if (digitalRead(rotary_up) == LOW) {
      if (start_letter + 12 <= message_length) {
        start_letter += 12;
        Serial.println("Next 12 letters");
      }
    }

    if (digitalRead(rotary_down) == LOW) {
      if (start_letter - 12 >= 0)  {
        start_letter -= 12;
        Serial.println("Last 12 letters");
      }
    }

    clock1 = millis() + INTERVAL1 ;

  }

  show_letters();

  //For serial printing of demonstration
  if (millis() >=  clock2) {

    if (incoming_alert != "") {
      Serial.print("Incoming Alert: ");
      Serial.println(incoming_alert);
    }

    //Serial.print("The current message or alert: ");
    Serial.println(string);

    
    clock2 = millis() + INTERVAL2 ;

  }

  //For 2-second duration of the vibration motor
  if (millis() >=  clock3 and vibration_on == true) {
    digitalWrite(motor, LOW);
    vibration_on = false;
    Serial.println("vibration off.");
  }


}

 

Humidity Monitor

Problem

Plotted plants are great choice for indoor decoration offering a sense of liveliness. However, the humidity in people’s rooms may be too low or too high. For example, some tropical plants typically require relatively higher humidity. In addition, it may get very dry during winter, making it difficult for the plotted plants to thrive.

Solution

The set-up of the device.

The solution I came up with is a system that monitors the humidity at the proximity of the plant. The system is composed of two separate device. The device on the left is hung on the wall with a fan and a servo arm that holds a spray bottle. The device on the right is a humidifier with a humidity sensor on it.

To demonstrate how the system work, suppose we have a delicate plant that requires misting if the average humidity is below 60% for the past 4 hours. The humidity cannot be over 90% because the plant can be suffocated or harmed by pests. Therefore, every 4 hours the system will check the average humidity and if  it is below 60%, the servo-arm will swing up and down to remind the person in the room to mist the plant. If the humidity level does not change in 5 minutes, the servo-arm will stop and the humidifier will be turned on to increase the humidity and sustain it for a specific period. If the humidity level is over 90%, the fan will work until the humidity is below 90%.

When higher humidity is required, the servo-arm comes in first because manual misting is preferred, which gives the person more awareness of the condition of the  plant. A night mode can be selected and the humidity is regulated autonomously by the system. Data and commands are sent between the two devices through IR transmitter and receiver.

Proof of Concept

The prototype includes a servo motor, a dc motor, a humidity sensor, a red LED, and a power module on the right.

Code:

#include <dht.h>
#include <Servo.h>

// Analog Pin sensor is connected to
#define dht_apin A0
#define SERVO_PIN 2
#define LED_PIN 13
#define ENABLE 5
#define DIRA 3
#define DIRB 4


dht DHT;
Servo servo;

//The vairables that define time-flow
const int hour = 8000; //In this time-scale 4 sec represent 1 hour

const int ideal_humid = 60;

//The array that store the humidities
int humidity_Data[48];
int record_digit; //The digit for inputing data into the array
int check_digit; // The digit for checkingg every 2-hour
int check_points = 8; // How many datapoints to check for the checking period

String plant_status;


//Clocks
unsigned long clock_record = 0; // The clock for recording humidity
const int INTERVAL1 = 2000; // milliseconds between each recroding


//Funtions

//Universal clock
bool Universal_clock(String type) {
  if (type == "record") {
    if (millis() >= clock_record) {

      clock_record = millis() + INTERVAL1;

      return true;
    }
    return false;
  }


}


//Check if the plant need to be mist
void need_mist() {
  int sum = 0;
  int average_humidity;

  //Serial.println(sum);
  for (int i = 0; i < check_points; i ++ ) {
    int d = check_digit + i;
    int humid = humidity_Data[d];
    sum = sum + humid;

    Serial.println(i);
    Serial.println(humidity_Data[d]);
    Serial.println(sum);
    //Serial.println("hey");

  }

  check_digit += check_points;

  //Serial.println("humidity checked");
  average_humidity = sum / check_points;


  if (average_humidity < ideal_humid) {
    servo.write(0);
    delay(200);
    servo.write(180);

    plant_status = "dry";

    //Serial.println(average_humidity);
    Serial.println("dry!");
  }
}







void setup() {
  pinMode(LED_PIN, OUTPUT);

  pinMode(ENABLE, OUTPUT);
  pinMode(DIRA, OUTPUT);
  pinMode(DIRB, OUTPUT);

  servo.attach(SERVO_PIN);


  Serial.begin(9600);
  
  Serial.println("DHT11 Humidity & temperature Sensor\n\n");
  delay(200);//Wait before accessing Sensor

}

void loop() {


  //Record the humidity
  if (Universal_clock("record") == true) {
    DHT.read11(dht_apin);
    humidity_Data[record_digit] = DHT.humidity ;



    Serial.println(humidity_Data[record_digit]);

    if (DHT.humidity >= 90) {
      plant_status = "wet";
      digitalWrite(ENABLE, HIGH);
      digitalWrite(DIRA, HIGH); //one way
      digitalWrite(DIRB, LOW);
    }

    if (plant_status == "wet") {
      //check if it is still wet
      if (DHT.humidity < 90) {
        plant_status = "fine";
         digitalWrite(ENABLE, LOW);
         check_digit = record_digit;
      }

    
    }

    if (plant_status == "dry") {
      //check if it is still dry
      if (DHT.humidity >= ideal_humid) {
        plant_status = "fine";
        digitalWrite(LED_PIN, LOW);
        check_digit = record_digit;
      }

      else {
        digitalWrite(LED_PIN, HIGH);
      }
    }

    if (record_digit - check_digit == 7) {
      need_mist();
    }




    record_digit += 1;



  }

  //Check if the plant is too dry in the past 2 hours




  //Serial.print("Current humidity = ");
  //Serial.print(DHT.humidity);
  //Serial.print("%  ");
  //Serial.print("temperature = ");
  //Serial.print(DHT.temperature);
  //Serial.println("C  ");

  //delay(5000);//Wait 5 seconds before accessing sensor again.

  //Fastest should be once every two seconds.

}

 

Pet Pointer

 

A lazy dog

It is always great to have the companion of pets. However, it can get hard to keep a pet sometimes for people with hearing difficulty.  For example, pets like dog and cat like to run around when it is playing outside or hide in corners in the house. Usually people locate their pet by sounds or noises their pets make. This can be difficult for deaf people.

The vertical front view of the device

The solution I came up with is a pet locator that mechanically points to the direction of the pet. It has frequency sensors around and above the circular base to recognize any sound with familiar frequency. The direction of the sound is then indicated by the hand on top, which has a “wrist” that can rotate and bend. However, the fingers do not move for the ease of making the device.

The gesture of the hand when the pet is at the right.