Dual Mode Count-Up Timer And Stopwatch With Buzzer

Dual Mode Count-Up Timer And Stopwatch With Buzzer

HARDWARE REQUIRED:

  • PICUNO Microcontroller board
  • 2 × Single-Digit 7-Segment Display (Common Cathode)
  • 2 × Push Button
  • 2 × 10kΩ resistor (pull-down)
  • 7 × 220Ω resistors (Current limiting for LEDs)
  • 1 × Buzzer
  • Breadboard
  • Jumper wires
  • USB cable

DESCRIPTION:

This project implements a dual-mode count-up timer using the PICUNO board and two 7-segment displays. Users can either enter a specific countdown duration (1 to 60 seconds) or press the start button to activate stopwatch mode. In Countdown timer, when the target is reached, the buzzer sounds and the display begins blinking. In stopwatch mode, the timer goes from 0 to 60 and stops. A reset button brings the counter back to 0 and prompts for a new mode selection. The start button also works as a pause button where user can pause the time when needed.

CIRCUIT DIAGRAM:

Circuit Diagram
  • Connect the PICUNO board to the computer using a USB cable.
  • Connect segment pins A to G of both displays in parallel to GPIO 6 to 12 using 220Ω resistors.
  • Connect COM pin of left display to GPIO 13.
  • Connect COM pin of right display to GPIO 15.
  • Connect one side of both Start/Pause button and Reset button to 3.3 V.
  • Connect the other sides of the buttons to GPIO 16 and 18 respectively and connect a 10kΩ resistor from each GPIO to GND.
  • Connect the positive terminal of the buzzer to GPIO 19 and negative terminal of the buzzer to GND.

SCHEMATIC:

A → 220Ω → GPIO 6

B → 220Ω → GPIO 7

C → 220Ω → GPIO 8

D → 220Ω → GPIO 9

E → 220Ω → GPIO 10

F → 220Ω → GPIO 11

G → 220Ω → GPIO 12

COM (Left Display) → GPIO 13

COM (Right Display) → GPIO 15

Start/Pause Button one side → 3.3 V

Other side → GPIO 16 → 10kΩ → GND

Reset Button one side → 3.3 V

Other side → GPIO 18 → 10kΩ → GND

Buzzer +ve terminal → GPIO 19

Buzzer -ve terminal → GND

CODE -- C:

const int segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; // a-g
const int digit1 = 13;
const int digit2 = 15;
const int buzzerPin = 19;

const int startPauseButton = 16;
const int resetButton = 18;

int digits[10][7] = {
  {1,1,1,1,1,1,0}, {0,1,1,0,0,0,0}, {1,1,0,1,1,0,1},
  {1,1,1,1,0,0,1}, {0,1,1,0,0,1,1}, {1,0,1,1,0,1,1},
  {1,0,1,1,1,1,1}, {1,1,1,0,0,0,0}, {1,1,1,1,1,1,1},
  {1,1,1,1,0,1,1}
};

int counter = 0;
int buzzerTarget = 0;
bool running = false;
bool timeReached = false;
bool userHasSetTarget = false;

int lastStartState = LOW;
int lastResetState = LOW;
unsigned long lastTick = 0;
unsigned long blinkTick = 0;
bool blinkState = true;

void clearAll() {
  for (int i = 0; i < 7; i++) digitalWrite(segmentPins[i], LOW);
  digitalWrite(digit1, LOW);
  digitalWrite(digit2, LOW);
}

void setSegments(int number) {
  for (int i = 0; i < 7; i++) {
    digitalWrite(segmentPins[i], digits[number][i]);
  }
}

void showDigits(int tens, int ones) {
  clearAll();
  setSegments(tens);
  digitalWrite(digit1, HIGH);
  delayMicroseconds(1000);
  digitalWrite(digit1, LOW);

  clearAll();
  setSegments(ones);
  digitalWrite(digit2, HIGH);
  delayMicroseconds(1000);
  digitalWrite(digit2, LOW);
}

void requestUserTargetTime() {
  // Clear any garbage before prompting
  while (Serial.available()) Serial.read();

  Serial.println();
  Serial.println("Enter buzzer target time (1-60) or press Start to begin stopwatch:");

  buzzerTarget = 0;
  userHasSetTarget = false;

  while (true) {
    // Start button = enter stopwatch mode
    if (digitalRead(startPauseButton) == HIGH) {
      running = true;
      Serial.println("Stopwatch mode started.");
      return;
    }

    // Wait only for valid number input
    if (Serial.available()) {
      int t = Serial.parseInt();

      while (Serial.available()) Serial.read();

      if (t >= 1 && t <= 60) {
        buzzerTarget = t;
        userHasSetTarget = true;
        Serial.print("Buzzer set to ");
        Serial.print(buzzerTarget);
        Serial.println(" seconds.");
        return;
      } else {
        Serial.println("Invalid input. Try again.");
      }
    }
  }
}

void setup() {
  Serial.begin(9600);
  while (!Serial);

  for (int i = 0; i < 7; i++) pinMode(segmentPins[i], OUTPUT);
  pinMode(digit1, OUTPUT);
  pinMode(digit2, OUTPUT);
  pinMode(startPauseButton, INPUT);
  pinMode(resetButton, INPUT);
  pinMode(buzzerPin, OUTPUT);

  requestUserTargetTime();
}

void loop() {
  int tens = counter / 10;
  int ones = counter % 10;

  int resetState = digitalRead(resetButton);
  if (resetState == HIGH && lastResetState == LOW) {
    counter = 0;
    running = false;
    timeReached = false;
    digitalWrite(buzzerPin, LOW);
    Serial.println("Reset. Timer back to 00.");
    for (int i = 0; i < 50; i++) showDigits(0, 0);
    requestUserTargetTime();
    delay(200);
  }
  lastResetState = resetState;

  // Time reached - blink 60
  if (timeReached) {
    if (millis() - blinkTick >= 500) {
      blinkTick = millis();
      blinkState = !blinkState;
    }

    if (blinkState) {
      for (int i = 0; i < 10; i++) showDigits(tens, ones);
    } else {
      clearAll();
      delay(10);
    }
    return;
  }

  // Always show digits
  for (int i = 0; i < 10; i++) showDigits(tens, ones);

  // Start/Pause toggle
  int startState = digitalRead(startPauseButton);
  if (startState == HIGH && lastStartState == LOW && !timeReached) {
    running = !running;
    delay(200);
  }
  lastStartState = startState;

  // Timer count
  if (running && millis() - lastTick >= 1000) {
    counter++;
    lastTick = millis();

    if (userHasSetTarget && counter == buzzerTarget) {
      digitalWrite(buzzerPin, HIGH);
      running = false;
      timeReached = true;
    }

    if (counter >= 60) {
      counter = 60;
      running = false;
      timeReached = true;
    }
  }
}
requestUserTargetTime() - Prompts user for buzzer time or start input. If none is given, system waits.

showDigits() - Multiplexes both digits at high refresh rate to simulate simultaneous display.

counter == buzzerTarget - Triggers the buzzer and blinking logic when the set time is reached.

counter >= 60 - Stops counting and blinks 60 continuously.

reset button - Resets everything and displays 00 immediately.

startPauseButton - Toggles between counting and pausing.

CODE -- PYTHON:

from machine import Pin
import time
import sys
import select

segments = [Pin(i, Pin.OUT) for i in range(6, 13)]
digit1 = Pin(13, Pin.OUT)
digit2 = Pin(15, Pin.OUT)
buzzer = Pin(19, Pin.OUT)

start_button = Pin(16, Pin.IN)
reset_button = Pin(18, Pin.IN)

digits = [
    [1,1,1,1,1,1,0], [0,1,1,0,0,0,0], [1,1,0,1,1,0,1],
    [1,1,1,1,0,0,1], [0,1,1,0,0,1,1], [1,0,1,1,0,1,1],
    [1,0,1,1,1,1,1], [1,1,1,0,0,0,0], [1,1,1,1,1,1,1], [1,1,1,1,0,1,1]
]

counter = 0
buzzer_target = 0
running = False
time_reached = False
user_has_set_target = False

last_start_state = 0
last_reset_state = 0
last_tick = time.ticks_ms()
blink_tick = time.ticks_ms()
blink_state = True

def clear_all():
    for seg in segments:
        seg.value(0)
    digit1.value(0)
    digit2.value(0)

def set_segments(num):
    for i in range(7):
        segments[i].value(digits[num][i])

def show_digits(tens, ones):
    clear_all()
    set_segments(tens)
    digit1.value(1)
    time.sleep_us(1000)
    digit1.value(0)

    clear_all()
    set_segments(ones)
    digit2.value(1)
    time.sleep_us(1000)
    digit2.value(0)

def request_user_target_time():
    global buzzer_target, user_has_set_target, running
    # Clear input and prompt user
    # (Full implementation in documentation)

request_user_target_time()

while True:
    tens = counter // 10
    ones = counter % 10

    # Reset logic
    reset_state = reset_button.value()
    if reset_state == 1 and last_reset_state == 0:
        counter = 0
        running = False
        time_reached = False
        buzzer.value(0)
        print("Reset. Timer back to 00.")
        for _ in range(50): show_digits(0, 0)
        request_user_target_time()
        time.sleep_ms(200)
    last_reset_state = reset_state

    # Time reached blinking
    if time_reached:
        if time.ticks_diff(time.ticks_ms(), blink_tick) > 500:
            blink_tick = time.ticks_ms()
            blink_state = not blink_state
        if blink_state:
            for _ in range(10): show_digits(tens, ones)
        else:
            clear_all()
            time.sleep_ms(10)
        continue

    # Main timer logic continues...
segments = [Pin(i, Pin.OUT) for i in range(6, 13)] - Assigns segment pins A-G (GPIO 6 to 12) as output.

digits[] - List of segment bitmaps (7 elements per number) for digits 0-9.

set_segments(num) - Activates segments corresponding to the digit using the bitmap.

show_digits(tens, ones) - Handles multiplexing logic to alternately show tens and ones digits.

request_user_target_time() - Prompts the user for countdown time via serial OR lets user press Start for stopwatch mode.

reset_button logic - Resets the timer, stops buzzer, and re-prompts user for mode.

time_reached block - Blinks the final time when target or 60 sec is reached.

start_button logic - Toggles start/pause state when pressed.