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
-Working Video
Note: Demo time in this video is sped up to 10x speed so the full functionality can be shown.
-Detail Photos
-Use Photos
Process Images and Review
-Process Images
-Review / Decision Points
1. The Candy Shoot
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
Block Diagram
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(); } }