Overview

The Physical Pomodoro Clock is a productivity tool disguised as a laptop stand that is designed to assist the user in staying focused over longer work periods by allowing the user to set a goal for how they would like to divide their productive time between working and taking healthy breaks as well as reinforcing achieving that goal through incentivizes for getting closer to the goal and reminders for the user when they stray from their goal.

Operation

Here’s a rundown of how the Physical Pomodoro Clock works.

It’s probably best to understand how the Pomodoro system itself works, which is a very simple concept. Break up the entire time you intend to work into 30 minute blocks. Work for the first 25 minutes, then take a break to stretch and move around for the next 5 minutes. Rinse and repeat for every 30 minute block.

The Physical Pomodoro follows a similar concept. While the clock is running, the user will either be in work or break mode. It’s up to the user to let the clock know when they’re doing what, and the clock will keep track of the proportion of time they spend working or on break. When a 30 minute cycle completes, the clock will compare the user’s work-to-break ratio, measured as a percentage, to the target ratio that the user can define as they please. The closer the user’s work-to-break ratio is to the target ratio, the more tokens they are awarded with. These tokens can then be spent for a random chance that a piece of candy is pushed out from the computer stand to award the user for their productivity and encourage them to keep at it.

Interacting with the Clock

Viewable on the LCD screen, the user can utilize two menus, the main pomodoro clock menu and a ‘popup’ menu for spending tokens, the virtual currency awarded for completing work cycles.

When interacting with the clock, there are two buttons and a knob.

  • The knob’s sole purpose is to set the target ratio.
  • The red button serves the following purposes
    • pressing it allows the user to start the clock from a paused state, resume work time from break, and spend tokens
    • holding it allows the user to access the token spending screen
  • The black button serves the following purposes
    • pressing it allows the user to start their break and exit from the token spending screen
    • holding it pauses the clock and resets the current cycle’s progress

Understanding the Clock Screen
The clock state

In the bottom right of the clock screen, there’s an indicator of whether the clock is running or not. If it is running, a “>” symbol will be shown and the clock time and progress indicators will be changing. If it is not running and thus in its paused state, a “||” symbol will appear. The clock as well as any progress on the current cycle will be set to 0 when it is paused.

Another useful indicator is the work vs. break indicator in the third column. If the current set mode is break, then there will be  a “B” in the top row of the third column. Otherwise the mode is work, so there is a “W” in the bottom row of the third column.

The ratio system

The clock menu displays a graphical representation of the user’s work-to-break ratio in the left-most column and the target ratio in the column to the right of it. The left side of each column shows work percentage while the right side shows the break percentage. The work time percentages are also shown numerically in the top row, where the first percentage, which has a checkered ‘target’ icon before it is the target ratio, while the user’s ratio is at the far right, which seems to be updating to a mid-20 percent at the moment the photo was taken, thus the furthest left column is around 1/4 of the way full on its left side, but there’s nothing on its right side since no break time has been taken in that cycle.  The target ratio at 94% is represented by a completely filled left half in the second column with not enough break time to be shown in the right half.

The ratio system has been scaled such that it has a minimum of 50% and a maximum of 95%.

Measures of overall progress

Note: The cycle and token values are stored on the device and persist even after powering the device off and on.

The remaining two unexplained parts are the numbers followed by “cy” and the coin symbol followed by a “x” and a number. The first set of numbers is an indicator of purely how many cycles have been completed, irregardless of ratios achieved or not achieved. The second number is the number of total tokens the user has available to spend on the token menu.

The token Menu

I’d say this menu is pretty self-explanatory. If you press the red button, it’ll deduct 10 tokens and do some probability math to decide whether it gives you a prize or not.

The Notification System

The clock can notify the user through the use of a vibrating motor which is mounted in the lower right corner of the above photo, as well as flashing an LED in the bottom left corner of this photo.

When the clock is turned on, put into play, or completes a cycle, it’ll do one short buzz.

When the clock wants to remind the user to take a break or get back to work because they’re too far from their target ratio, it repeatedly makes short buzzes until the user complies or the cycle completes.

When the user wins a prize from spending their tokens, a long buzz will sound from the clock to commemorate the moment.

Videos

Video of switching menus

Video of spending tokens for reward

Process and Review

This project began as one of several proposed ideas for something that could be useful in my life. The initial idea was a vague design based on a Pomodoro clock  with very minimal input, just 2 buttons and a potentiometer.

However, a mess of wires, electronic components, and boards aren’t exactly aesthetically appealing, so as the electronic circuit was nearly finished, I had to look for a suitable structure to mount the simple clock. As the goal of the project was to simply design something useful, I figured I’d mount the Pomodoro clock somewhere it would be most effective and give it a second purpose. A major reason I got a Pomodoro clock on my phone is how long I spend in front of a computer without getting up to move, which can be unhealthy. So I figured a computer stand could be a practical means to elevate my computer screen to a more comfortable level while being right where I need it most.

A picture of the WIP CAD design for the stand from the Fusion 360 software can be seen below.

Works great as a laptop stand, 1/4 in. plywood is pretty sturdy

In the later hours of working on this project, I had wavered on whether to keep or discard a reward system, where a virtual currency could be earned by following the Pomodoro clock closely. In the end, I decided I wanted to implement one and did a somewhat rushed job in adding a servo and some other parts to randomly reward the user with a candy when they spent the currency. Unfortunately, the candy I used did not fit very well through the hole it was designed to leave through, and the servo struggled to push the candy very far against friction. Perhaps if I had a different candy or designed the case to be larger to allow for more options, then it would’ve gone more smoothly.

 

A rainbow tangle of wires, normally kept hidden underneath the top panel of the laptop stand. The servo, candy, and, and part of a popsicle stick ramp can be seen on the right side, adjacent to the polycarbonate ‘window’.

Although much of the code ran smoothly, there were a few hitches. I had also uploaded a piece of code I had slightly modified but didn’t test thoroughly, thinking that there was no major affect. However, the notification system went off even at times it shouldn’t be during the demo, which made it rather difficult to demo the timer aspect due to the incessant buzzing from the vibration motor. But there was a very curious bug earlier in development when Japanese characters would appear, which was due to incorrect mapping to certain memory addresses of the LCD screen since the LCD screen came with Japanese and English characters by default that could be displayed on the screen. The intended goal was a few custom character slots that I had been writing the custom ratio indicator symbols to.

Discussion

Response

“It would be awesome if things are drilled onto the board instead of being taped.”

– I admit that the tape did not do as good a job as I hoped to hold things together. If I had more confidence in where things should be located, I may have gone ahead and glued it at least. Drilling wouldn’t be too bad for some components, but the breadboards I was using didn’t seem to be easily mounted in that manner, so I’d probably still avoid drilling.

“I wish we got a little more idea on how the timer itself worked.”

– And I wished I had given you a better idea about how it worked. That was just a flaw in my presentation skills after panicking from the notification system incorrectly going off while the timer was running. There was actually a lot of other functionality I wasn’t able to show, including a graphical representation of the ratios in the left two columns of the LCD, a break/work indicator column, and a play/plause indicator in the bottom right. But the basics are about the same as the classic Pomodoro clock. The ratio potentiometer adjusts a target goal to break out of the hard set 25-5 minute ratio, instead letting the user define the proportion of work they want to get done with each cycle. With the system of letting the user indicate whether they’re on break or working, my system gives the user more freedom in defining when they work and when they take a break, so the break doesn’t have to be at the end of the cycle. When the cycle completes, a productivity ratio closer to the self-chosen target ratio would award more tokens, which seem to be an aspect that received much praise, despite the system of reward delivery being close to non-functional.

“The circular acrylic window for the rectangular LCD display doesn’t make to much sense to me”

– I thought it would look cool to be able to see inside at the electronics underneath. Unfortunately, there’s not much in terms of LEDs or anything else to see, so it probably would’ve been better to stick to a square hole that would’ve made mounting it infinitely easier. So I certainly agree that the acrylic window was fairly unnecessary, though I think it does kind of look cool.

“Does it only hold one piece of candy at a time?” + “Try to use a smaller and lighter candy”

– Sadly yes, it holds only one piece of candy, since I was rather constrained by the space within the computer stand. If I was feeling more creative, I might play it off as a feature to encourage the user to get up and refill the candy each time instead of sit in front of the computer the whole time. If I were to do it again, especially if I wanted to hold more than one candy, I’d probably use a smaller and lighter candy that came in a shape that could easily roll, like a tic-tac, though I would have had to go buy the candy then.

“Nice popsicle stick” – Indeed.

Critique

After having completed the project, I can look back and say that there were at least two major problems. The first was creating a design that suffered too much from feature bloat. The design would’ve greatly benefited from retaining the simplicity of a basic Pomodoro clock to go with its simple controls. The whole reward system was an entertaining concept, but it appears to be detrimental to add unnecessary features that take away from the intentionally simple design. It probably would’ve been better to have used the second menu option for more in depth clock configurations.

The other problem is a general lack of planning for a number of issues. These included somewhat minor issues: a lack of space for the electronics to easily fit beneath the top board of the stand, not cutting certain openings a little smaller to account for the width of the laser, not laser cutting a slot for the small ‘tab’ in the potentiometer to fit into, as well as somewhat major ones: lack of a plan for mounting the devices and unclear aesthetic goals for the project. While the initial idea was feature-rich, it was lacking in implementation specifics.

Conclusion

This project has been a very valuable learning experience. The many issues that I didn’t anticipate this time around are something I can learn from and hopefully address when I can anticipate them in future projects and avoid making the same mistakes this time around. I also had to think outside the lasercut box for this project, opting for a laptop stand instead of a generic box. So at least, even if the Pomodoro clock doesn’t alway work, I can rely on it as a fine laptop stand. If I had another shot at it, I’d see if I could make it bigger with clear acrylic and set up a better candy delivery system, probably with a steeper ramp and a servo controlled valve that delivered smaller and rounder candies. The clear acrylic would let you could see the electronics working inside the stand as well as the candies moving around, which I think would be cool, plus the whole stand could pulse with the color of the internal LED when the user was being notified. I’d also see if I could fix the bug where the notification system goes off all the time when the intended behavior is to notify the user when they should end their break. At least it reminds the user to start their breaks and when a cycle ends, just like a classic Pomodoro clock.

Technical

Schematic

Code

/*
Physical Pomodoro Clock

Description: Code for taking input from two buttons and a potentiometer to control a pomodoro clock with a notification and reward functionality

Pin Mapping
Input
Pin |  Input
2      Button 1 (RED)
3      Button 2 (BLACK)
A0     Potentiometer
A5     Random Noise Pin

Output 
Pin | Output
5     Notification system (Vibration Motor + LED)
7     Servo Pin

//Referenced resources
   LCD Screen code contains snippets and references to code written by Robert Zacharias at Carnegie Mellon University, rzach@cmu.edu
   released by the author to the public domain, November 2018
//https://forum.arduino.cc/index.php?topic=418257.0
//https://forum.arduino.cc/index.php?topic=215062.0
//https://learn.robotgeek.com/28-robotgeek-getting-started-guides/61-robotgeek-i2c-lcd-library#customChar
//https://learn.robotgeek.com/getting-started/59-lcd-special-characters.html
*/




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

/* Create an LCD display object called "screen" with I2C address 0x27
   which is 16 columns wide and 2 rows tall. You can use any name you'd like. */
LiquidCrystal_I2C screen(0x27, 16, 2);

Servo gate;

//Pins
const int BUTTON1_PIN = 2;
const int BUTTON2_PIN = 3;
const int RATIO_PIN = A0;
const int NOTIFIER_PIN = 5;
const int REWARD_PIN = 7;
const int RANDOM_PIN = A5; //Intentionally unconnected to be a source of random noise
//***


//***Utilities***

//Mode
bool isPomo = true;

//Notifier
bool canNotify = false;
unsigned long notifyTimer = 0;
unsigned long endTimer = 0;
bool flop = false;
int flopFreq = 0;
int duration = 0;

//Masks
const int B1P_MASK = B0001;
const int B1H_MASK = B0010;
const int B2P_MASK = B0100;
const int B2H_MASK = B1000;

//Time constants
const int delayRate = 100; //Refresh rate in ms for the whole system
const int renderRate = 800; //rate of rendering

//Max Pot Value
const int MAX_POT = 1023; //Represents the highest potentiometer output

//Buttons
//tracks how many delays
int counter = 0;
//The minimum time in ms necessary for a press to count as any press
const int minPressTime = 300;
//The minimum time in ms necessary for a hold to register
const int minHoldTime = 2000;

//int[] for button hold times
unsigned int button_Times[2];

//Reward Gate parameters
const int gateTime = 5000; //2 seconds to grab the candy if won 
const int openAngle = 5;
const int closedAngle = 90;


//***Pomodoro
unsigned long referenceTime = 0; //Whenever the clock is 'paused', this number is no longer useful and must be reset on resuming pomodoro. Thus a pause resets progress on that pomodoro.
//For higher accuracy on progress time
unsigned long lastTick = 0;
unsigned int elapsed = 0; //Once elapsed in seconds reaches the equivalent of 30 minutes, a cycle will be updated, and 'prizes' awarded. Then it will be reset
unsigned long progress = 0; //productive time in milliseconds
unsigned int cycles = 0;
const unsigned int CYCLE_CONSTANT = 60; //number of seconds in 30 minute pomodoro
//const byte pRatios[6] = {1, 2, 4, 5, 9, 14}; //productivity ratio numbers to 1
float ratio = 0.83; //number of productive seconds
//Ratio limits. Max break is 15 min, while min break is 2 min per 30 minute pomodoro
const float minRatio = 0.5;
const float maxRatio = 0.95;


//ratio and progress percentages respectively
byte rPercent = 0;
byte pPercent = 0;

bool isPaused = true;
bool isBreak = false;

//byte columns
byte left_c = B11000;
byte right_c = B00011;

//symbol like > to indicate clock is running #Not Used
byte playSign[] = {
  B10000,
  B11000,
  B11100,
  B11111,
  B11100,
  B11000,
  B10000,
  B00000
}; // (note the extra row of zeros at the bottom)

//symbol like || to indicate clock is paused
byte pauseSign[] = {
  B10001,
  B11011,
  B11011,
  B11011,
  B11011,
  B10001,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)
//***

//Not Used
byte upSign[] = {
  B00000,
  B00100,
  B01110,
  B11111,
  B00000,
  B00000,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

//Not Used
byte downSign[] = {
  B00000,
  B00000,
  B00000,
  B11111,
  B01110,
  B00100,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

//Resembles a coin
byte rewardSign[] = {
  B00100,
  B01010,
  B11101,
  B11101,
  B11101,
  B01110,
  B00100,
  B00000
}; // (note the extra row of zeros at the bottom)

//Resembles a target
byte targetSign[] = {
  B00000,
  B00100,
  B01010,
  B10101,
  B01010,
  B00100,
  B00000,
  B00000
}; // (note the extra row of zeros at the bottom)

/*
  //This is adjustable and determined by code
  byte targetSign[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
  }; // (note the extra row of zeros at the bottom)

  //This is determined by code and updated as progress changes
  byte progressSign[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
  }; // (note the extra row of zeros at the bottom)
*/

//***Reward System
unsigned int tokens = 100; //Total 'currency'
unsigned int spinPrice = 10; //Price to attempt to draw a prize
bool trySpin = false; //Indicates whether an attempt to draw a prize is made
const int randomThreshold = 40; //Percent chance that prize is won 
//***


struct saveData {
  unsigned int savedCycles;
  unsigned int savedTokens;
};


void setup() {
  Serial.begin(9600);

  saveData lastSave;
  //Assumed that EEPROM will read and write from first address
  EEPROM.get(0,lastSave);
  Serial.println(lastSave.savedTokens);
  cycles = (lastSave.savedCycles!=65535)?lastSave.savedCycles:0;
  tokens = (lastSave.savedTokens!=65535)?lastSave.savedTokens:0;
  
  //Random Seed setup
  randomSeed(analogRead(RANDOM_PIN));

  //Setup Pins
  //Buttons
  pinMode(BUTTON1_PIN, INPUT);
  pinMode(BUTTON2_PIN, INPUT);
  //Potentiometer
  pinMode(RATIO_PIN, INPUT);
  //Audio-visual Notification
  pinMode(NOTIFIER_PIN, OUTPUT);
  gate.attach(REWARD_PIN);

  //turn gate to closed position initially
  gate.write(closedAngle);
  
  // initialize the screen (only need to do this once)
  screen.init();

  // turn on the backlight to start
  screen.backlight();

  //Loads symbols into memory
  screen.createChar(0, playSign);
  screen.createChar(1, pauseSign);
  //2 is reserved for both up and down signs and dynamically set later
  screen.createChar(3, rewardSign);
  screen.createChar(4, targetSign);
  // set cursor to home position, i.e. the upper left corner
  //screen.home();

  screen.clear();
  screen.home();
  //screen.print("b1 = ");
  //screen.setCursor(0, 1);
  //screen.print("b2 = ");
  digitalWrite(5, HIGH);
  delay(1000);
  digitalWrite(5, LOW);
  //tokens = 100;

}

void loop() {
  
  //screen.clear();
  delay(delayRate);

  //screen.setCursor(5, 0);
  //screen.print(digitalRead(2));

  //screen.setCursor(5, 1);
  //screen.print(digitalRead(3));
  //Serial.println(analogRead(RATIO_PIN));

  byte buttonData = buttonInput();
  //Button Data: ####
  //Last # (2^0): B1 Press
  //Left1# (2^1): B1 Hold
  //Left3# (2^2): B3 Press
  //Left4# (2^3): B4 Hold
  //A short hold has short hold logic
  //A long hold has long hold logic

  //if it isn't paused, process data from last loop
  if (!isPaused)pomodoro_tick();

  //Divides logic based on whether the mode is Pomodoro or not
  if (isPomo) {
    //updates various internal variables for this display mode
    pomodoro_logic();
    //Render to screen step
    if (counter > renderRate)renderPomo();

    //Button press logic
    //If the 1st button has been pressed and released, ***PLAY***
    if ((buttonData & B1P_MASK) != 0) { 
      //start/resume functionality
      //Start from paused
      if (isPaused) {
        isPaused = false;
        //Re-syncs the reference time
        referenceTime = millis();
        //syncs up the lasttick initially
        lastTick = millis();
        //enables notifies
        enableNotify();
      }

      //Resumes from break
      if (isBreak) isBreak = false;

    }
    //If the 1st button has been held, ***SHOP***
    if ((buttonData & B1H_MASK) != 0) { 
      //Switch menus
      isPomo = !isPomo;
      //Limits clear commands to when screen changes
      screen.clear();
    }

    //If the 2nd button has been pressed, Mark Break  ***BREAK***
    if ((buttonData & B2P_MASK) != 0) {
      isBreak = true;
    }
    //If the 2nd button has been held, Mark Pause/End ***PAUSE***
    if ((buttonData & B2H_MASK) != 0) {
      isPaused = true;
      //reset all current progress and time, this way there won't be any surprises when resyncing resets progress
      progress = 0;
      elapsed = 0;
      //Disable notifies
      disableNotify();
    }




  }
  else {
    //shop Logic, only relevant when in shop, so does not need to be in main loop body
    shopLogic();
    //Render Entertainment
    if (counter > renderRate)renderShop();

    //press button 1
    if ((buttonData & B1P_MASK) != 0) {
      //the spend your token sort of fun stuff
      trySpin = true;
    }
    //press button 2
    if ((buttonData & B2P_MASK) != 0) {
      //returns to main screen
      isPomo = !isPomo;
      //Limits clear commands to when screen changes
      screen.clear();
    }


  }



  //Ensures that counter always increments, so refresh doesn't get stuck at menu changes
  counter += delayRate;
}

//Relevant Logic for the Shop
void shopLogic() {
//Spin button pressed
 if (trySpin) {
    //Tokens are charged
    if (tokens >= spinPrice) {
      tokens -= spinPrice;
      generateReward();
      updateSave();
    }
    else {
      //show inssuficient funds
      //Serial.println("fail");
      screen.clear();
      screen.home();
      screen.print("Insufficient ");
      screen.write(3);
    }
    trySpin = false;
  }
  
}
void renderShop() {
  counter = 0;
  //screen.clear();
  screen.home();
  
  //Default Screen
  
  //Top Row
  screen.print("Token Spin | Own");

  //Bottom Row
  screen.setCursor(0, 1);
  screen.print("Use ");
  screen.write(3);
  screen.print("x" + (String)spinPrice);
  screen.setCursor(11,1);
  screen.print("|");
  screen.write(3);
  screen.print("x" + (String)tokens);

}
//to be implemented
void generateReward() 
{
  
  screen.clear();
  screen.home();
  int randNumb = random(100); //Generates a random number from 0 to 99 (100 possible numbers)
  if(randNumb<randomThreshold){ //If the number is less than the threshold, or percent chance to win, then the user wins a prize
    gate.write(openAngle); //Opens the prize gate
    screen.print("You Win a Prize!");
    digitalWrite(NOTIFIER_PIN, HIGH); //Some other indications of winning
    delay(gateTime); //Time to claim prize
    gate.write(closedAngle); //Closes the prize gate
    digitalWrite(NOTIFIER_PIN, LOW); //Silences the notification
  }
  else{
    screen.print("...Try again?"); //A somewhat encouraging message in case of loss
    delay(1000);
  }
  
}


//byte Cells[2][8] = {}; //fill rows 0-6 of 1D arrays
//byte Cell1[8] = {};
//byte cell2[8] = {};

//print double column (each cell has 7 rows)
void RatioPercent(int column, float r1, float r2, byte slot) {
  //These individual byte arrays may be converted to a 2D array
  //byte topCell[8]; //fill rows 0-6
  //byte lowCell[8]; //fill rows 0-6

  int divisions = 7;
  int cells = 2;

  //starts at the bottom of the cell, row 1
  for (int j = cells - 1; j >= 0; j--) {
    //j = cell index, therefore
    //j is inverse for left
    //j is direct for right
    byte temp[8] = {};
    for (int i = divisions - 1; i >= 0; i--) {
      //i begins at the bottom rows, at index 6

      //left: the relative number of rows that should be lit : Right (cells begin from the bottom, as do the row writing, thus thye have to be subtracted)
      if (r1 * divisions * cells > (cells - j - 1)*divisions + (divisions - i)) {
        //Cells[j][i] = Cells[j][i]|left_c;
        temp[i] = B11000;
      }
      //
      //At the bottom ,it would be all previous cells plus all rows of last cell (j*divisions) + i rows
      if (r2 * divisions * cells > (j)*divisions + i + 1) {
        //Cells[j][i] = Cells[j][i]|right_c;
        temp[i] = temp[i] | B00011;
      }

    }
    screen.createChar(slot + j, temp);
    //Print current cell
    screen.setCursor(column, j);
    screen.write(slot + j);
  }
}


//Renders the screen for the pomo mode
void renderPomo() {
  //Resets the counter for screen refresh
  counter = 0;

  //clear the screen
  //screen.clear();
  //Personal Progress/ratio (column 0, both rows)
  RatioPercent(0, pPercent / 100.0, (float)elapsed / CYCLE_CONSTANT - pPercent / 100.0, 5);

  //Target Progress/ratio (column 1, both rows)
  RatioPercent(1, rPercent / 100.0, 1 - rPercent / 100.0, 7);

  //Break or work (column 2, both rows)
  screen.setCursor(2, 0); //isBreak is true (top)
  screen.print((isBreak) ? ("B") : (" ")); //Prints B for break on top or empty for not break
  screen.setCursor(2, 1); //isBreak is false (bottom)
  screen.print((isBreak) ? (" ") : ("W")); //Prints B W for work on bottom or empty for on break
  //target percent (column 3-6, row 0)
  char char_buffer [6]; // a few bytes larger than your intended line
  sprintf (char_buffer, "%02d", rPercent);
  screen.setCursor(3, 0);
  screen.write (4); //target sign, index 3
  screen.print (char_buffer); // index 4,5
  screen.write (37); //percent symbol, index 6

  //# of cycles (column 4-7, row 1)
  screen.setCursor(4, 1);
  //screen.print(cycles);
  sprintf (char_buffer, "%02d", cycles); //number of cycles. May go into the 10s, but assumed that using this for 100 cycles or 3000 continuous minutes is unlikely
  screen.print(char_buffer);
  screen.setCursor(6, 1);
  screen.print("cy"); //for cycles

  //Prints time string (column 7-11, row 0)
  //char char_buffer2[6];
  sprintf (char_buffer, "%02d%s%02d", elapsed / 60, ":", elapsed % 60); // send data to the buffer
  screen.setCursor(7, 0);
  screen.print(char_buffer);

  //print reward string (column 9-13or14, row 1)
  screen.setCursor(9, 1);
  screen.write(3); //reward sign, index 8
  sprintf (char_buffer, "%03d", tokens);
  screen.print("x");
  screen.print(char_buffer);

  //indicator of current percent (c12-15, r0)
  //Writes percentage
  screen.setCursor(12, 0);
  if(pPercent<100){ //to avoid three digits pushing the line off the screen
    sprintf (char_buffer, "%3d", pPercent);
    screen.print (char_buffer); // index 4,5
  }
  screen.write (37); //percent symbol, index 6
  
  /*
  screen.setCursor(12, 0);
  //print indicator of more work or more rest
  //threshold is hard set as 5 currently, can be serialized as a const variable if necessary
  if (pPercent - rPercent > 5) {
    screen.createChar(2, downSign);
    screen.write(2);
  }
  else if (rPercent - pPercent > 5) {
    screen.createChar(2, upSign);
    screen.write(2);
  }
  else screen.print(" ");
  */
  
  //indicator of pause or play status
  screen.setCursor(15, 1);
  screen.write((isPaused) ? (1) : (62)); //if paused, print pause symbol, if playing, use > for play symbol (62).
}

//Pomodoro Logic to keep variables updated while clock functionality is in play
void pomodoro_tick() {
  //temp long
  //moves progress if not on break
  elapsed = (unsigned int)((unsigned long)(millis() - referenceTime) / (unsigned long)1000);
  if (!isBreak)progress += millis() - lastTick;
  lastTick = millis();
  
  //Loops pomodoro once time reaches limit
  if (elapsed > CYCLE_CONSTANT) {
    elapsed = 0;
    progress = 0;
    cycles += 1;
    calcReward();
    referenceTime = millis();
    updateSave();
  }

  //Reminder Logic
  //Operate on the NOTIFIER_PIN
  
  //Long LED and vibration for when cycle restarts
  if(elapsed == 0){ //an indicator that a cycle restart occurred
      
      startNotify(1000, 0);
  }
  
  //Short pulses when break should begin
  if((!isBreak)&&(pPercent-rPercent>5)){
    if(duration==0)startNotify(1000,1);
  }
  
  //Short pulses when work should resume
  if((isBreak)&&((int)((float)(elapsed*100)/CYCLE_CONSTANT-pPercent)-(100-rPercent)>5)){
    if(duration==0)startNotify(1000,1);
  }


  if(canNotify)writeNotify();
  

}

//Relevant logic that occurs in pomodoro screen mode but not related to its ticking (clock) functionality
void pomodoro_logic() {
  
  ratio = analogRead(RATIO_PIN)*(maxRatio - minRatio)/MAX_POT+minRatio; //Sets the ratio proportional to input potentiometer signal within the range between min and max ratio
  
  rPercent = (int)(ratio * 100);
  pPercent = ((int)(progress / (unsigned long)10) / (CYCLE_CONSTANT));
  
  
  
}

//Determines Reward for completing a pomodoro
void calcReward() {
  //Uses rPercent and pPercent to determine accuracy thresholds
  if(abs(pPercent-rPercent)<10)tokens += 10;
  else if(abs(pPercent-rPercent)<20)tokens+=8;
  else if(abs(pPercent-rPercent)<30)tokens+=6;
  else tokens+=4;
}

void startNotify(int totalTime, int flopfrq){
   notifyTimer = millis();
   endTimer = notifyTimer + totalTime;
   flop = false;
   flopFreq = flopfrq;
   duration = totalTime;
   digitalWrite(NOTIFIER_PIN, HIGH);
}

//Determines when and how to end the notify
void writeNotify(){
  Serial.println("Write notify");
  //Checks if there's a timer in progress, i.e. duration has been set.
  if(duration>0){
    //if the timer time has reached the flop time (the entire duration if 0 flopFreq)
    if(notifyTimer+(duration/(flopFreq+1))<=millis()){ 
        Serial.println("writing");     
        digitalWrite(NOTIFIER_PIN, flop?HIGH:LOW);
        flop = !flop;
        notifyTimer = millis();
        
    }
    //Deactivates timer if the time has been reached
    if(endTimer<=notifyTimer){
      duration = 0;
      digitalWrite(NOTIFIER_PIN, LOW);
    }
  }
}

//Allows notify system to go
void enableNotify(){
  canNotify = true;
}
//Prevents notify system from going off
void disableNotify(){
  digitalWrite(NOTIFIER_PIN, LOW);
  canNotify = false;
  duration = 0;
}
//Commits the persistent data to storage
void updateSave(){
  saveData newSave = {
    cycles,
    tokens
  };
  //Assumed same address
  EEPROM.put(0,newSave);
  
}

byte buttonInput() {
  //Register Button Hold Times
  //For that button, detect if it is held or released.
  //If the array elements are greater than 0, then that element has been held earlier.
  //Minimum time registered as any hold to avoid any flickering
  byte input = 0;

  //button 1 logic
  if (digitalRead(BUTTON1_PIN)) {
    //Determines if the signal duration has entered the hold range
    if (button_Times[0] >= minHoldTime)input = input | B1H_MASK;
    //While held, the button time will continue to increment
    button_Times[0] += delayRate;
  }
  else {
    //if the button hold duration is long enough to count as an intentional signal but not a hold, then it must be a press signal
    if (button_Times[0] >= minPressTime && button_Times[0] < minHoldTime)input = input | B1P_MASK;
    //Since the button has been 'released' or is currently not pressed, time is reset to 0.
    button_Times[0] = 0;
  }

  //Same logic as above but for button 2
  if (digitalRead(BUTTON2_PIN)) {
    //Determines if the signal duration has entered the hold range
    if (button_Times[1] >= minHoldTime)input = input | B2H_MASK;
    //While held, the button time will continue to increment
    button_Times[1] += delayRate;
  }
  else {
    //if the button hold duration is long enough to count as an intentional signal but not a hold, then it must be a press signal
    if (button_Times[1] >= minPressTime && button_Times[1] < minHoldTime)input = input | B2P_MASK;
    //Since the button has been 'released' or is currently not pressed, time is reset to 0.
    button_Times[1] = 0;
  }
  return input;
}