By: Jonathan Lindstrom

What is it?

The Study Buddy helps keeps you focused while studying and gives you a sweet reward after intense study sessions.

Discussion

For my project, I feel many things went well but there was also a lot that could be improved upon. Starting with the positive critiques I was happy to hear that “The interface was excellent. Super intuitive to use. And so well contained in the box!” That was one of my primary focuses in my project. I wanted to create an interface that anybody could pick up and be able to navigate. Creating the UI was my favorite part of the project. Working with it I learned a lot about what makes an effective UI. In the future, I think I would code it a little differently to save some memory on the Arduino but overall I was very proud of it.

For negative critiques many of the comments regarded the LED indicator, my classmates mentioned “I should possibly add more LEDs to signify if the user was in work time or break time, or having multiple colors of LEDs to make the design a bit more intuitive. “ I agree and it is something that I wish I had properly implemented. I was planning to have different colors for work and break but I simply ran out of time and it wasn’t a priority for me.

There were a couple of things I wish I had done differently or better. I wish I had implemented the different colored LEDs as I feel it would have made the light indicator more effective. I also would have liked to integrate the candy dispensing mechanism into the main body of the project as opposed to building it on the side.

I do not think I will build a second iteration but these would be the next steps if I were to build a second interaction of this project. Furthermore, when I performed some user tests I realized that the fact that a joystick is a button isn’t necessarily intuitive. I had users attempting to press the home button to select options in my menu. This led me to glue on little signs near my buttons to make it more intuitive but if there were a second iteration I would laser cut these messages.

Overall I was pleased with how my project turned out. It functions largely how I intended it to and I had a lot of fun designing the UI and laser cutting the housing for the electronics.

Images & Videos

-Final Image

The final images of my Study Buddy.

-Working Video

Note: Demo time in this video is sped up to 10x speed so the full functionality can be shown.

-Detail Photos

The LED on the top of the box serves two functions. Firstly it is an indicator that the device should be on and working and it also blinks when the user has paused their study time or when it is transitioning between work and break time.

The joystick is used to navigate the user interface. The code is constantly checking the position of the joystick as well as when it is pressed so it can perform the functions the user desires.

Candy Dispenser built out of a servo and cardboard. It is attached to the body using hot glue and balsa wood and the servo wires are fed in through a small hole.

-Use Photos

The screen for changing values. Can scroll up or down to increase/decrease the total time spent studying. There are the same screens for work time and break time.

The screen for studying. Counts you down and alternates between your work and break times until the total time is done.

Once you are done the screen displays “Great Study Session!” and dispenses the candy.

Process Images and Review

-Process Images

These were some of the first drawings of how I wanted the UI to look. Some changes were made as I went but you can see how these influenced the interface in the study buddy.

The start of my menu. Another section was added later, Demo Mode, but I started by just including the main function and directions pages.

This was the hardest section to figure out. I had to create new pages for each of the three values and be able to change and save them returning to this screen.

-Review / Decision Points

1. The Candy Shoot

This is a preliminary sketch. The original Plan was to incorporate the candy shoot and servo into the main box.

This was replaced with an external servo and candy shoot after I struggled to properly incorporate a working design into the main body.

2. How to keep track of time

The original plan was to keep track of time with a Real Time Clock Module. This module can be seen in some of my process photos. However I realized this was difficult to implement and I did not need to keep track of the actual time, just how long a minute is. So I opted to take this out and keep track of my time using the millis() function. 1000 milliseconds = 1 second so I could use millis to keep track of the seconds and count down. I found it much easier to implement and overall simpler for the purposes of my project.

Schematic & Block Diagram

Schematic

Electrical schematic of the Study Buddy.

Block Diagram

Block Diagram for the different inputs/outputs in the Study Buddy.

Code

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

///----THE STUDY BUDDY-----///
//----Jonathan Lindstrom---//

//This project is intended to help an individual study more
//Productively. It implements an intuitive UI to help the
//User set the total time they want to study for, the time for working, and the time for breaks
//The default is the pomodoro method which is 25 minutes of work with a 5 minute break.
//The user is also able to pause at any point for a break by pressing the joystick.
//Once total time is up it triggers a servo motor releasing some candy for a reward.

//-----PIN MAPPING--------//
//| Joy X Axis -->    A0   |
//| Joy X Axis -->    A1   |
//| Joy Button -->    2    |
//| Servo      -->    5    |
//| Green LED  -->    6    |
//| LCD Pins   --> SDA/SCL |

//-----VARIABLES-------///

//----CONSTANTS----//
Servo gaugeMotor;
const int QUARTERWAIT = 250;
const int VRX = A0;
const int VRY = A1;
const int SW = 2;
const int HOMEBUT = 9;
const int GREEN = 6;
const int SERVO = 5;

//--MAIN FUNCTIONS---//
String joyPos = ""; //Gets the joy position as "UP, DOWN, LEFT, OR RIGHT" Depending on joystick position
String screenState = "HOME"; //HOME, DIRECTIONS, OPTIONS, TOTAL, STUDY, BREAK, STUDYING, Used to tell the arduino which screen to display


//-----NUMS TO REPRESENT THE DIFFERENT SCREENS----//
int screenHome = 0;//0-1 for 2 options
int screenOptions = 0; //0-3 for 4 options
int screenDirections = 0; //0-3 for 4 options
boolean screenStudying = false;

//----INPUT VARIABLES-----??
int joyOnlyOnce = 0;//Makes sure that numbers can only change only once everytime the joystick is pressed
int joyButOnce = 0; //Makes sure joy button is only pressed once
int buttonState = 0; //State of JoyStick Button
int homeButtonState = 0; //Keeps Track of Button State of Home Button

//---VARIABLES FOR TIME/TIME CHECKS----//
int totalTime = 60; //Total Time with Studys AND Breaks
int studyTime = 25; //Total Time of each sec studying
int breakTime = 5; //Total Time of each break
boolean place = true; //Placeholder booleans that allow for
boolean pause = false; //Keeping make sure specific parts of the code run only once
boolean light = true;
int check = 1;
int totalPlace = totalTime; //Make a copy of totalTime that can be altered
long mins = - 1;
long sec = 60;
int millPerSec = 1000; //Used to change for Demo Mode

//---LCD VARIABLES----//
unsigned long quarterTimer = 0;
LiquidCrystal_I2C screen(0x27, 20, 4); //Initilize screen



void setup() {
  gaugeMotor.attach(SERVO);
  Serial.begin(9600);
  screen.init();
  screen.backlight();

  pinMode(VRX, INPUT);
  pinMode(VRY, INPUT);
  pinMode(SW, INPUT_PULLUP);
  pinMode(HOMEBUT, INPUT);
  pinMode(GREEN, OUTPUT);
  digitalWrite(GREEN, HIGH);
}

void loop() {
  millis(); //Start Millis
  joyPosition(); //Call JoyStick Functions in loops so the values are always changing with accordance to joystick position and Screen
  joyRight();
  joyLeft();
  joyUp();
  joyDown();
  joyButtonPressed(); //Checks all cases in which pressing the Joystick will cause a change in screen
  homeButtonPressed(); //Checks if at any point the home button is pressed

  if (pause) { //Makes light blink when in pause
    blinkLight();
  }
  if (!pause) { //LED solid green otherwise when the device is on
    digitalWrite(GREEN, LOW);
  }

  //LCD will change depending on the screenState and display the right screen accordingly. This is constantly changing.
  if (screenState == "HOME") {
    homeScreen();
    resetVals();
  }

  if (screenState == "DIRECTIONS") {
    printDirections();
  }

  if (screenState == "OPTIONS") {
    optionsScreen();
  }

  if (screenState == "TOTAL") {
    totaltimeScreen();
  }

  if (screenState == "STUDY") {
    studytimeScreen();
  }

  if (screenState == "BREAK") {
    breaktimeScreen();
  }
  if (screenState == "STUDYING") {
    studyingScreen();
    timeCheck();//Keep Track of Mins and Secs with millis(). Use when study screen is brought up;
  }
  if (screenState == "COMPLETE") {
    completeScreen();
  }

}



//==========JOYSTICK FUNCTIONS==========//
void joyPosition() {
  if (analogRead(VRX) > 900) {
    joyPos = "LEFT";
  }
  else if (analogRead(VRX) < 100) {
    joyPos = "RIGHT";
  }
  else if (analogRead(VRY) < 100) {
    joyPos = "DOWN";
  }
  else if (analogRead(VRY) > 900) {
    joyPos = "UP";
  }
  else {
    joyPos = "NEUTRAL";
    joyOnlyOnce = 0;
  }

}
void homeButtonPressed() { //Will go to home screen from any point in the code
  homeButtonState = digitalRead(HOMEBUT);
  if (homeButtonState) {
    screenState = "HOME";
    totalTime = 60; 
    studyTime = 25; 
    breakTime = 5; 
    millPerSec = 1000; //Resets actual mill of a second to 1000 --> Demo Mode
    pause = false;
    check = 1;
    screenDirections = 0;

    screen.clear();
  }
}
void joyButtonPressed() { //Performs all functions for the Joystick being pressed
  buttonState = digitalRead(SW);
  if (buttonState == 0) {
    if (joyButOnce == 0) {
      joyButOnce = 1;
      screen.clear();

      if (screenState == "HOME") {
        if (screenHome == 1) {
          screenState = "DIRECTIONS";
        }
        if (screenHome == 0) {
          screenState = "OPTIONS";
        }
        if (screenHome == 2) {
          demo();
        }
      }

      else if (screenState == "OPTIONS") {
        if (screenOptions == 0) {
          screenState = "STUDYING";
          setMins();
        }
        if (screenOptions == 1) {
          screenState = "TOTAL";
        }
        if (screenOptions == 2) {
          screenState = "STUDY";
        }
        if (screenOptions == 3) {
          screenState = "BREAK";
        }
      }

      else if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
        screenState = "OPTIONS";
      }

      else if (screenState == "STUDYING") {
        pause = !pause;
      }


    }
  }

  if (buttonState == 1) {
    joyButOnce = 0;
  }
}


void joyUp() { //Performs all functions and changes when the Joystick is pushed up
  if (joyPos == "UP") {
    if (screenState == "HOME") {
      if (joyOnlyOnce == 0) {
        screenHome -= 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenHome <= -1) {
          screenHome = 2;
        }
      }
    }
    if (screenState == "OPTIONS") {
      if (joyOnlyOnce == 0) {
        screenOptions -= 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenOptions <= -1) {
          screenOptions = 3;
        }
      }
    }
    if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
      if (joyOnlyOnce == 0) {
        if (screenState == "TOTAL") {
          totalTime += 5;
          totalPlace = totalTime;
        }
        if (screenState == "STUDY") {
          studyTime += 5;
        }
        if (screenState == "BREAK") {
          breakTime += 5;
        }
        screen.clear();
        joyOnlyOnce = 1;
        if (totalTime > 120) {
          totalTime = 0;
          totalPlace = totalTime;
        }
        if (studyTime > 60) {
          studyTime = 0;
        }
        if (breakTime > 30) {
          breakTime = 0;
        }
      }
    }



  }
}

void joyDown() { //Performs all functions and changes when the Joystick is pushed down
  if (joyPos == "DOWN") {
    if (screenState == "HOME") {
      if (joyOnlyOnce == 0) {
        screenHome += 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenHome >= 3) {
          screenHome = 0;
        }
      }
    }
    if (screenState == "OPTIONS") {
      if (joyOnlyOnce == 0) {
        screenOptions += 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenOptions >= 4) {
          screenOptions = 0;
        }
      }
    }

    if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
      if (joyOnlyOnce == 0) {
        if (screenState == "TOTAL") {
          totalTime -= 5;
          totalPlace = totalTime;
        }
        if (screenState == "STUDY") {
          studyTime -= 5;
        }
        if (screenState == "BREAK") {
          breakTime -= 5;
        }
        screen.clear();
        joyOnlyOnce = 1;
        if (totalTime < 0) {
          totalTime = 120;
          totalPlace = totalTime;
        }
        if (studyTime < 0) {
          studyTime = 60;

        }
        if (breakTime < 0) {
          breakTime = 30;
        }
      }
    }


  }
}

void joyRight() { ////Performs all functions and changes when the Joystick is pushed right
  if (joyPos == "RIGHT") {
    if (screenState == "DIRECTIONS") {
      if (joyOnlyOnce == 0) { //Navigate Directions
        screen.clear();
        screenDirections += 1;
        joyOnlyOnce = 1;
        if (screenDirections >= 4) {
          screenDirections = 0;
        }
      }
    }
  }
}

void joyLeft() { //Performs all functions and changes when the Joystick is pushed left
  if (joyPos == "LEFT") {
    if (screenState == "DIRECTIONS") {
      if (joyOnlyOnce == 0) { //Navigate Directions
        screen.clear();
        screenDirections -= 1;
        joyOnlyOnce = 1;
        if (screenDirections <= -1) {
          screenDirections = 3;
        }
      }
    }
  }
}



//===========SCREENS===========//

void directions1() { //Directions screen 1
  screen.setCursor(0, 0);
  screen.print("Directions(1/4):");
  screen.setCursor(0, 1);
  screen.print("-Use stick to scroll");
  screen.setCursor(0, 2);
  screen.print("through directions");
  screen.setCursor(0, 3);
  screen.print("<-- stick = joystick");
}

void directions2() { //Directions screen 2
  screen.setCursor(0, 0);
  screen.print("Directions(2/4):");
  screen.setCursor(0, 1);
  screen.print("-Press button to");
  screen.setCursor(0, 2);
  screen.print("right for home -->");
  screen.setCursor(0, 3);
  screen.print("-Press on joy for Ok");
}

void directions3() { //Directions screen 3
  screen.setCursor(0, 0);
  screen.print("Directions(3/4):");
  screen.setCursor(0, 1);
  screen.print("-Numbers");
  screen.setCursor(0, 2);
  screen.print("Up for increase");
  screen.setCursor(0, 3);
  screen.print("Down for decrease");
}

void directions4() { //directions screen 4
  screen.setCursor(0, 0);
  screen.print("Directions(4/4):");
  screen.setCursor(0, 1);
  screen.print("Ok to pause timer");
  screen.setCursor(0, 3);
  screen.print("Press Home :)");
}

void printDirections() { //ScreenState DIRECTIONS, Changes the directions page accordingly
  if (screenDirections == 0) {
    directions1();
  }
  if (screenDirections == 1) {
    directions2();
  }
  if (screenDirections == 2) {
    directions3();
  }
  if (screenDirections == 3) {
    directions4();
  }
}

void homeScreen() { //ScreenState HOME
  screen.setCursor(0, 0);
  screen.print("Home:");
  screen.setCursor(4, 1);
  screen.print("Start Studying");
  screen.setCursor(4, 2);
  screen.print("Directions");
  screen.setCursor(4, 3);
  screen.print("Demo Mode");
  if (screenHome == 0) {
    screen.setCursor(0, 1);
  }
  if (screenHome == 1) {
    screen.setCursor(0, 2);
  }
  if (screenHome == 2) {
    screen.setCursor(0, 3);
  }
  screen.print("-->");
}

void optionsScreen() { //ScreenState OPTIONS

  screen.setCursor(0, 0);
  screen.print("Options:");
  screen.setCursor(3, 1);
  screen.print("Time Total = ");
  screen.setCursor(3, 2);
  screen.print("Time Study = ");
  screen.setCursor(3, 3);
  screen.print("Time Break = ");
  screen.setCursor(15, 0);
  screen.print("OK?");

  if (screenOptions == 0) {
    screen.setCursor(12, 0);
  }
  if (screenOptions == 1) {
    screen.setCursor(0, 1);
  }
  if (screenOptions == 2) {
    screen.setCursor(0, 2);
  }

  if (screenOptions == 3) {
    screen.setCursor(0, 3);
  }
  screen.print("-->");
  screen.setCursor(16, 1);
  screen.print(totalTime);
  screen.setCursor(16, 2);
  screen.print(studyTime);
  screen.setCursor(16, 3);
  screen.print(breakTime);
}

void totaltimeScreen() { //Screen State TOTAL
  screen.setCursor(0, 0);
  screen.print("Total Time:");
  screen.setCursor(13, 0);
  screen.print(totalTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}
void studytimeScreen() { //ScreenState STUDY
  screen.setCursor(0, 0);
  screen.print("Study Time:");
  screen.setCursor(13, 0);
  screen.print(studyTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}

void breaktimeScreen() { //ScreenState BREAK
  screen.setCursor(0, 0);
  screen.print("Break Time:");
  screen.setCursor(13, 0);
  screen.print(breakTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}

void studyingScreen() {//ScreenState STUDYING
  screen.setCursor(0, 0);
  if (screenStudying) {
    screen.print("Study: "); //Changes depending on if user is studying or on break
  }
  if (!screenStudying) {
    screen.print("Break: ");
  }

  screen.setCursor(7, 0);
  screen.print(mins / 10);
  screen.setCursor(8, 0);
  screen.print(mins % 10);
  screen.setCursor(9, 0);
  screen.print(":");
  screen.setCursor(10, 0);
  screen.print(sec / 10);
  screen.setCursor(11, 0);
  screen.print(sec % 10);
  screen.setCursor(0, 1);
  screen.print("--------------------");
  screen.setCursor(0, 2);
  if (pause) {
    screen.print("Press Ok to Continue");
  }
  else if (screenStudying) {
    screen.print("Good Luck! Work Hard");
  }
  else if (!screenStudying) {
    screen.print("Enjoy Your Break!   ");
  }
  screen.setCursor(0, 3);
  screen.print("Home=Quit, Ok=Pause");


}

void completeScreen() { //Displays completed when the user finishes. Dispenses candy, ScreenState COMPLETE
  screen.setCursor(0, 0);
  screen.print("GREAT STUDY SESSION!");
  screen.setCursor(0, 2);
  screen.print("Candy Dispensing :)");
  screen.setCursor(5, 3);
  screen.print("Press Home");
  if (check == 1) { //Makes sure the candy code only runs once each COMPLETE screen
    gaugeMotor.write(0);
    delay(5000); //Delay to make sure that the candy is properly dispensed and servo reset before the user can go home or start studying again
    gaugeMotor.write(90);
    check = 0;
  }
}

//=========TIME & DEMO FUNCTIONS======//

void timeCheck() { //Keeps track of time using millis to count seconds and minutes
  static long t = 0;
  if (!pause) {
    if (millis() - t >= millPerSec) {
      t = millis();
      //1 Second Passing
      sec -= 1;
      if (sec == 0) {
        if (mins == 0 && totalPlace <= 0) {
          screen.clear();
          screenState = "COMPLETE";
        }
        mins -= 1;
        sec = 60;
      }
    }
    if (mins == -1) {
      setMins(); //Onces mins set runs out it changes it accordingly to breakTime/StudyTime
    }
  }
}

void setMins() { //resets mins alternating between break and studying when called
  if (place) {
    mins = studyTime - 1;
    place = !place;
    screenStudying = !screenStudying;
  }

  else {
    mins = breakTime - 1;
    place = !place;
    screenStudying = !screenStudying;
  }
  if (totalTime != totalPlace && totalPlace != 0) { //Only runs this after the start
    pause = true;
  }
  totalPlace -= mins + 1;

}

void resetVals() { //RESETS VALS
  mins = studyTime - 1;
  sec = 60;
  place = true;
  totalPlace = totalTime;
  screenStudying = false;
}

void demo() { //Demo mode
  millPerSec = 100;
  totalTime = 3;
  totalPlace = totalTime;
  studyTime = 1;
  breakTime = 1;
  screenState = "OPTIONS";
}

void blinkLight() { //Causes Green LED To Blink
  static int t = 0; //For alt between break/study and for pauses
  if (millis() - t >= 250) {
    if (light) {
      digitalWrite(GREEN, LOW);
      light = !light;
    }
    else if (!light) {
      digitalWrite(GREEN, HIGH);
      light = !light;
    }
    t = millis();


  }
}