/*
Forum: https://forum.arduino.cc/t/need-help-with-code/1201805
Wokwi: https://wokwi.com/projects/384551283449602049
*/
//“S”: Starts all delays for the solenoids.
//“6”: Moves the cursor up.
//“4”: Moves the cursor down.
//“8”: Toggles the edit mode.
//“EEPROM RESET”: Resets all EEPROM values to zero.
//“DEFAULT”: Sets the Offsets and Intervals to their default values and saves them to the EEPROM.
//--------------------------------------------------------------------------------------------------------------------------------------------------------
//
// ______ _ _____ _ _ __ ___ _ _
// | ____| | | / ____| | | | | /_ | / _ \ | | | |
// | |__ | | ___ __ __ | | ___ _ __ | |_ _ __ ___ | | __ __ | | | | | | | |__ ___ | |_ __ _
// | __| | | / _ \ \ \ /\ / / | | / _ \ | '_ \ | __| | '__| / _ \ | | \ \ / / | | | | | | | '_ \ / _ \ | __| / _` |
// | | | | | (_) | \ V V / | |____ | (_) | | | | | | |_ | | | (_) | | | \ V / | | _ | |_| | | |_) | | __/ | |_ | (_| |
// |_| |_| \___/ \_/\_/ \_____| \___/ |_| |_| \__| |_| \___/ |_| \_/ |_| (_) \___/ |_.__/ \___| \__| \__,_|
//
//
//
// SSSSSSSSSSSSSSS EEEEEEEEEEEEEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFF
// SS:::::::::::::::S E::::::::::::::::::::E F::::::::::::::::::::F
// S:::::SSSSSS::::::S E::::::::::::::::::::E F::::::::::::::::::::F
// S:::::S SSSSSSS EE::::::EEEEEEEEE::::E FF::::::FFFFFFFFF::::F
// S:::::S E:::::E EEEEEE F:::::F FFFFFF
// S:::::S E:::::E F:::::F
// S::::SSSS E::::::EEEEEEEEEE F::::::FFFFFFFFFF
// SS::::::SSSSS E:::::::::::::::E F:::::::::::::::F
// SSS::::::::SS E:::::::::::::::E F:::::::::::::::F
// SSSSSS::::S E::::::EEEEEEEEEE F::::::FFFFFFFFFF
// S:::::S E:::::E F:::::F
// S:::::S E:::::E EEEEEE F:::::F
// SSSSSSS S:::::S EE::::::EEEEEEEE:::::E FF:::::::FF
// S::::::SSSSSS:::::S E::::::::::::::::::::E F::::::::FF
// S:::::::::::::::SS E::::::::::::::::::::E F::::::::FF
// SSSSSSSSSSSSSSS EEEEEEEEEEEEEEEEEEEEEE FFFFFFFFFFF
//
//-------------------------------------------------------------------------------------------------------------------------------------------------------
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// Define the LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Define the relay pins
const int relayPins[] = {22, 23, 24, 25, 26, 27};
// Define the relay states
int relayStates[] = {LOW, LOW, LOW, LOW, LOW, LOW}; // Start with all relays off
// Define the delay times (in milliseconds)
unsigned long Offsets[6];
unsigned long Intervals[6];
unsigned long lastButtonUpTime = 0;
unsigned long lastButtonDownTime = 0;
unsigned long lastButtonSelectTime = 0;
unsigned long debounceDelay = 500; // Adjust the debounce delay as needed
unsigned long currentTime = millis();
//Double press mills
unsigned long buttonPressTime = 0;
// Define the button pins
const int buttonUp = 12;
const int buttonDown = 13;
const int buttonSelect = 11;
// Define the cursor position
volatile int cursorPosition = 0;
// Define the edit mode
volatile bool editMode = false;
// Define LCD update
volatile bool updateLCD = true;
// Define the number of solenoids
const int numSolenoids = 6;
// Define the edit item (0 = Offset, 1 = Opening Time)
volatile int editItem = 0;
// Try get updates instantly. Test logic
bool eepromUpdated = false;
//Rest of additional menus and options
bool secretMenuActive = false;
bool simulateOption = false;
bool cleaningOption = false;
// Define a timer class
class Timer {
private:
unsigned long startTime;
unsigned long interval;
bool running;
public:
Timer(unsigned long interval = 0) {
this->interval = interval;
running = false;
}
void start() {
startTime = millis();
running = true;
}
void stop() {
running = false;
}
bool isRunning() {
return running;
}
bool event() {
if (running && millis() - startTime >= interval) {
startTime = millis(); // reset the start time
return true;
}
return false;
}
};
// Define a delay class
class Delay {
private:
unsigned long startDTime;
unsigned long delayTime;
bool running;
public:
Delay(unsigned long delayTime = 0) {
this->delayTime = delayTime;
running = false;
}
void start() {
startDTime = millis();
running = true;
}
void stop() {
running = false;
}
bool isRunning() {
return running;
}
bool event() {
if (running && millis() - startDTime >= delayTime) {
startDTime = millis(); // reset the start time
return true;
}
return false;
}
};
// Create timers for each relay
Timer timers[6];
// Create timer Delays for each relay
Delay Delays[6];
void setup() {
// Initialize the LCD
lcd.init();
lcd.backlight();
// Bootloader
lcd.setCursor(2, 1);
lcd.print("Bandau Uzsikraut");
lcd.setCursor(0, 2);
for (int i = 0; i < 20; ++i) {
lcd.print(".");
delay(100);
}
delay(500);
// Clear screen after loading animation
lcd.clear();
delay(750);
// Set the relay pins as output and initialize states
for (int i = 0; i < 6; i++) {
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], relayStates[i]); // Ensure all relays start off
}
// Set pullup on buttons
pinMode(buttonUp, INPUT_PULLUP);
pinMode(buttonDown, INPUT_PULLUP);
pinMode(buttonSelect, INPUT_PULLUP);
// Begin serial communication
Serial.begin(9600);
// Load the values from the EEPROM
for (int i = 0; i < numSolenoids; i++) {
Offsets[i] = EEPROM.read(18 + i * 3) * 10;
Intervals[i] = EEPROM.read(i * 3) * 10;
timers[i] = Timer(Intervals[i]); // Initialize the timers
Delays[i] = Delay(Offsets[i]); // Initialize the delays
}
// Read and print EEPROM values
printEEPROMValues();
// Attach interrupts to the buttons
attachInterrupt(digitalPinToInterrupt(buttonUp), moveCursorUp, RISING);
attachInterrupt(digitalPinToInterrupt(buttonDown), moveCursorDown, RISING);
attachInterrupt(digitalPinToInterrupt(buttonSelect), toggleEditMode, RISING);
}
void loop() {
static unsigned long lastButtonUpTime = 0;
static unsigned long lastButtonDownTime = 0;
static unsigned long lastButtonSelectTime = 0;
unsigned long lastUpdate = 0;
unsigned long debounceDelay = 250; // Adjust the debounce delay as needed
unsigned long currentTime = millis();
// Button Up
if (currentTime - lastButtonUpTime > debounceDelay && digitalRead(buttonUp) == LOW) {
moveCursorUp();
lastButtonUpTime = currentTime;
}
// Button Down
if (currentTime - lastButtonDownTime > debounceDelay && digitalRead(buttonDown) == LOW) {
moveCursorDown();
lastButtonDownTime = currentTime;
}
// Button Select
if (currentTime - lastButtonSelectTime > debounceDelay && digitalRead(buttonSelect) == LOW) {
toggleEditMode();
lastButtonSelectTime = currentTime;
}
//Secret menu call and axit
if (digitalRead(buttonUp) == LOW && digitalRead(buttonDown) == LOW) {
if (buttonPressTime == 0) { // if not already started
buttonPressTime = millis(); // start timer
} else if (millis() - buttonPressTime >= 2000) { // if 2 seconds have passed
secretMenu(); // call your function
secretMenuActive = true;
buttonPressTime = 0; // reset timer
updateLCD = true;
}
}
// Check for serial input
if (Serial.available()) {
processSerialCommand();
}
// Update relay states
updateRelayStates();
// Update the LCD if needed
if (updateLCD) {
updateLCDContent();
}
}
void printEEPROMValues() {
Serial.println("------------------------------------------------");
for (int i = 0; i < numSolenoids; i++) {
Serial.print("Solenoid ");
Serial.print(i + 1);
Serial.print(" Opening Time [ ");
Serial.print(EEPROM.read(i * 3) * 10);
Serial.print(" ] Offset [ ");
Serial.print(EEPROM.read(18 + i * 3) * 10);
Serial.println(" ]");
}
Serial.println("------------------------------------------------");
}
void processSerialCommand() {
String command = Serial.readStringUntil('\n');
command.trim();
if (command == "HELP") {
// Display help information
displayHelp();
} else if (command == "S") {
// Load the values from the EEPROM
for (int i = 0; i < numSolenoids; i++) {
Offsets[i] = EEPROM.read(18 + i * 3) * 10;
Intervals[i] = EEPROM.read(i * 3) * 10;
timers[i] = Timer(Intervals[i]); // Initialize the timers
Delays[i] = Delay(Offsets[i]); // Initialize the delays
}
// Start all delays for the solenoids
startAllDelays();
updateRelayStates();
} else if (command == "6") {
moveCursorUp();
} else if (command == "4") {
moveCursorDown();
} else if (command == "8" || (digitalRead(buttonSelect) == LOW )) {
toggleEditMode();
} else if (command == "DEFAULT") {
setDefaultValues();
} else if (command == "EEPROM RESET") {
resetEEPROMValues();
} else if (command == "Print") {
printEEPROMValues();
} else if (command == "x") {
startSimulation();
} else if (command == "Menu") {
secretMenuActive = true;
secretMenu();
}
}
void secretMenu() {
int currentPage = 1; // Track the current page in the secret menu
int cursorPosition = 0; // Track the cursor position (0 for ON, 1 for OFF)
unsigned long lastUpdate = 0; // Variable to store the last update time
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("* Secret Menu *");
delay(1000);
Serial.println("Entering Secret Menu");
lcd.clear();
while (secretMenuActive) {
if (currentPage == 1) {
lcd.setCursor(5, 0);
lcd.print("Simulation");
lcd.setCursor(2, 2);
lcd.print(cursorPosition == 1 ? "<< ON >> OFF " : " ON << OFF >>");
} else if (currentPage == 2) {
lcd.setCursor(6, 0);
lcd.print("Cleaning");
lcd.setCursor(2, 2);
lcd.print(cursorPosition == 1 ? "<< ON >> OFF " : " ON << OFF >>");
}
if (digitalRead(buttonUp) == LOW) {
if (currentPage == 2) {
currentPage = 1;
lcd.clear();
}
} else if (digitalRead(buttonDown) == LOW) {
if (currentPage == 1) {
currentPage = 2;
lcd.clear();
}
} else if (digitalRead(buttonSelect) == LOW) {
// Toggle the cursor position
cursorPosition = !cursorPosition;
delay(300);
if (currentPage == 1) {
if (cursorPosition == 1) {
simulateOption = true ;
} else {
stopSimulation();
simulateOption = false;
}
} else if (currentPage == 2) {
if (cursorPosition == 1) {
startCleaning();
cleaningOption = true;
} else {
stopCleaning();
cleaningOption = false;
}
}
}
if (simulateOption && millis() - lastUpdate >= 2000) {
startSimulation();
}
if (cleaningOption && millis() - lastUpdate >= 5000) {
startCleaning();
Serial.println("Cleaning mode active. Press 'OFF' to stop.");
lastUpdate = millis();
}
//Secret menu call and axit
if (digitalRead(buttonUp) == LOW && digitalRead(buttonDown) == LOW) {
if (buttonPressTime == 0) { // if not already started
buttonPressTime = millis(); // start timer
} else if (millis() - buttonPressTime >= 2000) { // if 2 seconds have passed
Serial.println("Exiting");
lcd.clear();
lcd.setCursor(5, 1);
lcd.print("Exiting...");
delay(1000);
secretMenuActive = false;
buttonPressTime = 0; // reset timer
updateLCD = true;
} else if (millis() - buttonPressTime >= 3000) {
Serial.print("pritruko palaikyt nulinam laikmati knopkem");// if 3 seconds have passed
buttonPressTime = 0;
}
}
}
}
//----------------------------------------Secret menu end
void startAllDelays() {
for (int i = 0; i < numSolenoids; i++) {
Delays[i].start();
}
Serial.println("---Values updated for cycle");
}
void updateRelayStates() {
for (int i = 0; i < numSolenoids; i++) {
if (Delays[i].event()) {
Offsets[i] = EEPROM.read(18 + i * 3) * 10;
Intervals[i] = EEPROM.read(i * 3) * 10;
timers[i].start();
relayStates[i] = HIGH;
digitalWrite(relayPins[i], relayStates[i]);
Delays[i].stop();
}
if (timers[i].event()) {
relayStates[i] = LOW;
digitalWrite(relayPins[i], relayStates[i]);
timers[i].stop();
}
}
}
//-------------------------------------------------------------------------------
void startSimulation() {
Timer simulationTimer(2000); // Adjust the interval as needed
Serial.println("Start Simulation");
simulationTimer.start();
startCleaning();
while (!simulationTimer.event()) {
// Do nothing ...
}
stopCleaning();
stopSimulation();
}
//--------------------------------------------Trying to get Simulation to work on toggle....
void startCleaning() {
// Open all relays
for (int i = 0; i < numSolenoids; i++) {
relayStates[i] = HIGH;
digitalWrite(relayPins[i], HIGH);
}
updateLCD = true; // Set the flag to update the LCD
}
void stopSimulation() {
// Add logic to stop the simulation
// Load the values from the EEPROM
for (int i = 0; i < numSolenoids; i++) {
Offsets[i] = EEPROM.read(18 + i * 3) * 10;
Intervals[i] = EEPROM.read(i * 3) * 10;
}
// Start all delays for the solenoids
startAllDelays();
updateRelayStates();
Serial.println("----Simulation completed----");
updateLCD = true;
Serial.println("-------------Simulation stopped----------------");
updateLCD = true; // Set the flag to update the LCD
}
void stopCleaning() {
// Add logic to stop the cleaning
for (int i = 0; i < numSolenoids; i++) {
relayStates[i] = LOW;
digitalWrite(relayPins[i], relayStates[i]);
}
updateLCD = true; // Set the flag to update the LCD
Serial.println("Cleaning mode stopped. Returning to normal state.");
}
//--------------------------------------------------------------------------------
void updateLCDContent() {
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Solenoid ");
lcd.print(cursorPosition + 1);
lcd.setCursor(0, 1);
lcd.print("Offset : ");
if (editMode && editItem == 0) {
lcd.print("<< ");
lcd.print(Offsets[cursorPosition] / 10);
lcd.print(" >> %");
} else {
lcd.print(Offsets[cursorPosition] / 10);
lcd.print(" %");
}
lcd.setCursor(0, 2);
lcd.print("Opening: ");
if (editMode && editItem == 1) {
lcd.print("<< ");
lcd.print(Intervals[cursorPosition] / 10);
lcd.print(" >> cs");
} else {
lcd.print(Intervals[cursorPosition] / 10);
lcd.print(" cs");
}
updateLCD = false; // Reset the flag
}
void displayHelp() {
Serial.println("List of available commands:");
Serial.println("\"S\": Start all delays for the solenoids.");
Serial.println("\"6\": Move the cursor up.");
Serial.println("\"4\": Move the cursor down.");
Serial.println("\"8\": Toggle the edit mode.");
Serial.println("\"EEPROM RESET\": Reset all EEPROM values to zero.");
Serial.println("\"DEFAULT\": Set the Offsets and Intervals to their default values and save them to the EEPROM.");
Serial.println("\"Print\": Prints a table of current values saved on Solenoids.");
Serial.println("\"Menu\": Secret menu to Operate Simulation and Cleaning.");
Serial.println("\"Exit\": Exit Secret Menu");
}
void moveCursorUp() {
if (editMode) {
// Increment the value of the selected item
if (editItem == 0) {
Offsets[cursorPosition] = min(1500, Offsets[cursorPosition] + 10);
} else {
Intervals[cursorPosition] += 10;
}
} else {
// Move the cursor up
cursorPosition = min(numSolenoids - 1, cursorPosition + 1);
}
updateLCD = true; // Set the flag to update the LCD
}
void moveCursorDown() {
if (editMode) {
// Decrement the value of the selected item
if (editItem == 0) {
Offsets[cursorPosition] = max(0, Offsets[cursorPosition] - 10);
} else {
Intervals[cursorPosition] = max(0, Intervals[cursorPosition] - 10);
}
} else {
// Move the cursor down
delay(1000);
cursorPosition = max(0, cursorPosition - 1);
}
updateLCD = true; // Set the flag to update the LCD
}
void toggleEditMode() {
if (editMode) {
// Switch to the next item to edit
editItem = (editItem + 1) % 2;
if (editItem == 0) {
// If we've cycled back to the first item, exit edit mode and display a "Saved" message
editMode = false;
displaySavedMessage();
delay(2000);
saveValuesToEEPROM();
eepromUpdated = true;
}
} else {
// Enter edit mode
editMode = true;
}
updateLCD = true; // Set the flag to update the LCD
}
void displaySavedMessage() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*******************");
lcd.setCursor(5, 1);
lcd.print("Solenoid ");
lcd.print(cursorPosition + 1);
lcd.setCursor(7, 2);
lcd.print("Saved!");
lcd.setCursor(0, 3);
lcd.print("*******************");
}
void saveValuesToEEPROM() {
for (int i = 0; i < numSolenoids; i++) {
EEPROM.update(18 + i * 3, Offsets[i] / 10);
EEPROM.update(i * 3, Intervals[i] / 10);
}
Serial.println("Values saved to EEPROM.");
}
void setDefaultValues() {
for (int i = 0; i < numSolenoids; i++) {
if (i == 0 || i == 2 || i == 4) { // Solenoids 1, 3, 5
Offsets[i] = 0;
} else { // The rest of the solenoids
Offsets[i] = 500;
}
Intervals[i] = 60;
EEPROM.update(18 + i * 3, Offsets[i] / 10);
EEPROM.update(i * 3, Intervals[i] / 10);
}
Serial.println("Default Values Saved to EEPROM.");
updateLCD = true; // Set the flag to update the LCD
}
void resetEEPROMValues() {
// Zero out the EEPROM values
for (int i = 0; i < numSolenoids * 2 + 1; i++) {
EEPROM.update(i, 0);
}
Serial.println("EEPROM VALUES ZEROED");
updateLCD = true; // Set the flag to update the LCD
}