DEV Community

Rogier van den Berg
Rogier van den Berg

Posted on

Building a Battery-Powered Pomodoro Timer with Deep Sleep

The Pomodoro Technique is simple but effective: 25 minutes of pure focus, followed by a 5-minute break. Back in 2013, I built my first custom timer to automate this. Now, over a decade later, it's time for a serious upgrade.

In this post, I'll show you how I converted my old bulky design into a modern, ultra-low-power version using an OLED display and Deep Sleep technology. I'm sharing the code for both the ESP32 and the energy-efficient Seeed XIAO nRF52840, including a custom 3D-printed case!

The History: Version 2013

My original project was built around a classic Arduino Uno and a 7-segment display. All put together in a 3D-printed box, held together with tape as printers then were not that good yet. While it looked cool, the wiring was a nightmare—I had to use shift registers (74HC595) to manage the pins, and power consumption was high, meaning it always had to be plugged in.

The code was functional but verbose, handling all the bit-shifting manually:

// The old way: Managing a 7-segment display through shift registers
byte chars[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, chars[digit]);
digitalWrite(latchPin, HIGH);
Enter fullscreen mode Exit fullscreen mode

It worked, but it wasn't elegant—and certainly not portable.

The Upgrade: Version 2026

The new version does exactly the same thing, but smarter.

What's Changed?

  • Display: A crisp I2C OLED screen (needs only 2 wires for data!).
  • Power: Battery support, and thanks to "Deep Sleep," the microcontroller turns off completely when the timer finishes. One press of the button wakes it up instantly.
  • Audio: A triumphant melody plays when your session is done.
  • Memory: The timer remembers your last setting, even after sleeping!

The Power Advantage

Here's the magic of Deep Sleep:

State ESP32 Current nRF52840 Current
Active (counting) ~40mA ~5mA
Deep Sleep ~10µA ~1µA

With the nRF52840, a small 500mAh LiPo battery could theoretically last for months in standby, waking up only when you press the button.

What You Need

Component Description
Microcontroller ESP32 (DevKitC) (accessible, pin headers, ready to prototype), Seeed Studio XIAO ESP32-C6 (cheap!), or Seeed XIAO nRF52840 (best for battery life)
Display 0.96 inch OLED Display (I2C SSD1306)
Button Simple push button
Audio Passive Piezo buzzer
Power USB cable or a 3.7V LiPo battery (ideal for the XIAO)
Case 3D printed (files in the repository)

Wiring Guide

The wiring is straightforward. Here is the pinout for both board options:

Component ESP32 Pin XIAO nRF52840 Pin
OLED SDA GPIO 33 D4
OLED SCL GPIO 32 D5
Button GPIO 25 (to GND) D1 (to GND)
Buzzer (+) GPIO 27 D2
Buzzer (-) GND GND

Both the button and buzzer connect to ground on the other side. The button uses the internal pull-up resistor, so no external resistor is needed. I soldered the battery directly to the board in the final product, as the XIAO supports battery charging.

The Case (3D Print)

I designed a compact housing to fit everything neatly. The design includes:

  • Slot for Seeed Studio XIAO nRF52840, with recessed USB port
  • Cutout for the OLED display
  • Button hole
  • Battery compartment

The board is clamped in place by a bracket and a pin from the top of the enclosure, the battery rests inside a compartment and the display hangs in a small gutter, with lips behind the screen coming from the top of the enclosure. Together with a snap fit this keeps everything neatly in place.

Download the STL files: Available in the enclosure/ folder of the GitHub repository.

The Code

I made two versions, first based on ESP32 for prototyping, later with the nRF52840 in the final product, to have a more power efficient solution.

Both versions share the same logic:

  1. Wake up → Show last selected duration
  2. Button press → Cycle through 5, 15, 25, 45 minutes
  3. Wait 2 seconds → Auto-start countdown
  4. Count down → Display remaining minutes
  5. Finish → Play melody, show "KLAAR!" (Dutch for "Ready!" 🇳🇱), go to sleep

The key difference is how they sleep and remember settings.

Option A: ESP32 Deep Sleep

The ESP32 uses RTC_DATA_ATTR to store variables in RTC memory (survives deep sleep) and esp_deep_sleep_start() to enter low-power mode:

// Store setting in RTC memory - survives deep sleep!
RTC_DATA_ATTR int countdownSetting = 2;

void goToSleep() {
  display.ssd1306_command(SSD1306_DISPLAYOFF);
  esp_sleep_enable_ext0_wakeup(buttonPin, 0);  // Wake on button press
  esp_deep_sleep_start();
}
Enter fullscreen mode Exit fullscreen mode

Option B: Seeed XIAO nRF52840

The nRF52840 uses System OFF mode for even lower power consumption. It stores settings in the GPREGRET register:

// Store setting in GPREGRET - survives System OFF!
NRF_POWER->GPREGRET = countdownSetting;

void goToSleep() {
  display.ssd1306_command(SSD1306_DISPLAYOFF);

  // Configure button to wake from System OFF
  nrf_gpio_cfg_sense_input(
    digitalPinToPinName(buttonPin),
    NRF_GPIO_PIN_PULLUP,
    NRF_GPIO_PIN_SENSE_LOW
  );

  NRF_POWER->SYSTEMOFF = 1;  // Enter System OFF mode
}
Enter fullscreen mode Exit fullscreen mode

When the nRF52840 wakes up, it's essentially a reset—the code starts fresh from setup(). We check GPREGRET to restore the previous setting.

Getting Started

  1. Clone the repository:
   git clone https://github.com/rogiervandenberg/pomodorotimer.git
Enter fullscreen mode Exit fullscreen mode
  1. Install the Arduino libraries (via Library Manager):
  • Adafruit GFX Library
  • Adafruit SSD1306
  1. Install the board package:
  • ESP32: Add the ESP32 boards URL in Arduino IDE preferences
  • nRF52840: Follow the Seeed XIAO BLE guide
  1. Upload the appropriate sketch to your board

  2. Print the case and assemble everything

Lessons Learned

After 13 years, some things have changed dramatically:

  • I2C is supercool: Managing shift registers was fun in 2013, but modern I2C displays with clean libraries make life so much easier.
  • Deep Sleep changes everything: The ability to run on battery for months opens up entirely new use cases. My little clock is now fully portable!
  • One button is enough: Constraint breeds creativity. The single-button interface forces simplicity.

Conclusion

This project proves that sometimes the best gadget is the one that does exactly one thing well. No notifications, no apps, no distractions—just a timer and a button.

The full code is available on GitHub: rogiervandenberg/pomodorotimer
The 3D enclosure is available on Makerworld

Happy building and stay focused! 🍅

Top comments (0)