Front view of the timer, without power on, and all of the switches in the off position.
This project helps me follow the pomodoro technique. The pomodoro technique is a time management method commonly used for studying that involves working in focused 25-minute intervals separated by 5-minute breaks. The box can be set with the number of cycles and will count down 25 minutes for studying before counting down 5 minutes for a break. Once the number of indicated cycles has been reached, the box opens, allowing the user to grab what was put inside.
This is a top view of the box with the lid removed. The arduino and breadboard are mounted to the box using velcro and the servo, LCD, and switches are glued in place. Additionally, pieces of wood are glued to the interior walls of the box to hold up the lid of the box.
This is the left-side view of the box which has an opening to access the cable ports on the arduino.
This is a front view of the box with the timer on. The timer counts down from 25 minutes when in the “studying mode” and counts down from 5 minutes when in the “break” mode. For both cases, the LED ring on the right side of the box lights up proportionally to the time remaining (in this case 1:16 remains, so two LEDs are off while the rest are fully lit).
This is a front view of the box when the timer has ended. The LCD and LEDs turn off while the servo rotates upward, lifting the lid from the box and allowing the user to access the contents inside.
In this video, I show how the number of cycles can be changed using the keypad and also demonstrate the switch that enables pausing and starting the timer.
In this video, the timer counts down the studying time before the LED ring is lit up to indicate that the timer has ended. After the LED ring lights up, the timer resets for the break time period (In this case the studying time is set to 2 minutes and the break time is set to 1 minute). In addition, the number of cycles decreases by 1 following the end of the studying time.
The first step in the project was coding a timer on the Arduino to display on the LCD. The video above shows the timer working alongside a switch that can pause and start the timer.
Above is the predicted layout of the box. The plan was to have an LCD screen alongside a few buttons and switches to turn the screen on and off as well as pause and start the timer. Additionally, the number of cycles was added to the timer, with a keypad to enter the number of cycles easily. The phone was included in the image to obtain a rough scale of the box as it was intended to enclose items up to the size of a phone.
The box was laser cut out of ⅛ inch plywood. The lid, backside, and ring for the LEDs was laser cut out of ⅛ acrylic for transparency purposes. The image above was taken during the gluing process where wood glue and tape was used to initially hold the box together.
The components placed in the box roughly where they lie in the final assembly.
The box, fully assembled, with the ring LEDs fully lit up at the end of a study cycle.
When creating the pomodoro timer box, a lot of thought was put into the size of the box itself. Past students had commented that one of the biggest issues in many past projects was that the final container for the project could not fit all of the components needed, so I exhibited extreme caution when deciding on the box dimensions. Another uncertainty regarding the box fabrication was how the lid would open at the end of the cycles. Initially, I thought I would utilize a servo to lift the entire lid off of the box and hold it in the open position. After some experimentation with the servo, it was clear that the entire lid being lifted upwards was unreasonable. The solution involved creating a lid that sat flush with the walls of the box and was supported by small wooden ledges inside the box. This meant that the servo just had to move one side of the lid to pop it off of the box and allow the user to remove the lid easily while also making the lid unremovable when the servo isn’t actuated.
During the final critique of the Pomodoro Timer Box, peers identified many positive and negative aspects of the box. Firstly, many commented on the design of the box to be very clean and the functionality not being overcomplicated. It was great feedback as the intention of the box is that it can be used to help study, which many agreed the box itself would not be overly distracting because of its simple design. The place where the box could use further improvement is with the compartment itself. In its current form, the box contains wiring in the same compartment that the user would set an item into. A future improvement to the design could involve separating the wiring from the user’s items by compartmentalizing within the box. Furthermore, another area for improvement is with the LED ring. Currently, the remaining number of unlit LEDs represents the number of minutes left on the timer. A potential improvement to this system would be having the LED ring light up proportionally to the time remaining. This would allow for changes to the preset studying and break times to be made while also conveying a more clear message of how much time is remaining.
Overall, I am quite satisfied with how the Pomodoro Timer Box turned out. The basic functionality works great and can be easily used to help aid in studying. While the interior of the box could be more compartmentalized and organized, the box can still function properly and a phone can fit within the box alongside the other components which was the initial goal of the project. I think that the scope of the project fit well with the skills I had but also challenged me to learn more about Arduino code and physical fabrication. While at first I had little idea of how to code a timer, breaking the problem down into small steps made the process far more manageable and ultimately worked out very well. In addition, I thought carefully about the size of the box so that all of the necessary components would fit inside and still have space for the user to place items (up to the size of an iPhone) inside as well. I’m glad I spent time on those particularly as I was most worried about the code not working properly and the box not being large enough (or too large). In the end, it worked out very well, and I intend on ensuring that in future projects I have the same thoughtful approach during the early stages of the project.
Beyond the potential small improvements to the project that have already been mentioned, there are other, larger-scale, improvements that could be made. For instance, currently the studying time and break times are pre-set in the arduino code. An improvement to this could involve having multiple presets that the user can choose from, or even enabling the user to type in the times they wish to set for studying and for their break. In addition, the lid of the box is not mounted to anything, rather the servo lifts an edge upward to allow the user to remove the lid. The box could be far more robust if the lid were mounted to the servo and lifted open and closed by the servo itself. Despite these improvements contributing greatly to the quality of the Pomodoro Timer Box, I think that the current version successfully performs the tasks that I find most important and therefore I believe it is satisfactory in its current form.
//Ideate Physical Computing Project 2: Pomodoro Timer Box - Cody Ho
//The code below executes a timer that is displayed on an LCD that follows the Pomodoro Technique.
//In addition, the number of cycles can be set using a keypad and a ring of LEDs is lit to indicate the time remaining.
//At the end of the cycles, a servo rotates to lift the lid and let the user access the contents of the box.
//Servo Pin: connected to pin 12
//Key Pad: key 1 to pin 4, key 2 to pin 5, key 3 to pin 6, and key 4 to pin 7
//On Button: connected to pin 2
//Light Button: connected to pin 8
//LED Ring Pin: connected to pin 13
//Adafruit Neopixel Library used for LED ring
//Editable Values
int studyTime = 25;
int breakTime = 5;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long nextSecond = 1000;
int minutesDigit = studyTime;
int secondsDigit = 0;
int cycles = 1;
const int SERVO_PIN = 12;
const int onPin = 2;
const int lightPin = 8;
//Servo
#include <NewPing.h>
#include <Servo.h>
int servoMovement = 90;
int defaultPosition = 0;
Servo myMotor;
//LCD
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);
//Key Pad
#define key1 4 //Connects Wire 1 to pin 4
#define key2 5 //Connects Wire 2 to pin 5
#define key3 6 //Connects Wire 3 to pin 6
#define key4 7 //Connects Wire 4 to pin 7
int buttonState = 0;
int lightState = 0;
int study = 1;
//LED setup
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
#define LED_PIN 13 // On Trinket or Gemma, suggest changing this to 1
#define LED_COUNT 24
#define BRIGHTNESS 10 // Set BRIGHTNESS to about 1/5 (max = 255)
int NUMPIXELS = 24;
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels
int value = 0;
void setup() {
Serial.begin(9600);
//Servo
myMotor.attach(SERVO_PIN);
//LCD
screen.init();
screen.backlight();
screen.home();
//Buttons
pinMode(onPin, INPUT);
pinMode(lightPin, INPUT);
//NumPad
pinMode(key1, INPUT_PULLUP);
pinMode(key2, INPUT_PULLUP);
pinMode(key3, INPUT_PULLUP);
pinMode(key4, INPUT_PULLUP);
//LEDs
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);
returnServo();
}
void loop() {
//LED ring lit according to the number of minutes elapsed
value = (NUMPIXELS - 1) - minutesDigit;
ringLEDs(strip.Color(0, 0, 0, 255), value);
Timer();
buttonState = digitalRead(onPin);
lightState = digitalRead(lightPin);
if (lightState == 1) {
screen.noDisplay();
screen.noBacklight();
}
if (lightState == 0){
screen.display();
screen.backlight();
}
if (buttonState == 1) {
updateTimer();
if (minutesDigit == 0 && secondsDigit == 0) {
if (cycles > 0) {
rainbowFade2White(5, 5, 0);
if (study == 0) {
study = 1;
minutesDigit = studyTime;
secondsDigit = 0;
} else if (study == 1) {
study = 0;
cycles = cycles - 1;
minutesDigit = breakTime;
secondsDigit = 0;
}
} else if (cycles == 0) {
moveServo();
}
}
}
//Check Keypad entry to set number of cycles
if (buttonState == 0) {
int key1S = digitalRead(key1);
int key2S = digitalRead(key2);
int key3S = digitalRead(key3);
int key4S = digitalRead(key4);
if (!key1S) {
cycles = 1;
}
if (!key2S) {
cycles = 2;
}
if (!key3S) {
cycles = 3;
}
if (!key4S) {
cycles = 4;
}
Timer();
}
}
//Timer function
void updateTimer() {
currentMillis = millis();
if (currentMillis >= nextSecond) {
nextSecond = currentMillis + 1000;
if (secondsDigit > 0) {
secondsDigit = secondsDigit - 1;
} else if (secondsDigit == 0) {
if (minutesDigit > 0) {
secondsDigit = 59;
minutesDigit = minutesDigit - 1;
}
}
}
}
//Timer funtion to display on LCD
void Timer() {
if (secondsDigit == 9) {
screen.clear();
}
screen.setCursor(0, 0);
if (study == 1) {
screen.print("Study:");
} else if (study == 0) {
screen.print("Break:");
}
screen.print(int(minutesDigit)); //Set up timer to be Minutes:Seconds Remaining
screen.print(":");
screen.print(int(secondsDigit));
screen.setCursor(0, 1);
screen.print("Cycles:");
screen.print(int(cycles));
}
//LED Ring function
void rainbowFade2White(int wait, int rainbowLoops, int whiteLoops) {
int fadeVal = 0, fadeMax = 100;
// Hue of first pixel runs 'rainbowLoops' complete loops through the color
// wheel. Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to rainbowLoops*65536, using steps of 256 so we
// advance around the wheel at a decent clip.
for (uint32_t firstPixelHue = 0; firstPixelHue < rainbowLoops * 65536;
firstPixelHue += 256) {
for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
uint32_t pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the three-argument variant, though the
// second value (saturation) is a constant 255.
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue, 255,
255 * fadeVal / fadeMax)));
}
strip.show();
delay(wait);
if (firstPixelHue < 65536) { // First loop,
if (fadeVal < fadeMax) fadeVal++; // fade in
} else if (firstPixelHue >= ((rainbowLoops - 1) * 65536)) { // Last loop,
if (fadeVal > 0) fadeVal--; // fade out
} else {
fadeVal = fadeMax; // Interim loop, make sure fade is at max
}
}
for (int k = 0; k < whiteLoops; k++) {
for (int j = 0; j < 256; j++) { // Ramp up 0 to 255
// Fill entire strip with white at gamma-corrected brightness level 'j':
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
}
delay(1000); // Pause 1 second
for (int j = 255; j >= 0; j--) { // Ramp down 255 to 0
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
}
}
delay(500); // Pause 1/2 second
}
//LED Ring function that lights the LED Ring
void ringLEDs(uint32_t color, int value) {
for (int i = 0; i < value; i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
}
}
//Functions for moving servo and returning to starting position
void moveServo() {
myMotor.write(servoMovement);
}
void returnServo() {
myMotor.write(defaultPosition);
}