Built-in project · Arduino Uno R3

Pomodoro timer on an Arduino Uno

Twenty-five minutes of focus and five of rest on a four-digit display, with a red lamp for work, a green one for breaks and a chime at every change. The whole build, an Arduino Uno and 8 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Beginner Runs in your browser. Free, and no account needed.

The parts retail for about $34. On Mokxi they're free.

Step by step, with the wiring and the common mistakes: Arduino TM1637 Display: Library and Countdown

Pomodoro timer · Arduino Uno R3live0.000 s 0.00x
Press start to begin a focus session
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Pomodoro timer: twenty-five minutes of focus, five of rest, on a TM1637.
//
// Wiring:
//   focus lamp   pin 2 -> 220 ohm -> red LED -> GND
//   break lamp   pin 3 -> 220 ohm -> green LED -> GND
//   start        pin 4 -> pushbutton -> GND   (press: start or pause, hold: reset)
//   skip         pin 5 -> pushbutton -> GND   (end this session now)
//   piezo        pin 6 -> piezo -> GND
//   TM1637       CLK -> A0   DIO -> A1   VCC -> 5 V   GND -> GND
//
// The Pomodoro method: work for twenty-five minutes, rest for five, and after
// four rounds take a longer fifteen-minute break. The display counts down in
// minutes and seconds with the colon blinking while it runs, the red lamp
// means focus and the green lamp means rest, and the piezo chimes at every
// change.
//
// Press start to begin or pause. Hold it for a second to reset to the start
// of a focus session. Press skip to end the current session early.

#include <TM1637Display.h>

const uint8_t FOCUS_LAMP = 2;
const uint8_t BREAK_LAMP = 3;
const uint8_t START_PIN = 4;
const uint8_t SKIP_PIN = 5;
const uint8_t PIEZO_PIN = 6;

const uint32_t FOCUS_S = 25UL * 60;
const uint32_t SHORT_S = 5UL * 60;
const uint32_t LONG_S = 15UL * 60;

TM1637Display display(A0, A1);

enum Phase : uint8_t { FOCUS, SHORT_BREAK, LONG_BREAK };
Phase phase = FOCUS;
uint8_t done = 0;            // focus sessions finished
uint32_t left = FOCUS_S;     // seconds left in this session
bool running = false;
uint32_t tickAt = 0;         // when the next second is due
bool startWas = false, skipWas = false;
uint32_t startDownAt = 0;
bool resetDone = false;

static const char *phaseName()
{
  return phase == FOCUS ? "Focus" : (phase == SHORT_BREAK ? "Short break" : "Long break");
}

static void show()
{
  int mmss = (int)((left / 60) * 100 + left % 60);
  // The colon blinks once a second while it runs and stays on when paused.
  bool colon = !running || (millis() % 1000) < 500;
  display.showNumberDecEx(mmss, colon ? 0b01000000 : 0, true);
  digitalWrite(FOCUS_LAMP, phase == FOCUS ? HIGH : LOW);
  digitalWrite(BREAK_LAMP, phase == FOCUS ? LOW : HIGH);
}

static void chime(bool toWork)
{
  // Up for rest, down for work, so you can tell them apart without looking.
  const uint16_t up[3] = {523, 659, 784};
  const uint16_t down[3] = {784, 659, 523};
  const uint16_t *notes = toWork ? down : up;
  for (uint8_t i = 0; i < 3; i++) {
    tone(PIEZO_PIN, notes[i], 150);
    delay(180);
  }
}

static void nextPhase()
{
  if (phase == FOCUS) {
    done++;
    phase = (done % 4 == 0) ? LONG_BREAK : SHORT_BREAK;
    left = phase == LONG_BREAK ? LONG_S : SHORT_S;
  } else {
    phase = FOCUS;
    left = FOCUS_S;
  }
  Serial.print(phaseName());
  Serial.print(" (");
  Serial.print(done);
  Serial.println(done == 1 ? " session done)" : " sessions done)");
  show();
  chime(phase == FOCUS);
  tickAt = millis() + 1000;
}

void setup()
{
  Serial.begin(9600);
  pinMode(FOCUS_LAMP, OUTPUT);
  pinMode(BREAK_LAMP, OUTPUT);
  pinMode(START_PIN, INPUT_PULLUP);
  pinMode(SKIP_PIN, INPUT_PULLUP);
  noTone(PIEZO_PIN);
  display.setBrightness(4);
  show();
  delay(20);
  Serial.println("Pomodoro: press start for 25 minutes of focus");
}

void loop()
{
  uint32_t now = millis();
  bool start = digitalRead(START_PIN) == LOW;
  bool skip = digitalRead(SKIP_PIN) == LOW;

  if (start && !startWas) {
    startDownAt = now;
    resetDone = false;
  }
  if (start && !resetDone && now - startDownAt >= 1000) {
    // held: back to the beginning
    running = false;
    phase = FOCUS;
    left = FOCUS_S;
    done = 0;
    resetDone = true;
    tone(PIEZO_PIN, 330, 200);
    Serial.println("Reset");
  }
  if (!start && startWas && !resetDone) {
    running = !running;
    tickAt = now + 1000;
    tone(PIEZO_PIN, running ? 880 : 440, 60);
    Serial.println(running ? "Running" : "Paused");
  }
  if (skip && !skipWas) {
    Serial.println("Skipped");
    nextPhase();
  }
  startWas = start;
  skipWas = skip;

  if (running && (int32_t)(now - tickAt) >= 0) {
    tickAt += 1000;
    if (left > 0) {
      left--;
    }
    if (left == 0) {
      nextPhase();
    }
  }
  show();
  delay(20);
}

Parts list

10 parts, plus the jumper wires. They retail for about $34; on Mokxi they're free.

How it is wired

11 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.

  • Ground: Arduino Uno R3 pin GND; LED, red pin C; LED, green pin C; Pushbutton (1) pin 2a; Pushbutton (2) pin 2a; Piezo speaker pin 2; Four-digit display, TM1637 pin GND
  • Arduino Uno R3 pin 6; Piezo speaker pin 1
  • Arduino Uno R3 pin 5; Pushbutton (2) pin 1a
  • Arduino Uno R3 pin 4; Pushbutton (1) pin 1a
  • Arduino Uno R3 pin 3; Resistor, 220 Ω (2) pin 1
  • Arduino Uno R3 pin 2; Resistor, 220 Ω (1) pin 1
  • Arduino Uno R3 pin 5V; Four-digit display, TM1637 pin VCC
  • Arduino Uno R3 pin A0; Four-digit display, TM1637 pin CLK
  • Arduino Uno R3 pin A1; Four-digit display, TM1637 pin DIO
  • Resistor, 220 Ω (1) pin 2; LED, red pin A
  • Resistor, 220 Ω (2) pin 2; LED, green pin A

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.