#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
const byte ROW_NUM = 4; // Four rows
const byte COLUMN_NUM = 4; // Four columns
char keys[ROW_NUM][COLUMN_NUM] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte pin_rows[ROW_NUM] = {9, 8, 7, 6}; // Connect to the row pinouts of the keypad
byte pin_column[COLUMN_NUM] = {5, 4, 3, 2}; // Connect to the column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);
int numUsers = 0;
unsigned long elapsedTimes[8] = {0}; // Store elapsed time for each user
unsigned long timerStartTimes[8]; // Store the start time for each user
bool timersRunning[8] = {false}; // Store the running state for each user
unsigned long lastUpdateTime = 0;
unsigned long lastMillisecondUpdate = 0; // Track the last update time for milliseconds
const unsigned long updateInterval = 50; // Update every 50 ms
const unsigned long millisecondInterval = 50; // Update milliseconds every 50 ms
bool userSelectionInProgress; // Flag to prevent toggling during user selection
bool resetButtonPressed = false; // Declare the resetButtonPressed flag globally
void setup() {
lcd.init(); // Initialize the I2C LCD
lcd.backlight(); // Turn on the backlight
lcd.setCursor(0, 0); // Set the cursor to the first row, first column
Serial.begin(9600); // Initialize Serial for debugging
// Display "How many users?" prompt on the LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("How many users?");
lcd.setCursor(0, 1);
lcd.print("{1-8}");
}
void loop() {
char key = keypad.getKey(); // Get the pressed key from the keypad
if (keypad.getState() == PRESSED) { // Check if a key is pressed and handle only when the state is PRESSED
if (key >= '1' && key <= '8' && numUsers ==0 ) { // Handle user selection (1-8)
userSelectionInProgress = true;
numUsers = key - '0'; // Set number of users (1 to 8)
// Display selected number of users on the LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Selected: " + String(numUsers) + " users");
delay(500); // Delay to give time to read the LCD
lcd.clear();
for (int i =0 ; i < 8; i++) { // Ensure all timers are stopped when users are selected
timersRunning[i] = false; // Stop all timers
elapsedTimes[i] =0 ; // Reset elapsed times
}
}
if (key == '9') { // Handle "All" button press (Button 9)
if (resetButtonPressed) {
resetAllTimers(); // Reset all timers if reset button is also pressed
resetButtonPressed = false; // Reset the flag after reset
} else {
toggleAllTimers(); // Toggle all timers when button 9 is pressed without reset
}
}
if (key == '0') { // Handle "Reset" button press (Button )
resetButtonPressed = true; // Set flag indicating Reset button is pressed
}
if (key >= '1' && key <= '8') { // Handle user button presses (1-8) to start/stop their respective timers
if (userSelectionInProgress)
return;
int userIndex = key - '1'; // Get the user index (-7)
if (timersRunning[userIndex]) {
timersRunning[userIndex] = false; // Stop the timer
elapsedTimes[userIndex] += millis() - timerStartTimes[userIndex]; // Add elapsed time
} else {
timerStartTimes[userIndex] = millis(); // Start the timer
timersRunning[userIndex] = true;
}
if (resetButtonPressed) { // If the reset button () was pressed while a user button was pressed, reset that user's timer
resetUserTimer(userIndex); // Reset the selected user's timer
resetButtonPressed = false; // Reset the flag after the reset is performed
}
}
}
if (millis() - lastUpdateTime >= updateInterval) { // Update the screen only at controlled intervals
lastUpdateTime = millis();
updateTimers(); // Update the timer display
}
userSelectionInProgress = false; // Reset the flag once the selection is complete
}
void updateTimers() { // Function to update the timers on the screen
for (int i = 0; i < numUsers; i++) {
unsigned long elapsedTime = elapsedTimes[i];
if (timersRunning[i]) {
elapsedTime = millis() - timerStartTimes[i] + elapsedTimes[i]; // Calculate running timer
}
int minutes = (elapsedTime / 1000) / 60;
int seconds = (elapsedTime / 1000) % 60;
int milliseconds = elapsedTime % 1000;
// Update the LCD display for each user
int row = i % 4; // 4 users per row
int col = (i / 4) * 11; // First 4 users in column 0, second 4 users in column 10
lcd.setCursor(col, row);
lcd.print(i + 1); // Print user number
lcd.print(")"); // Divider
lcd.print(formatTime(minutes)); // Print timer formatted as 00:00:0
lcd.print(":");
lcd.print(formatTime(seconds));
lcd.print(":");
lcd.print(milliseconds / 100); // Display only the first digit of milliseconds (as 00:00:0)
}
}
String formatTime(int timeUnit) { // Single formatTime function
if (timeUnit < 10) {
return "0" + String(timeUnit); // Add leading zero if timeUnit < 10
} else return String(timeUnit);
}
void resetUserTimer(int userIndex) { // Function to handle user timer reset
elapsedTimes[userIndex] = 0; // Reset the user's elapsed time
timersRunning[userIndex] = false; // Stop the timer
timerStartTimes[userIndex] = millis(); // Set the start time to current time
updateTimers(); // Update the timer display after resetting
}
void resetAllTimers() { // Function to reset all timers
for (int i = 0; i < numUsers; i++) {
elapsedTimes[i] = 0; // Reset the elapsed time for each user
timersRunning[i] = false; // Stop the timer
timerStartTimes[i] = millis(); // Set the start time to current time
}
}
void toggleAllTimers() { // Function to toggle all timers (start any stopped timers or stop all if all are running)
bool allRunning = true;
// Check if all timers are running
for (int i = 0; i < numUsers; i++) {
if (!timersRunning[i]) {
allRunning = false;
break;
}
}
if (allRunning) { // If all timers are running, stop all of them
for (int i = 0; i < numUsers; i++) {
timersRunning[i] = false;
elapsedTimes[i] += millis() - timerStartTimes[i]; // Add elapsed time before stopping
}
} else { // If not all timers are running, start the stopped ones
for (int i = 0; i < numUsers; i++) {
if (!timersRunning[i]) {
timerStartTimes[i] = millis(); // Start the timer
timersRunning[i] = true;
}
}
}
}