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:
- 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;
}
}
}
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.
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...
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.
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.