#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
// Define LCD properties
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Define Keypad properties
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {3, 4, 5, 6}; // Connect to the row pinouts of the keypad
byte colPins[COLS] = {7, 8, 9, 10}; // Connect to the column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Define relay properties for 2-channel relay module
const int relayPin1 = 12; // Connect the first relay to pin 12
const int relayPin2 = 13; // Connect the second relay to pin 13
// Define variables to store timer values
int hours = 0;
int minutes = 0;
int seconds = 0;
int currentPosition = 0; // 0 - Hours, 1 - Minutes, 2 - Seconds
bool timerRunning = false;
// Define IR sensor pin and RPM calculation variables
const int IR_PIN = 2; // IR sensor input pin
volatile unsigned int counter = 0; // Counter variable for revolutions
unsigned long previousMillis = 0; // Variable to store previous time
unsigned int rpm = 0; // Variable to store RPM value
// Interrupt service routine for IR sensor
void IRinterrupt() {
counter++;
}
// Function to update timer on LCD
void updateTimerLCD() {
Serial.print("Updating Timer: ");
Serial.print(hours);
Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.println(seconds);
lcd.setCursor(0, 0);
lcd.print("Timer: ");
if (hours < 10) lcd.print("0");
lcd.print(hours);
lcd.print(":");
if (minutes < 10) lcd.print("0");
lcd.print(minutes);
lcd.print(":");
if (seconds < 10) lcd.print("0");
lcd.print(seconds);
lcd.print(" "); // Clear any previous digits
}
// Function to update RPM on LCD
void updateRPMLCD() {
Serial.print("Updating RPM: ");
Serial.println(rpm);
lcd.setCursor(0, 1);
lcd.print("RPM: ");
lcd.print(rpm);
lcd.print(" "); // Clear any previous digits
}
void setup() {
// Initialize Serial for debugging
Serial.begin(9600);
// Initialize LCD
lcd.init();
lcd.backlight();
// Initialize relay pins
pinMode(relayPin1, OUTPUT);
digitalWrite(relayPin1, HIGH); // Initially turn off the first relay
pinMode(relayPin2, OUTPUT);
digitalWrite(relayPin2, HIGH); // Initially turn off the second relay
// Display initial timer values
updateTimerLCD();
updateRPMLCD();
// Initialize IR sensor pin and attach the interrupt
pinMode(IR_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(IR_PIN), IRinterrupt, FALLING);
}
void loop() {
unsigned long currentMillis = millis();
// Check for keypad input
char key = keypad.getKey();
if (key) {
delay(50); // Debounce delay
if (timerRunning) {
if (key == '*') { // Allow only reset key
resetTimer();
updateTimerLCD();
}
} else {
handleKeypadInput(key);
updateTimerLCD();
}
}
// Handle timer countdown
static unsigned long lastTimerUpdate = 0;
if (timerRunning && currentMillis - lastTimerUpdate >= 1000) {
lastTimerUpdate = currentMillis;
updateCountdown();
updateTimerLCD();
}
// Calculate RPM every second
if (currentMillis - previousMillis >= 1000) {
calculateRPM();
updateRPMLCD();
}
}
// Function to handle keypad input
void handleKeypadInput(char key) {
switch (key) {
case 'A': // Set hours
currentPosition = 0;
break;
case 'B': // Set minutes
currentPosition = 1;
break;
case 'C': // Set seconds
currentPosition = 2;
break;
case 'D': // Start timer
if (hours > 0 || minutes > 0 || seconds > 0) {
timerRunning = true;
digitalWrite(relayPin1, LOW); // Energize the first relay when the timer starts
}
break;
case '#': // Delete last digit
deleteDigit();
break;
case '*': // Reset timer
resetTimer();
break;
default:
updateTimer(key);
break;
}
}
// Function to update the timer based on keypad input
void updateTimer(char key) {
int value = key - '0';
switch (currentPosition) {
case 0: // Update hours
hours = (hours * 10 + value) % 100; // Temporarily allow up to 99 to handle digit input
if (hours > 12) {
hours = 12; // Limit hours to a maximum of 12
}
break;
case 1: // Update minutes
minutes = (minutes * 10 + value) % 60;
break;
case 2: // Update seconds
seconds = (seconds * 10 + value) % 60;
break;
default:
break;
}
}
// Function to delete the last entered digit
void deleteDigit() {
switch (currentPosition) {
case 0: // Delete hours
hours /= 10;
break;
case 1: // Delete minutes
minutes /= 10;
break;
case 2: // Delete seconds
seconds /= 10;
break;
default:
break;
}
}
// Function to reset the timer
void resetTimer() {
hours = 0;
minutes = 0;
seconds = 0;
timerRunning = false;
digitalWrite(relayPin1, HIGH); // Ensure the first relay is de-energized
digitalWrite(relayPin2, HIGH); // Ensure the second relay is de-energized
updateTimerLCD(); // Update the timer display
}
// Function to handle the timer countdown
void updateCountdown() {
seconds--;
if (seconds < 0) {
seconds = 59;
minutes--;
if (minutes < 0) {
minutes = 59;
hours--;
if (hours < 0) {
// Timer has run out
timerRunning = false;
hours = 0;
minutes = 0;
seconds = 0;
handleTimerEnd();
}
}
}
}
// Function to handle the end of the timer
void handleTimerEnd() {
digitalWrite(relayPin1, HIGH); // De-energize the first relay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("STOPPED");
digitalWrite(relayPin2, LOW); // Energize the second relay
delay(5000); // Wait for 5 seconds
digitalWrite(relayPin2, HIGH); // De-energize the second relay
resetTimer();
}
// Function to calculate RPM
void calculateRPM() {
detachInterrupt(digitalPinToInterrupt(IR_PIN));
rpm = counter * 60; // Calculate RPM
counter = 0;
attachInterrupt(digitalPinToInterrupt(IR_PIN), IRinterrupt, FALLING);
previousMillis = millis();
}