Creating an adjustable countdown timer with an Arduino using an I2C LCD, rotary encoder, pushbuttons with interrupt handling, two status LEDs, and an RTC module to control a relay requires careful management of interrupts for responsive button handling while maintaining accurate timing. It is an advanced project. The person going to create it requires to know few theories and previous work experience I2C LCD, interrupt and RTC module.
Here are related theories which can help you:
- How Interrupt Works
- Understanding Arduino Interrupts
- Read a Pushbutton with Arduino with Interrupts and Debounce
- What is I²C Protocol? Relevance of I²C in Arduino
For this project, we can not give you a Tinkercad simulation since RTC module, rotary encoder etc are absent in Tinkercad. The wiring is quite easy if you look at the sketch.
---
Components Needed For This Project
- Arduino board (e.g., Arduino Uno)
- I2C LCD display (16×2 or 20×4)
- Rotary encoder with push button
- Pushbuttons (for start/stop/reset)
- Two LEDs (for indicating timer running and countdown complete)
- RTC module (e.g., DS3231)
- Relay module (if controlling external devices)
- Breadboard
- Few jumper wires
- A computer running Arduino IDE

Suggested final design.
Image is from
https://forum.arduino.cc/t/ardunio-8x-relay-timer-with-i2c-20×4-lcd-rotary-encoder/77097
Libraries Required
Ensure you have the following libraries installed via the Arduino Library Manager:
- LiquidCrystal_I2C (for I2C LCD)
- RTClib (for RTC module)
- Encoder (for rotary encoder)
Required Code/Sketch
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | // under GNU GPL 3.0 License // Code written by Abhishek Ghosh // Website: https://thecustomizewindows.com // // #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <RTClib.h> #include <Encoder.h> // Define pins for rotary encoder #define ENCODER_PIN1 A0 #define ENCODER_PIN2 A1 #define BUTTON_PIN A2 // Define pins for pushbuttons using interrupts #define START_STOP_PIN 2 #define RESET_PIN 3 // Define pins for LEDs #define LED_RUNNING_PIN 4 #define LED_COMPLETE_PIN 5 // Initialize objects LiquidCrystal_I2C lcd(0x3F, 16, 2); // Adjust address and size as per your LCD RTC_DS3231 rtc; Encoder encoder(ENCODER_PIN1, ENCODER_PIN2); volatile bool startStopPressed = false; volatile bool resetPressed = false; // Timer variables int countdownMinutes = 0; unsigned long countdownEndTime = 0; bool timerRunning = false; void setup() { lcd.begin(); lcd.backlight(); pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(START_STOP_PIN, INPUT_PULLUP); pinMode(RESET_PIN, INPUT_PULLUP); pinMode(LED_RUNNING_PIN, OUTPUT); pinMode(LED_COMPLETE_PIN, OUTPUT); attachInterrupt(digitalPinToInterrupt(START_STOP_PIN), startStopInterrupt, FALLING); attachInterrupt(digitalPinToInterrupt(RESET_PIN), resetInterrupt, FALLING); // Initialize RTC if (!rtc.begin()) { lcd.print("RTC not found!"); while (1); } } void loop() { // Read rotary encoder long encoderValue = encoder.read(); if (encoderValue > 0) { countdownMinutes++; } else if (encoderValue < 0 && countdownMinutes > 0) { countdownMinutes--; } // Check if start/stop button was pressed if (startStopPressed) { startStopPressed = false; if (!timerRunning) { startTimer(); } else { stopTimer(); } } // Check if reset button was pressed if (resetPressed) { resetPressed = false; resetTimer(); } // Update LCD display updateDisplay(); // Check if timer has ended if (timerRunning && millis() >= countdownEndTime) { timerRunning = false; digitalWrite(LED_COMPLETE_PIN, HIGH); // Activate relay or perform any action when timer completes (optional) } } void startStopInterrupt() { startStopPressed = true; } void resetInterrupt() { resetPressed = true; } void startTimer() { countdownEndTime = millis() + (countdownMinutes * 60000); // Convert minutes to milliseconds timerRunning = true; digitalWrite(LED_RUNNING_PIN, HIGH); digitalWrite(LED_COMPLETE_PIN, LOW); } void stopTimer() { timerRunning = false; digitalWrite(LED_RUNNING_PIN, LOW); } void resetTimer() { countdownMinutes = 0; stopTimer(); digitalWrite(LED_COMPLETE_PIN, LOW); } void updateDisplay() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Set Timer: "); lcd.print(countdownMinutes); lcd.print(" min"); lcd.setCursor(0, 1); lcd.print("Status: "); if (timerRunning) { lcd.print("Running"); } else { lcd.print("Stopped"); } } |
Setup and Initialization:
Initializes all necessary components including the LCD, RTC, rotary encoder, pushbuttons, and LEDs. The interrupt service routines (ISRs) are attached to the start/stop and reset buttons to handle their actions immediately upon press.
Loop Function:
Reads input from the rotary encoder to adjust the countdown time.
Checks flags (startStopPressed and resetPressed) set by the interrupt service routines to execute corresponding functions (startTimer(), stopTimer(), resetTimer()).
Updates the LCD display with current timer status.
Interrupt Service Routines (ISRs):
startStopInterrupt(): Sets startStopPressed flag when the start/stop button is pressed.resetInterrupt(): Sets resetPressed flag when the reset button is pressed.
Timer Control Functions:
startTimer(): Calculates the end time of the countdown based on the set minutes and starts the countdown.stopTimer(): Stops the countdown timer.resetTimer(): Resets the countdown timer and stops it.
Display Update: Updates the LCD display to show the current countdown time and timer status (running or stopped).
Countdown Completion: When the countdown timer reaches zero (millis() reaches countdownEndTime), the timerRunning flag is set to false. Optionally, an action can be performed such as lighting up an LED (LED_COMPLETE_PIN) or triggering a relay.
Ensure the interrupt pins (START_STOP_PIN and RESET_PIN) are connected to the correct pins on your Arduino board and configured correctly in attachInterrupt.
Use volatile variables (startStopPressed and resetPressed) for flags accessed within both the main loop and interrupt service routines to ensure proper handling of shared data.
Customize LCD initialization (LiquidCrystal_I2C lcd(0x3F, 16, 2);) based on your LCD module’s address and size.
Extend functionality by adding more buttons for additional features like pause, resume, etc.
Incorporate the RTC module to synchronize the timer with real-time if precise timing is critical.
This sketch provides a foundation for building an adjustable countdown timer using interrupts for responsive button handling while maintaining accurate timing and display updates on an I2C LCD. Adjust and expand it further based on your specific project requirements and hardware setup.