The 25-Minute Focus Machine: How I Built an ESP32 Pomodoro Timer (And Stopped Wasting Time)

The “I Can’t Focus” Problem

I have a confession.

I sit down to study. I open my laptop. I swear I’ll work for one hour.

Twenty minutes later, I’m watching a video about how to build a robot that makes coffee. Ten minutes after that, I’m checking my phone. Five minutes later, I’m staring at the ceiling wondering where my life went.

The Pomodoro Technique saved me. But the apps? They didn’t.

Phone notifications. Distracting interfaces. “Just one more minute” lies.

So I did what any sensible BUET student would do. I built my own.

A physical ESP32 Pomodoro timer. ESP32 brain. OLED display. Buzzer that actually annoys me enough to stop scrolling.

Twenty-five minutes of focus. Five minutes of break. Repeat.

No notifications. No excuses. Just a box that beeps at me until I work.

What is the Pomodoro Technique?

Invented by Francesco Cirillo in the late 1980s, the Pomodoro Technique is simple:

 
 
PhaseDurationWhat You Do
Work Session25 minutesFocus on ONE task. No distractions.
Short Break5 minutesStand up, stretch, check phone, breathe.
Long Break15 minutesEvery 4 work sessions, take a longer break.

That’s it. No complex system. No expensive apps. Just a timer and discipline.

But here’s the problem: using your phone as a timer is dangerous. One notification and you’re gone. Using a physical timer? Pure focus.

What You’ll Build

A standalone Pomodoro timer that:

  • ✅ Shows countdown on a bright OLED display

  • ✅ Beeps when time is up (loud enough to notice)

  • ✅ Has physical buttons to start, pause, reset

  • ✅ Runs on USB power (or battery if you want)

  • ✅ Fits in the palm of your hand

No phone. No distractions. Just work.

A functional block diagram showing the system flow of an ESP32 Pomodoro timer, including start/pause buttons, reset button, ESP32 microcontroller, OLED display, and buzzer.

What You’ll Need

Hardware Components

 
 
ComponentSpecsPrice (USD)
ESP32 Development BoardAny 30-pin version$5.00 – $7.00
0.96″ OLED DisplayI2C, 128×64 pixels$2.00 – $4.00
Passive Buzzer5V$0.50 – $1.00
Push Buttons (2x)Tactile switch$0.20 – $0.50 each
10kΩ Resistors (2x)For pull-down$0.05 – $0.10 each
Breadboard400 points$1.00 – $2.00
Jumper WiresMale-to-female and male-to-male$1.00 – $2.00
USB CableFor power/programming$2.00 – $4.00

Total: ~$12.00 – $21.00 USD

A collection of electronics components on a green cutting mat for an ESP32 Pomodoro timer project, featuring an ESP32 board, OLED display, breadboards, buzzer, resistors, and jumper wires.
 
 
ComponentWhere to Find
ESP32 Development BoardAliExpress / Amazon
0.96″ OLED Display I2CAliExpress
Passive BuzzerAliExpress
Tactile Push Buttons (Pack)AliExpress
10kΩ Resistors (Pack)AliExpress
Breadboard + Jumper KitAliExpress

Wiring It Up (10 Minutes)

I2C OLED Display

 
 
OLED PinESP32 PinNotes
VCC3.3VPower
GNDGNDGround
SCLGPIO 22I2C Clock
SDAGPIO 21I2C Data

Buzzer

 
 
Buzzer PinESP32 Pin
Positive (+)GPIO 26
Negative (-)GND

Push Buttons

 
 
ButtonESP32 PinThrough Resistor
Start/PauseGPIO 1310kΩ to GND (pull-down)
ResetGPIO 1410kΩ to GND (pull-down)

Button wiring explained:

  • One side of button → ESP32 pin

  • Same side → 10kΩ resistor → GND (pull-down)

  • Other side of button → 3.3V

When button is pressed, pin reads HIGH. When released, resistor pulls it LOW.

The Code

Full code available on GitHub: [Link to repository]

How the Timer Logic Works

logic.ino
// Pomodoro states
enum TimerState { WORK, BREAK, LONG_BREAK };
TimerState state = WORK;

// Timer variables
unsigned long workDuration = 25 * 60;    // 25 minutes in seconds
unsigned long breakDuration = 5 * 60;    // 5 minutes
unsigned long longBreakDuration = 15 * 60; // 15 minutes
unsigned long timeLeft = workDuration;
bool timerRunning = false;

If VS Code is your preferred editor, make sure you have the PlatformIO extension installed. Read this blog – I Used Arduino IDE for 3 Months. Then I Found VS Code + PlatformIO and Never Went Back

How to Use It

 
 
ButtonAction
Start/PausePress once to start countdown. Press again to pause.
ResetPress to reset to 25:00 work session.

The flow:

  1. Power on the ESP32. Display shows “WORK 25:00”

  2. Press Start. Timer counts down.

  3. When timer hits 0, buzzer beeps. Automatically switches to BREAK (5:00).

  4. After 4 work sessions, switches to LONG BREAK (15:00).

  5. Press Reset anytime to start over.

An infographic showing the Pomodoro cycle for an ESP32 timer project, featuring 25-minute work sessions, 5-minute short breaks, and a 15-minute long break after four cycles.

Understanding the Code Logic

 
 
Code SectionWhat It Does
enum TimerStateDefines the three timer modes (WORK, BREAK, LONG_BREAK)
millis()Non-blocking timer – allows button presses during countdown
updateDisplay()Refreshes OLED screen with current time and state
nextState()Handles transitions between work/break cycles
workSessionsCompletedTracks how many work sessions done to trigger long break

The millis() function is crucial. Unlike delay(), it doesn’t freeze the ESP32. You can press buttons while the timer runs.

(Photo: Serial Monitor showing state transitions)

Make It Your Own (Upgrades)

 
 
UpgradeDifficultyWhat You’ll Need
Adjustable times⭐ EasyAdd buttons to increase/decrease work/break duration
Battery powered⭐⭐ Medium18650 battery + TP4056 charger module
3D printed case⭐⭐ Medium3D printer or laser cutter
Vibration motor⭐⭐ MediumVibration motor (for silent alerts)
LED indicators⭐ EasyAdd RGB LED for visual status (green=work, blue=break)
LCD instead of OLED⭐ Easy16×2 I2C LCD (cheaper, but less fancy)

The Science: Why 25 Minutes Works

The Pomodoro Technique isn’t random. Research shows:

 
 
TimeWhy It Works
25 minutesLong enough to get meaningful work done, short enough to maintain focus
5 minutesAllows brain to rest, reduces mental fatigue
15 minutesLonger recovery after 2 hours of focused work

Your brain has limited attention resources. After 25-30 minutes, focus naturally declines. A short break replenishes these resources.

A student using a custom-built ESP32 Pomodoro timer on a breadboard to manage a 25-minute study session alongside a laptop and textbooks.

Troubleshooting

 
 
ProblemLikely CauseFix
OLED shows nothingWrong I2C addressTry address 0x3C or 0x3D
Buttons not workingMissing pull-down resistorsAdd 10kΩ resistor from pin to GND
Timer doesn’t startButton wiring issueCheck BTN_START pin connection
Buzzer not beepingPassive vs active buzzertone() works only on passive buzzers
Display flickersPower supply issueAdd 100µF capacitor across 5V and GND

Why This is Better Than Phone Apps

 
 
FeatureMy ESP32 TimerPhone App
No distractions✅ Yes❌ Notifications everywhere
Physical buttons✅ Satisfying❌ Touchscreen (accidental pauses)
Battery lastsDays/weeksHours
Loud buzzer✅ Annoying enough❌ Easy to ignore
Learning value✅ Priceless❌ Zero
Cost~$15 one-timeFree but with ads/in-app purchases

I’ve tried every Pomodoro app. I always ended up scrolling Instagram mid-session. This timer? It just sits there. Beeping. Judging me. Perfect.

What You Learned

 
 
ConceptWhy It Matters
millis() for timingNon-blocking code – run multiple things simultaneously
State machines (enum)Manage different modes (work, break, long break)
I2C communicationTalk to OLED displays with only 2 wires
Button debouncingPrevent multiple triggers from one press
Buzzer with tone()Generate beeps without extra hardware

Project Demo

Your Turn

This project took me an afternoon to build and has saved me hours of procrastination.

Now go build yours. Then put it on your desk. And when that buzzer goes off, don’t ignore it.

Twenty-five minutes of focus. Five minutes of rest. That’s all it takes.

See the timer in action! Watch the other video’s of Roborear, and https://www.youtube.com/@Roborear for more weekly projects.

FAQs

1. Why is my OLED display showing nothing or just white?

This is the most common issue. Here's how to fix it: First, check your I2C address. Most 0.96" OLEDs use address 0x3C, but some use 0x3D. In the code, change #define OLED_ADDR 0x3C to 0x3D and re-upload. Second, verify wiring. SDA must go to GPIO 21, SCL to GPIO 22 on the ESP32. Swap them if it's not working. Third, check power. The OLED needs 3.3V, not 5V. Connecting to 5V can damage the display. Finally, try the I2C scanner sketch to confirm your display is detected. If the scanner finds nothing, your wiring or display is faulty.

2. Why does my timer pause randomly or not start when I press the button?

This is usually a button debouncing issue. Mechanical buttons "bounce" – they make multiple electrical connections in milliseconds. Your code reads these as multiple presses. The fix is already in the code: delay(300); after detecting a button press. This ignores any further signals for 300ms. If the problem persists, add this improved debounce logic: cpp if (startPressed && (millis() - lastButtonPress > 300)) { lastButtonPress = millis(); timerRunning = !timerRunning; } Also check your wiring. Each button needs a 10kΩ pull-down resistor from the ESP32 pin to GND. Without it, the pin floats and reads random values.

3. How do I change the work and break durations?

The durations are defined at the top of the code. Find these lines: cpp const unsigned long WORK_TIME = 25 * 60; const unsigned long BREAK_TIME = 5 * 60; const unsigned long LONG_BREAK_TIME = 15 * 60; To change to 30-minute work sessions and 10-minute breaks: cpp const unsigned long WORK_TIME = 30 * 60; const unsigned long BREAK_TIME = 10 * 60; If you want to adjust times without re-uploading code, add two extra buttons and modify the code to increase/decrease WORK_TIME and BREAK_TIME on the fly. Store the values in EEPROM so they persist after power loss.

Subscribe to The Newsletter

Join robotics enthusiasts getting weekly project ideas:

• 🔧 Under $50 projects

• ⚙️ Under $100 projects

• 🎥 New video tutorials

• 📝 Blog updates

We don’t spam! Read our privacy policy for more info.

Similar Posts

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *