Pomodoro Device
Take a look at the demo
Introduction
A few years ago, while on a mission to enhance my productivity, I discovered the Pomodoro Technique. This method involves working in focused 25-minute intervals, followed by a 5-minute break, which helped me avoid burnout and stay efficient.
A few months later, I started experimenting with Arduino and was immediately fascinated by its capabilities. This project is a harmonious blend of these two passions – a professional approach to productivity, infused with a touch of creativity and innovation.
This blog is more technical than explanatory. I have provided the code and and necessary diagrams if someone wants to replicate this
Components and supplies list
Arduino Nano R3 -> 1
Tactile Switch -> 1
Resistor 220 ohm -> 7
Resistor 100 ohm -> 1
Shift Register- Parallel to Serial -> 1
Buzzer -> 1
7-Segment Serial Display -> 1
LED, Orange -> 1
LED, Green -> 1
Solder Wire, Lead Free
Soldering iron (generic)
Arduino IDE
Schematics Diagram
Ki Cad 3D views
Code
int D1 = 2;
int D2 = 3;
int D3 = 4;
int D4 = 5;
int dataPin = 11;
int latchPin = 8;
int clockPin = 12;
int buzzerPin = 6;
int buttonPin = 7;
int stateLedPin = 9;
// ----- ^^ PINS ^^ -----
int delayTime = 4;
int buzzerFrequency = 523;
int nums[10] = {
1, 79, 18, 6, 76, 36, 32, 15, 0, 4
};
int blanckNum = 127;
int pLetter = 24;
void updateShiftRegisterState(byte input){
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, input);
digitalWrite(latchPin, HIGH);
}
void draw(int state, bool enableDecimal){
int leds = state*2;
if(!enableDecimal) {
leds = leds + 1;
}
updateShiftRegisterState(leds);
}
void resetTo(int letter, int dState){
digitalWrite(D1, dState);
digitalWrite(D2, dState);
digitalWrite(D3, dState);
digitalWrite(D4, dState);
draw(letter, false);
}
void setDigit(int d1, int d2, int d3, int d4){
digitalWrite(D1, d1);
digitalWrite(D2, d2);
digitalWrite(D3, d3);
digitalWrite(D4, d4);
}
void setD1() {
setDigit(HIGH, LOW, LOW, LOW);
}
void setD2() {
setDigit(LOW, HIGH, LOW, LOW);
}
void setD3() {
setDigit(LOW, LOW, HIGH, LOW);
}
void setD4() {
setDigit(LOW, LOW, LOW, HIGH);
}
void drawNumber(int num){
if(num == -1){
resetTo(blanckNum, LOW);
return;
}
else if(num == -2){
resetTo(pLetter, HIGH);
return;
}
for(int i=0; i<4; i++){
if(num==0 and i>0){
break;
}
int dig = num % 10;
num = num/10;
if(i==0){
setD4();
}
else if(i==1){
setD3();
}
else if(i==2){
setD2();
}
else if(i==3){
setD1();
}
bool enableDecimal = false;
if(i==2){
enableDecimal = true;
}
draw(nums[dig], enableDecimal);
delay(delayTime);
resetTo(blanckNum, LOW);
}
}
int secToTime(int secs){
int mins = secs/60;
int remSecs = secs%60;
return mins*100 + remSecs;
}
int pomodoroSecs[8] = {
1500, 300, 1500, 300, 1500, 300, 1500, 900
};
//int pomodoroSecs[8] = {
// 10, 5, 10, 5, 10, 5, 10, 6
//};
bool isWorkState[8] = {
true, false, true, false, true, false, true, false
};
int curPomodoro = 0;
int curSecs = -1;
int STOP_SIGNAL = -2;
int WELCOME_STATE = -1;
int TIMER_STATE = 0;
int TIMER_END = 1;
int TIMER_PAUSED = 2;
int SKIP_SIGNAL = 3;
int deviceState = WELCOME_STATE;
void resetTimer(){
curSecs = 0;
curPomodoro = (curPomodoro+1)%8;
}
unsigned long nextMillis = 0;
int setNextMillis(int gap){
nextMillis = millis()+gap;
nextMillis = nextMillis - nextMillis%100;
}
int lastButtonState = LOW;
int curButtonState;
unsigned long debounceDelay = 50;
unsigned long lastDebounceTime = 0;
unsigned long buttonDownMillis = 0;
unsigned long longPressTime = 1000;
unsigned long stopPressTime = 3000;
unsigned long isButtonPressed(){
unsigned long pressTime = 0;
int buttonState = digitalRead(buttonPin);
if(lastButtonState != buttonState)
lastDebounceTime = millis();
if((millis() - lastDebounceTime) > debounceDelay)
if(buttonState != curButtonState){
curButtonState = buttonState;
if(curButtonState == HIGH)
buttonDownMillis = millis();
if(curButtonState == LOW)
pressTime = millis() - buttonDownMillis;
}
lastButtonState = buttonState;
return pressTime;
}
bool disOn = true;
void resetDevice(){
curSecs = -1;
curPomodoro = 0;
}
int timerPaused(){
unsigned long buttonMillis = isButtonPressed();
if(buttonMillis>stopPressTime){
drawNumber(-1);
noTone(buzzerPin);
resetDevice();
return STOP_SIGNAL;
}
else if(buttonMillis>longPressTime)
return SKIP_SIGNAL;
else if(buttonMillis)
return TIMER_STATE;
if(millis() > nextMillis){
setNextMillis(500);
disOn = !disOn;
}
if(disOn)
drawNumber(secToTime(curSecs));
else
drawNumber(-1);
return TIMER_PAUSED;
}
int timerRunner(){
unsigned long buttonMillis = isButtonPressed();
if(buttonMillis>stopPressTime){
drawNumber(-1);
noTone(buzzerPin);
resetDevice();
return STOP_SIGNAL;
}
else if(buttonMillis>longPressTime)
return SKIP_SIGNAL;
else if(buttonMillis) {
tone(buzzerPin, buzzerFrequency);
noTone(buzzerPin);
return TIMER_PAUSED;
}
if(curSecs <= pomodoroSecs[curPomodoro]){
if(millis() > nextMillis){
curSecs++;
setNextMillis(1000);
}
digitalWrite(stateLedPin, isWorkState[curPomodoro]);
drawNumber(secToTime(curSecs));
return TIMER_STATE;
}
else{
resetTimer();
return TIMER_END;
}
}
int timerEnderProgramm(){
unsigned long buttonMillis = isButtonPressed();
if(buttonMillis>stopPressTime){
drawNumber(-1);
noTone(buzzerPin);
resetDevice();
return STOP_SIGNAL;
}
else if(buttonMillis > longPressTime)
return SKIP_SIGNAL;
else if(buttonMillis){
noTone(buzzerPin);
return TIMER_STATE;
}
digitalWrite(stateLedPin, isWorkState[curPomodoro]);
if(millis() > nextMillis){
setNextMillis(500);
disOn = !disOn;
}
if(disOn){
drawNumber(secToTime(pomodoroSecs[curPomodoro]));
tone(buzzerPin, buzzerFrequency);
}
else{
drawNumber(-1);
noTone(buzzerPin);
}
return TIMER_END;
}
int skipCurrentTimer(){
resetTimer();
return TIMER_END;
}
unsigned long welcomeTomeLength = 2000;
unsigned long startMillis = 0;
int welcomeProgramm(){
if(millis()-startMillis<=welcomeTomeLength){
tone(buzzerPin, buzzerFrequency);
}
else
noTone(buzzerPin);
unsigned long buttonMillis = isButtonPressed();
if(buttonMillis>stopPressTime){
drawNumber(-1);
noTone(buzzerPin);
resetDevice();
return STOP_SIGNAL;
}
else if(buttonMillis){
pinMode(stateLedPin, OUTPUT);
noTone(buzzerPin);
return TIMER_STATE;
}
drawNumber(-2);
return WELCOME_STATE;
}
int doNothing(){
pinMode(stateLedPin, INPUT);
if(isButtonPressed()){
startMillis = millis();
return WELCOME_STATE;
}
return STOP_SIGNAL;
}
void setup() {
pinMode(D1, OUTPUT);
pinMode(D2, OUTPUT);
pinMode(D3, OUTPUT);
pinMode(D4, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(stateLedPin, INPUT);
}
void loop() {
if(deviceState == STOP_SIGNAL)
deviceState = doNothing();
else if(deviceState == WELCOME_STATE)
deviceState = welcomeProgramm();
else if(deviceState == TIMER_STATE)
deviceState = timerRunner();
else if(deviceState == TIMER_PAUSED)
deviceState = timerPaused();
else if(deviceState == TIMER_END)
deviceState = timerEnderProgramm();
else if(deviceState == SKIP_SIGNAL)
deviceState = skipCurrentTimer();
}
How to customize?
The provided code can be changed to work for any sequence of events with existing hardware setup. Existing code has [25 min work, 5 min break, 25 min work, 5 min break, 25 min work, 5 min break, 25 min work, 15 min break] and then repeat.
If you wish to change with sequence just change bellow lines of code:
int pomodoroSecs[8] = {
1500, 300, 1500, 300, 1500, 300, 1500, 900
};
bool isWorkState[8] = {
true, false, true, false, true, false, true, false
};
Reference
This whole project is available in mt git repo at rahul38888/PomodoroDevice
Same blog you can find under my Arduino projecthub profile