Stopwatch Using ESP32 & LCD – Start Stop Reset Button
In this project, we have designed a simple stopwatch using an ESP32 and an I²C LCD display with start, stop, and reset functionalities. A stopwatch typically requires two primary modes—a start mode and a stop mode—to function properly. In our design, the start button is used to initiate or resume the timing, while the stop button pauses the timer. Additionally, if the stop button is pressed again when the timer is paused, the system resets, making it ready for a new timing session.
The ESP32 code uses the Arduino framework’s millis() function to accurately track time in milliseconds. This function allows the system to measure elapsed time with high precision, ensuring 100% accuracy for the stopwatch’s timing functions. Although the ESP32 is capable of displaying up to four digits after the decimal point, our implementation simplifies the output by showing only three digits for clarity on the LCD.
Earlier we made StopWatch using Arduino. You can also make Stopwatch Using Digit using Segment Display which is more cost effective and cheaper compared to LCD version.
Components Required
Following are the components that are required to make a stopwatch using the ESP32 Microcontroller. You can purchase all these components from the Amazon or AliExpress links.
| S.N. | Components Name | Quantity | Purchase Link |
|---|---|---|---|
| 1 | ESP32 Board | 1 | Amazon | AliExpress |
| 2 | 16x2 I2C LCD Display | 1 | Amazon | AliExpress |
| 3 | Push Button Switch | 2 | Amazon | AliExpress |
| 4 | Connecting Wires | 10 | Amazon | AliExpress |
| 5 | Breadboard | 1 | Amazon | AliExpress |
Circuit Diagram & Hardware Connections
Here is a connection diagram for a Stopwatch designed using an ESP32, a 16×2 I²C LCD Display, and two push-button switches.
Connect the push buttons to the ESP32 digital pins 18 and 19. Digital pin 18 is used for the start button, while digital pin 19 is used for the stop or reset button.
For the 16×2 I²C LCD Display, the wiring is simplified compared to a parallel connection. Connect the LCD’s SDA pin to the ESP32’s SDA line (commonly GPIO21) and the SCL pin to the ESP32’s SCL line (commonly GPIO22). Also, connect the LCD’s VCC pin to the 5V supply and the GND pin to the ESP32’s ground.
Assemble the circuit on a breadboard as shown in the image above, ensuring that all connections are secure for proper operation of the stopwatch.
Source Code/Program:
Here is the C++ code that functions as a ESP32 Simple Stopwatch that allows the user to start, pause, and reset timing events with two physical buttons. For this code, you need to install I2C LCD Library for LCD Display.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#include <Wire.h> #include <LiquidCrystal_I2C.h> // Initialize the I²C LCD at address 0x27 with 16 columns and 2 rows. LiquidCrystal_I2C lcd(0x27, 16, 2); // Button pin definitions for the ESP32. const int startButtonPin = 18; // Start/resume button const int stopButtonPin = 19; // Stop/pause/reset button // Timer states. enum TimerState { IDLE, RUNNING, PAUSED }; TimerState timerState = IDLE; unsigned long startTime = 0; // Time when the timer is started/resumed. unsigned long elapsedTime = 0; // Accumulated elapsed time in milliseconds. void setup() { lcd.init(); lcd.backlight(); lcd.clear(); Serial.begin(115200); // Setup button pins with internal pull-ups. pinMode(startButtonPin, INPUT_PULLUP); pinMode(stopButtonPin, INPUT_PULLUP); } void loop() { switch(timerState) { case IDLE: // In the IDLE state, display "Press Start" on row 0. lcd.clear(); lcd.setCursor(0, 0); lcd.print("Press Start"); lcd.setCursor(0, 1); lcd.print(" "); // Clear row 1. // Wait for the start button press. if (digitalRead(startButtonPin) == LOW) { delay(50); // Debounce delay. while (digitalRead(startButtonPin) == LOW) { delay(10); // Wait for button release. } // Start the timer. startTime = millis(); elapsedTime = 0; timerState = RUNNING; lcd.clear(); } break; case RUNNING: // Update the elapsed time continuously. elapsedTime = millis() - startTime; displayTime(elapsedTime); // If the stop button is pressed, pause the timer. if (digitalRead(stopButtonPin) == LOW) { delay(50); // Debounce delay. while (digitalRead(stopButtonPin) == LOW) { delay(10); // Wait for button release. } // Capture the elapsed time and change state to PAUSED. elapsedTime = millis() - startTime; timerState = PAUSED; } break; case PAUSED: // Display the paused time. displayTime(elapsedTime); // Check for resume or reset: // - Start button resumes the timer. // - Stop button resets the timer. if (digitalRead(startButtonPin) == LOW) { delay(50); while (digitalRead(startButtonPin) == LOW) { delay(10); } // Resume the timer by adjusting startTime. startTime = millis() - elapsedTime; timerState = RUNNING; lcd.clear(); } else if (digitalRead(stopButtonPin) == LOW) { delay(50); while (digitalRead(stopButtonPin) == LOW) { delay(10); } // Reset the timer (return to IDLE). timerState = IDLE; lcd.clear(); } break; } } // Helper function to format and display the elapsed time on row 0. void displayTime(unsigned long timeInMillis) { // Calculate minutes, seconds, and milliseconds. unsigned int minutes = timeInMillis / 60000; unsigned int seconds = (timeInMillis % 60000) / 1000; unsigned int milliseconds = timeInMillis % 1000; // Create a formatted string "MM:SS:MMM" (always 9 characters). char timeStr[10]; // 9 characters + null terminator. sprintf(timeStr, "%02u:%02u:%03u", minutes, seconds, milliseconds); // Display the formatted time on the first row. lcd.setCursor(0, 0); lcd.print(timeStr); // Optional: output the time to the Serial Monitor. Serial.println(timeStr); } |
Copy the above code to the Arduino IDE editor window. Select the ESP32 board and the COM port. Click on upload button to upload the code.
Working of ESP32 StopWatch Timer
This code implements a basic stopwatch using an ESP32 Microcontroller connected to a standard I2C LCD. It defines three states—IDLE, RUNNING, and STOPPED—using an enumerated type to manage the stopwatch’s behavior. Two buttons are used for user interaction: one to start the timer and one to stop (and later reset) it.
In the IDLE state, the display shows the message “Press Start” and waits for the start button to be pressed.
When the button is activated (with a debounce delay to ensure stable input), the program clears the display, records the current time using the millis() function, and transitions to the RUNNING state. In this state, the stopwatch continuously calculates the elapsed time by subtracting the recorded start time from the current time, formatting this elapsed time into minutes, seconds, and milliseconds, and then displaying it on the LCD.
While in the RUNNING state, if the stop button is pressed, the program again debounces the input, calculates the final elapsed time, and then transitions to the STOPPED state. If start button is pressed here, the timer resumes from the previous value.
In the STOPPED state, the final time remains displayed on the LCD. If the stop button is pressed once more in this state, the code resets the system back to the IDLE state by clearing the display and showing the “Press Start” message again, readying the stopwatch for another timing session.
This is how you can make a stopwatch using ESP32 and 16×2 I2C LCD Display.









