#include <Arduino.h>
#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);
bool backlightOn = true;
//========================================== decleration ===========================================
// Button Pins
const int enterButton = 8; // Enter/Select button
const int upButton = 7; // Up button
const int downButton = 6; // Down button
const int startButton = 5; // Start button
const int counterButton = 4; //ShowCounter/Reset Counter button.
const int setupprg = 3;
// LED Pins for 16 LEDs
const int ledPins[] = {9,10,11,12,13};// 18, 19, 20};//,21,22,23, 31, 30, 29, 28,27};// 9, 10, 11, 18, 33, 34, 35, 36, 37, 38, 39};
const int buzzerPin = 14;
// Number of Program Levels
const int programLevels = 10;
const int ledCount = 14; //Total Task
const String ledNames[14] =
{
"F", "R", "A", "P1", "P2", "P3", "S1", "S2A",
"S2", "CW", "HWW", "CWW", "SC1", "SC2"
};
int counter = 0; // New counter variable
// EEPROM Addresses
const int eepromBaseAddress = 100; // Starting address for EEPROM storage
// LED start/stop times for 10 Program Levels, each with 16 LEDs
int ledStartTimes[programLevels][ledCount] = {0}; // Start times for each LED in each Program Level
int ledStopTimes[programLevels][ledCount] = {0}; // Stop times for each LED in each Program Level
int cycleTimes[programLevels] = {0}; // Cycle times for each Program Level
int ledOffTime[programLevels]= {0};
// Number of LEDs for each Program Level
int numLEDsInProgram[programLevels] = {ledCount}; // Default to maximum for each program
// Main Timer
unsigned long mainTimer = 0;
unsigned long timerStart = 0;
// Menu Control Variables
int currentProgram = 0; // Current Program Level
int currentLED = 0; // Current LED task in the selected Program Level
int currentSetting = -1; // 0: Program Number, 1: Cycle Time, 2: Number of LEDs, 3: Set Start Time, 4: Set Stop Time
bool settingMode = false;
bool isRunning = false;
bool showCounterMode = false; // To display counter on short press
// Button debounce timing
unsigned long lastButtonPress = 0;
const unsigned long debounceDelay = 200; // Debounce delay (ms)
const unsigned long longPressDuration = 4000; // 4 seconds for reset
// Password mechanism variables
const int passwordLength = 4;
const int correctPassword[passwordLength] = {1, 2, 3, 4}; // Default password: 1234
int enteredPassword[passwordLength] = {0}; // To store user input
int currentDigit = 0; // Track the current digit being entered
bool passwordMode = false; // Track if password entry mode is active
unsigned long passwordPressStartTime = 0; // Track button press duration
//unsigned long pressStartTime=0;
bool isSettingPassGranted=false;
bool isActivated=false;
int lastProgram =-1;
int lastCycleTime = -1;
int lastNumLEDs = -1;
int lastCounter =-1;
int lastDisplayedSetting = -1;
unsigned long blinkStartTime = 0;
bool cursorVisible = true;
const int cursorBlinkInterval = 500; // Cursor blink interval in milliseconds
bool isOffTimePassed=false;
//========================================== declera All functions =================================
int GetSuPasswordInEEPROM();
void loadSettings();
void GetCounterValueFromEEPROM();
void handleButtons();
void handlePassword();
bool checkPassword();
void updatePasswordDisplay();
void updateDisplay();
void displayElapsedTime();
void controlLEDs();
void saveSettings();
void loadSettings();
void SaveCounterValueInEEPROM();
void GetCounterValueFromEEPROM();
void saveSettings2() ;
//========================================== All functions =========================================
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <class T> int EEPROM_readAnything(int ee, T& value)
{
byte* p = (byte*)(void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
void clearEEPROM()
{
for (int i = 0; i < EEPROM.length(); i++)
{
EEPROM.update(i, 2); // Set all EEPROM locations to 0xFF
}
}
int GetSuPasswordInEEPROM()
{
//EEPROM_writeAnything(0, 1200);
//EEPROM_writeAnything(5, 2000);
// int my_value_1, my_value_2;
// EEPROM_readAnything(0, my_value_1);
//EEPROM_readAnything(5, my_value_2);
int address=10;
//byte highByteValue = EEPROM.read(address); // Read high byte of cycle time
//address++;
//byte lowByteValue = EEPROM.read(address); // Read low byte of cycle time
//address++;
//int pass = (highByteValue << 8) | lowByteValue; // Combine into full cycle time
int pass = EEPROM_readAnything(address, pass);
//int pass = EEPROM.read(address);
// address++;
return pass;
}
void updatePasswordDisplay()
{
// Update blink timer and toggle cursor visibility
if (millis() - blinkStartTime >= cursorBlinkInterval) {
cursorVisible = !cursorVisible; // Toggle cursor visibility
blinkStartTime = millis(); // Reset timer
}
lcd.setCursor(0, 1);
lcd.print("Pass: ");
for (int i = 0; i < passwordLength; i++) {
if (i == currentDigit && cursorVisible) {
lcd.print("_"); // Show cursor at the current position
} else {
lcd.print(enteredPassword[i]); // Show entered digit
}
}
}
void handlePassword()
{
// Check if both buttons are pressed
// if (digitalRead(upButton) == LOW && digitalRead(downButton) == LOW)
if (digitalRead(setupprg) == LOW )
{
if (passwordPressStartTime == 0)
{
passwordPressStartTime = millis(); // Start timing
}
// Check if buttons are held for 3 seconds to enter password mode
if (millis() - passwordPressStartTime >= 3000)
{
passwordMode = true; // Activate password mode
currentDigit = 0; // Reset digit index
lcd.clear();
lcd.print("Enter Password");
passwordPressStartTime = 0; // Reset timing
blinkStartTime = millis(); // Start blink timer
}
} else {
if (passwordPressStartTime != 0)
{
passwordPressStartTime = 0; // Reset if buttons are released
}
}
// If in password mode
if (passwordMode)
{ updatePasswordDisplay();
// Increment current digit with up button
if (digitalRead(upButton) == LOW)
{
enteredPassword[currentDigit] = (enteredPassword[currentDigit] + 1) % 10; // Increment digit
updatePasswordDisplay(); // Update display
delay(100); // Simple debounce delay
}
// Move to the next digit with down button
if (digitalRead(downButton) == LOW)
{
currentDigit = (currentDigit + 1) % passwordLength; // Move to the next digit
updatePasswordDisplay(); // Update display
delay(100); // Simple debounce delay
}
// Handle enter button to submit password
if (digitalRead(enterButton) == LOW)
{
// Check the entered password
if (checkPassword())
{
lcd.clear();
lcd.print("Access Granted");
delay(1000); // Show message for a second
lcd.clear();
settingMode = true;
passwordMode = false; // Exit password mode
currentSetting = 0;
} else
{
lcd.clear();
lcd.print("Access Denied");
delay(1000); // Show message for a second
lcd.clear();
lcd.print("Try Again");
}
// Reset enteredPassword
for (int i = 0; i < passwordLength; i++) {
enteredPassword[i] = 0;
}
currentDigit = 0; // Reset digit index
delay(100); // Simple debounce delay
}
}
}
bool checkPassword()
{
for (int i = 0; i < passwordLength; i++)
{
if (enteredPassword[i] != correctPassword[i])
{
return false; // Password is incorrect
}
}
return true; // Password is correct
}
void updateDisplay()
{
//lcd.clear();
//lcd.setCursor(0, 0);
if (settingMode==false)
{
//======================================================
if (currentProgram != lastProgram || cycleTimes[currentProgram]!= lastCycleTime || numLEDsInProgram[currentProgram]!= lastNumLEDs || counter != lastCounter)
{
//lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Prog ");
lcd.print(currentProgram); // Program Level
lcd.print(" CYT:");
lcd.print(cycleTimes[currentProgram]); // Cycle time
lcd.setCursor(0, 1); // Move to the second line for LED information
lcd.print("Relay:");
lcd.print(numLEDsInProgram[currentProgram]); // Display number of LEDs
lcd.print(" CT:");
lcd.print(counter);
lastProgram = currentProgram;
lastCycleTime = cycleTimes[currentProgram];
lastNumLEDs = numLEDsInProgram[currentProgram];
lastCounter = counter;
}
//======================================================
}
else
{
//lcd.clear();
lcd.setCursor(0, 0);
switch (currentSetting)
{
case 0:
lcd.print("Prog No: ");
lcd.setCursor(0, 1);
lcd.print(currentProgram);
break;
case 1:
lcd.print("Cycle Time: ");
lcd.setCursor(0, 1);
lcd.print(cycleTimes[currentProgram]);
break;
case 2:
lcd.print("Total Relay: ");
lcd.setCursor(0, 1);
lcd.print(numLEDsInProgram[currentProgram]);
break;
case 3:
// lcd.print("OUT ");
lcd.print(ledNames[currentLED]);
lcd.print(" StartTime:");
lcd.setCursor(0, 1);
lcd.print(ledStartTimes[currentProgram][currentLED]);
break;
case 4:
//lcd.print("OUT ");
lcd.print(ledNames[currentLED]);
if(currentLED==0 || currentLED==1)
lcd.print(" O/f Interval:");
else
lcd.print(" StopTime:");
lcd.setCursor(0, 1);
lcd.print(ledStopTimes[currentProgram][currentLED]);
break;
case 5:
// lcd.print("OUT ");
lcd.print(ledNames[currentLED]);
lcd.print(" OffTime:");
lcd.setCursor(0, 1);
lcd.print(ledOffTime[currentProgram]);
break;
}
}
}
void displayElapsedTime()
{
static unsigned long lastElapsedTime = -1;
unsigned long elapsedTime = (millis() - timerStart) / 1000;
if (elapsedTime!= lastElapsedTime)
{
//lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Run Time: ");
lcd.print(elapsedTime);
lcd.print("s");
lastElapsedTime = elapsedTime;
}
lcd.setCursor(0, 1);
lcd.print("Cycle Time: ");
lcd.print(cycleTimes[currentProgram]);
lcd.print(" ");
}
void controlLEDs()
{
unsigned long currentTime = (millis() - timerStart) / 1000; // Current elapsed time in seconds
// Control LED 1 and LED 2 with special alternating behavior
for (int i = 0; i < numLEDsInProgram[currentProgram]; i++)
{
if (i < 2)
{ // Special handling for the first two LEDs
if (currentTime >= ledStartTimes[currentProgram][i])
{
// Calculate ON/OFF duration based on stop time
unsigned long durationOn = ledStopTimes[currentProgram][i]; // Duration ON equals stop time
unsigned long durationOff = durationOn + 10; // Duration OFF includes extra 10 seconds
unsigned long cycleDuration = durationOn + durationOff; // Total cycle duration (ON + OFF)
unsigned long elapsedCycleTime = currentTime - ledStartTimes[currentProgram][i];
unsigned long timeInCurrentCycle = elapsedCycleTime % cycleDuration; // Time within the current ON-OFF cycle
// Determine ON/OFF state based on the elapsed cycle time
if (timeInCurrentCycle < durationOn)
{
digitalWrite(ledPins[i], HIGH); // Turn LED ON for the stop time duration
}
else
{
digitalWrite(ledPins[i], LOW); // Turn LED OFF for (stop time + 10 seconds)
}
}
else
{
digitalWrite(ledPins[i], LOW); // Ensure LED is OFF before start time
}
}
else if (i == 2)
{ // Special handling for LED 3 with OffTime
if (currentTime >= ledStartTimes[currentProgram][i] && currentTime < ledStopTimes[currentProgram][i])
{
digitalWrite(ledPins[i], HIGH); // Turn LED ON
}
else if( currentTime >= ledStopTimes[currentProgram][i])
{
if(!isOffTimePassed)
{
isOffTimePassed=true;
digitalWrite(ledPins[i], LOW); // Turn LED OFF
}
else
{
if (currentTime >= ledStopTimes[currentProgram][i]+ledOffTime[currentProgram])
{
digitalWrite(ledPins[i], HIGH); //Again Turn LED ON after offtime
}
}
}
}
else
{ // For other LEDs (3 to 16), regular behavior
if (currentTime >= ledStartTimes[currentProgram][i] && currentTime < ledStopTimes[currentProgram][i])
{
digitalWrite(ledPins[i], HIGH); // Turn LED ON
}
else
{
digitalWrite(ledPins[i], LOW); // Turn LED OFF
}
}
}
// Stop the program if the cycle time is exceeded
if (currentTime >= cycleTimes[currentProgram])
{
isRunning = false;
counter++; // Increment counter at the end of each cycle
SaveCounterValueInEEPROM();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Cycle End");
for (int l = 0; l < ledCount; l++) //resetting all led to OFF
digitalWrite(ledPins[l], LOW);
currentSetting = 0;
isOffTimePassed=false;
digitalWrite(buzzerPin, HIGH);
delay(5000); // Keep the buzzer on for 5 seconds
digitalWrite(buzzerPin, LOW);
}
}
void saveSettings()
{
int address = eepromBaseAddress;
for (int p = 0; p < programLevels; p++)
{
// Store LED Start/Stop Times
for (int l = 0; l < ledCount; l++)
{
EEPROM.update(address, ledStartTimes[p][l]); // Save LED start time
address++;
EEPROM.update(address, ledStopTimes[p][l]); // Save LED stop time
address++;
}
// Store Cycle Time (as 2 bytes)
EEPROM.update(address, highByte(cycleTimes[p])); // Save high byte of cycle time
address++;
EEPROM.update(address, lowByte(cycleTimes[p])); // Save low byte of cycle time
address++;
// Store Number of LEDs
EEPROM.update(address, numLEDsInProgram[p]); // Save number of LEDs
address++;
EEPROM.update(address, ledOffTime[p]); //storing LED3 OffTime
address++;
}
// Store the current program index
EEPROM.update(address, currentProgram);
}
void loadSettings()
{
int address = eepromBaseAddress;
for (int p = 0; p < programLevels; p++)
{
// Load LED Start/Stop Times
for (int l = 0; l < ledCount; l++)
{
ledStartTimes[p][l] = EEPROM.read(address); // Read LED start time
address++;
ledStopTimes[p][l] = EEPROM.read(address); // Read LED stop time
address++;
}
// Load Cycle Time (from 2 bytes)
//byte highByteValue = EEPROM.read(address); // Read high byte of cycle time
//address++;
//byte lowByteValue = EEPROM.read(address); // Read low byte of cycle time
//address++;
//cycleTimes[p] = (highByteValue << 8) | lowByteValue; // Combine into full cycle time
cycleTimes[p] = EEPROM.read(address);
address++;
// Load Number of LEDs
numLEDsInProgram[p] = EEPROM.read(address); // Read number of LEDs
address++;
ledOffTime[p] = EEPROM.read(address); // Read LED3 OffTime
address++;
}
// Load the current program index
currentProgram = EEPROM.read(address);
Serial.println("Pre current Program");
Serial.println(currentProgram);
if (currentProgram < 0 || currentProgram >= programLevels)
{
currentProgram = 0; // Reset to 0 if invalid
}
Serial.println("Post current Program");
Serial.println(currentProgram);
}
void SaveCounterValueInEEPROM()
{
EEPROM.update(0, counter);
}
void GetCounterValueFromEEPROM()
{
counter = EEPROM.read(0);
}
void handleButtons()
{
if (millis() - lastButtonPress < debounceDelay) return; // Debounce check
if (settingMode==true)
{
if (digitalRead(enterButton) == LOW)
{
lcd.clear();
lastButtonPress = millis();
if(currentSetting>5 || (currentSetting==5 && currentLED!=2))
{
currentLED++;
currentSetting=3;
}
if(currentLED==numLEDsInProgram[currentProgram])
{
currentSetting=0;
currentLED=0;
settingMode=false;
}
currentSetting++;
saveSettings();
}
if (digitalRead(upButton) == LOW)
{
lastButtonPress = millis();
lcd.clear();
switch (currentSetting)
{
case 0: // Program Number
currentProgram++; // Move to next program level
if (currentProgram >= programLevels)
{
currentProgram = 0; // Loop back to first program
}
break;
case 1: // Cycle Time
cycleTimes[currentProgram]++;
break;
case 2: // Number of LEDs (limit to 16)
numLEDsInProgram[currentProgram]++;
if (numLEDsInProgram[currentProgram] > ledCount)
{
numLEDsInProgram[currentProgram] = 1; // Loop back to first LED count
}
break;
case 3: // Set Start Time for current LED
ledStartTimes[currentProgram][currentLED]++;
break;
case 4: // Set Stop Time for current LED
ledStopTimes[currentProgram][currentLED]++;
break;
case 5: // Set Stop Time for current LED
ledOffTime[currentProgram]++;
break;
}
// Save settings to EEPROM after changing program level
saveSettings();
}
if (digitalRead(downButton) == LOW)
{
lastButtonPress = millis();
lcd.clear();
switch (currentSetting)
{
case 0: // Program Number
currentProgram--; // Move to previous program level
if (currentProgram < 0) currentProgram = programLevels - 1; // Loop back to last program
break;
case 1: // Cycle Time
cycleTimes[currentProgram]--;
break;
case 2: // Number of LEDs (limit to 16)
numLEDsInProgram[currentProgram]--;
if (numLEDsInProgram[currentProgram] < 1) numLEDsInProgram[currentProgram] = ledCount; // Loop back to max LED count
break;
case 3: // Set Start Time for current LED
ledStartTimes[currentProgram][currentLED]--;
break;
case 4: // Set Stop Time for current LED
ledStopTimes[currentProgram][currentLED]--;
break;
case 5: // Set Stop Time for current LED
ledOffTime[currentProgram]--;
break;
}
// Save settings to EEPROM after changing program level
saveSettings();
}
if (digitalRead(startButton) == LOW)
{
lcd.clear();
settingMode=false;
}
if (digitalRead(counterButton) == LOW)
{
lcd.clear();
lastButtonPress = millis();
unsigned long pressStartTime = millis();
// Detect long press (4 seconds)
while (digitalRead(counterButton) == LOW)
{
if (millis() - pressStartTime >= longPressDuration)
{
counter = 0; // Reset counter
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Counter Reset");
delay(1000); // Show reset message briefly
return;
}
}
}
}
if (settingMode==false)
{
if (digitalRead(startButton) == LOW)
{
lcd.clear();
if (!isRunning) // Check if not already running
{
lastButtonPress = millis();
isRunning = true; // Start the timer
// lcd.clear();
timerStart = millis(); // Record start time
}
//comment start toggle
// lastButtonPress = millis();
// isRunning = !isRunning; // Toggle running state
// if (isRunning)
// {
// timerStart = millis(); // Record start time
// }
//comment end
}
}
}
void saveSettings2()
{
for (int i = 0; i < EEPROM.length(); i++)
{
EEPROM.update(i, 2); // Set all EEPROM locations to 0xFF
}
}
//========================================== void setup() ==========================================
void setup()
{
Serial.begin(9600);
lcd.init(); // initialize the i2c lcd
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("LED Timer Setup");
// Setup button pins
pinMode(enterButton, INPUT);
pinMode(upButton, INPUT);
pinMode(downButton, INPUT);
pinMode(startButton, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(setupprg, INPUT);
// Setup LED pins as OUTPUT
for (int i = 0; i < ledCount; i++)
{
pinMode(ledPins[i], OUTPUT);
}
saveSettings2() ;
int adminpass=GetSuPasswordInEEPROM(); //Getting admin pass from eeprom
// ////Serial.println("adminpass ");
// ////Serial.println(adminpass);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(adminpass);
delay(2000);
if(adminpass==2) // matching with admin pass
{ //////Serial.println("match");
isActivated=true;
//Load settings from EEPROM
loadSettings();
GetCounterValueFromEEPROM();
}
else
{
// ////Serial.println("not matched");
isActivated=false;
}
delay(100);
}
//========================================== void Loop() ===========================================
void loop()
{
handlePassword();
handleButtons() ;
if(!isRunning)
{
updateDisplay();
}
else
{
displayElapsedTime();
controlLEDs();
}
delay(50);
}
//========================================== End of void Loop() ====================================