Built-in project · Arduino Uno R3

Pomodoro timer on an active buzzer (pin 9), on an Arduino Uno

An active buzzer on pin 9 times 25 minutes of work and 5 minutes of rest with millis(): one long beep to start work, two short ones to start the break, and the minutes left in the serial monitor. The whole build, an Arduino Uno and 1 more part, 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 About 10 minutes to build and run. Runs in your browser. Free, and no account needed.

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

The circuit runs here once the page has loaded.

What you'll learn

  • Timing with millis instead of delay
  • Switching between two states with a flag
  • Driving a buzzer from a pin
  • Printing progress to the serial monitor
  • Difficulty: Beginner
  • Time: About 10 minutes
  • Board: Arduino Uno R3
  • Parts: 3

How it works

A buzzer on pin 9 marks a work and rest cycle: 25 minutes of work, then 5 minutes of rest, over and over. One long beep at the start tells you work has begun.

The sketch keeps time with millis and not delay. It remembers when the current period began and switches when enough time has passed. That leaves the loop free while it waits.

At the end of work the buzzer beeps twice, short. At the end of rest it beeps once, long. Each minute the serial monitor shows how many minutes are left.

Parts list

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

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Buzzer pin 2Arduino Uno R3 pin GND
Buzzer pin 1Arduino Uno R3 pin 9

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
// A Pomodoro timer: 25 minutes of work, 5 of rest, on a buzzer on pin 9.
//
//   pin 9 -> the active buzzer's + leg; the other leg -> GND
//
// delay() would freeze the board for 25 minutes, so the timer keeps time with
// millis() instead: the milliseconds since the board started. It remembers
// when the current period began and switches when enough time has passed,
// with the loop free to do other things (here, print the minutes left).

const int BUZZER = 9;
const unsigned long WORK_MS = 25UL * 60UL * 1000UL;
const unsigned long BREAK_MS = 5UL * 60UL * 1000UL;

bool working = true;
unsigned long started = 0;
long lastMinute = -1;

void beep(int times, int ms) {
  for (int i = 0; i < times; i++) {
    digitalWrite(BUZZER, HIGH);
    delay(ms);
    digitalWrite(BUZZER, LOW);
    delay(200);
  }
}

void setup() {
  pinMode(BUZZER, OUTPUT);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: Pomodoro timer, 25 minutes of work, then 5 of rest");
  beep(1, 600);
  started = millis();
}

void loop() {
  unsigned long period = working ? WORK_MS : BREAK_MS;
  unsigned long elapsed = millis() - started;
  if (elapsed >= period) {
    working = !working;
    started = millis();
    lastMinute = -1;
    if (working) beep(1, 600);
    else beep(2, 200);
    Serial.println(working ? "back to work" : "break time");
    return;
  }
  long minutesLeft = (period - elapsed + 59999UL) / 60000UL;
  if (minutesLeft != lastMinute) {
    lastMinute = minutesLeft;
    Serial.print(working ? "work: " : "break: ");
    Serial.print(minutesLeft);
    Serial.println(" min left");
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

beep sketch.ino

It sounds the buzzer a number of times. Each time, it sets the pin high for the given number of milliseconds, sets it low, and waits 200 ms before the next beep.

void beep(int times, int ms) {
  for (int i = 0; i < times; i++) {
    digitalWrite(BUZZER, HIGH);
    delay(ms);
    digitalWrite(BUZZER, LOW);
    delay(200);
  }
}

setup sketch.ino

It sets the buzzer pin as an output and starts the serial port. It gives one 600 ms beep so you know the timer started. Then it saves the current millis as the start of the work period.

void setup() {
  pinMode(BUZZER, OUTPUT);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: Pomodoro timer, 25 minutes of work, then 5 of rest");
  beep(1, 600);
  started = millis();
}

loop sketch.ino

It works out the time elapsed in this period. When that reaches the period length, it flips between work and break, restarts the clock and beeps. Otherwise it prints the minutes left, once per minute.

void loop() {
  unsigned long period = working ? WORK_MS : BREAK_MS;
  unsigned long elapsed = millis() - started;
  if (elapsed >= period) {
    working = !working;
    started = millis();
    lastMinute = -1;
    if (working) beep(1, 600);
    else beep(2, 200);
    Serial.println(working ? "back to work" : "break time");
    return;
  }
  long minutesLeft = (period - elapsed + 59999UL) / 60000UL;
  if (minutesLeft != lastMinute) {
    lastMinute = minutesLeft;
    Serial.print(working ? "work: " : "break: ");
    Serial.print(minutesLeft);
    Serial.println(" min left");
  }
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The buzzer makes no sound. This project
    Check pin 9 goes to the buzzer's + leg (pin 1) and the other leg goes to GND.
  • Testing takes far too long. This project
    Make WORK_MS and BREAK_MS shorter while you test. They are in milliseconds.
  • The buzzer makes a constant tone, or you cannot change its pitch. Buzzer
    An active buzzer has its own oscillator, so it only beeps on and off. A passive buzzer needs tone() to set pitch. Check which type you have.
  • The buzzer is silent or very quiet. Buzzer
    Check polarity: the plus mark goes to the pin or supply. For loud buzzers, drive them through a transistor instead of straight from a pin.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

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