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:
| Phase | Duration | What You Do |
|---|---|---|
| Work Session | 25 minutes | Focus on ONE task. No distractions. |
| Short Break | 5 minutes | Stand up, stretch, check phone, breathe. |
| Long Break | 15 minutes | Every 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.

What You’ll Need
Hardware Components
| Component | Specs | Price (USD) |
|---|---|---|
| ESP32 Development Board | Any 30-pin version | $5.00 – $7.00 |
| 0.96″ OLED Display | I2C, 128×64 pixels | $2.00 – $4.00 |
| Passive Buzzer | 5V | $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 |
| Breadboard | 400 points | $1.00 – $2.00 |
| Jumper Wires | Male-to-female and male-to-male | $1.00 – $2.00 |
| USB Cable | For power/programming | $2.00 – $4.00 |
Total: ~$12.00 – $21.00 USD

Quick Buy Links
| Component | Where to Find |
|---|---|
| ESP32 Development Board | AliExpress / Amazon |
| 0.96″ OLED Display I2C | AliExpress |
| Passive Buzzer | AliExpress |
| Tactile Push Buttons (Pack) | AliExpress |
| 10kΩ Resistors (Pack) | AliExpress |
| Breadboard + Jumper Kit | AliExpress |
Wiring It Up (10 Minutes)
I2C OLED Display
| OLED Pin | ESP32 Pin | Notes |
|---|---|---|
| VCC | 3.3V | Power |
| GND | GND | Ground |
| SCL | GPIO 22 | I2C Clock |
| SDA | GPIO 21 | I2C Data |
Buzzer
| Buzzer Pin | ESP32 Pin |
|---|---|
| Positive (+) | GPIO 26 |
| Negative (-) | GND |
Push Buttons
| Button | ESP32 Pin | Through Resistor |
|---|---|---|
| Start/Pause | GPIO 13 | 10kΩ to GND (pull-down) |
| Reset | GPIO 14 | 10kΩ 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
// 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
| Button | Action |
|---|---|
| Start/Pause | Press once to start countdown. Press again to pause. |
| Reset | Press to reset to 25:00 work session. |
The flow:
Power on the ESP32. Display shows “WORK 25:00”
Press Start. Timer counts down.
When timer hits 0, buzzer beeps. Automatically switches to BREAK (5:00).
After 4 work sessions, switches to LONG BREAK (15:00).
Press Reset anytime to start over.

Understanding the Code Logic
| Code Section | What It Does |
|---|---|
enum TimerState | Defines 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 |
workSessionsCompleted | Tracks 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)
| Upgrade | Difficulty | What You’ll Need |
|---|---|---|
| Adjustable times | ⭐ Easy | Add buttons to increase/decrease work/break duration |
| Battery powered | ⭐⭐ Medium | 18650 battery + TP4056 charger module |
| 3D printed case | ⭐⭐ Medium | 3D printer or laser cutter |
| Vibration motor | ⭐⭐ Medium | Vibration motor (for silent alerts) |
| LED indicators | ⭐ Easy | Add RGB LED for visual status (green=work, blue=break) |
| LCD instead of OLED | ⭐ Easy | 16×2 I2C LCD (cheaper, but less fancy) |
The Science: Why 25 Minutes Works
The Pomodoro Technique isn’t random. Research shows:
| Time | Why It Works |
|---|---|
| 25 minutes | Long enough to get meaningful work done, short enough to maintain focus |
| 5 minutes | Allows brain to rest, reduces mental fatigue |
| 15 minutes | Longer 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.
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
| OLED shows nothing | Wrong I2C address | Try address 0x3C or 0x3D |
| Buttons not working | Missing pull-down resistors | Add 10kΩ resistor from pin to GND |
| Timer doesn’t start | Button wiring issue | Check BTN_START pin connection |
| Buzzer not beeping | Passive vs active buzzer | tone() works only on passive buzzers |
| Display flickers | Power supply issue | Add 100µF capacitor across 5V and GND |
Why This is Better Than Phone Apps
| Feature | My ESP32 Timer | Phone App |
|---|---|---|
| No distractions | ✅ Yes | ❌ Notifications everywhere |
| Physical buttons | ✅ Satisfying | ❌ Touchscreen (accidental pauses) |
| Battery lasts | Days/weeks | Hours |
| Loud buzzer | ✅ Annoying enough | ❌ Easy to ignore |
| Learning value | ✅ Priceless | ❌ Zero |
| Cost | ~$15 one-time | Free 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
| Concept | Why It Matters |
|---|---|
millis() for timing | Non-blocking code – run multiple things simultaneously |
State machines (enum) | Manage different modes (work, break, long break) |
| I2C communication | Talk to OLED displays with only 2 wires |
| Button debouncing | Prevent 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.







One Comment