Flow control. help!

Hello, I am trying to create a simple control system for one of my implements on a farm. It's a watering system. AI been a great help, i am electronically savvy but not with code. i am looking for help ! i know what i need and want, got parts in, trying to play about with code but to complete the code of what i need i have no clue where to start.

this is what I'm trying to achieve.

list of parts i have
Arduino mega2560R3
16 Relay module (12v)
I2C 20x4 LCD with driver 4 pins (SCL,SDA,VVC,GND)
Still awaiting membrane Buttons to arrive

everything will be powered from an external power source, when tractor ON will be supplying 16ish V tested on LAB bench all is good.

WHAT I NEED TO ACHIEVE

When booted up list of Relays with 2 values displayed and be able to scroll through them but module in constant monitoring of a Momentary switch activation that will start of a pre-set cycle once.

the cycle is determined in code, that i will not need amending ever. as follows:
Relays 1,3,5,7,9,11 activate with their individual presets ofd delay before activation and activation duration before closing.
irrelevant of whats happening in the background after 500ms second group activates 2,4,6,8,10 with their individual presets of delay before activation and activation time.

so in theory if
Selenoid 1 activates with 0 delay for 1000ms
Selenoid 2 activates after group delay of 500ms with 0 delay for 500ms
Both should deactivate at the same time just have diferent activation time. i cant seem to get that in code no matter how i try im just no good with code.

 Have menu on LCD that can amend values 
  1. of each individual relay and store on EEPROM.
    Opening time xx cs (centiseconds max 100cs )
    Offset xx % (range from 0 to 2 seconds)
    2)Delay between 2 groups of relays as previously mentioned 500ms, just want to be able to amend value on screen and save to eeprom too
  2. Have additional secret menu after pressing some button for 2 seconds that will let me enter SIMULATION mode. Once selected ON it imitates the poress of the momentary switch that activates then cycle every second or so. or even beter cycles in a loop untill stopped

Now who is up to a challenge ?

-------------------------------------------------UPDATE----------------------------------------------------

//“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>
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[] = {EEPROM.read(18)*10, EEPROM.read(21)*10, EEPROM.read(24)*10, EEPROM.read(27)*10, EEPROM.read(30)*10, EEPROM.read(33)*10};

// Define the interval (in milliseconds)
unsigned long Intervals[] = {EEPROM.read(0)*10, EEPROM.read(3)*10, EEPROM.read(6)*10, EEPROM.read(9)*10, EEPROM.read(12)*10, EEPROM.read(15)*10};

// Define the button pins
const int buttonUp = 2;
const int buttonDown = 3;
const int buttonSelect = 4;

// 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;

class Timer {
  private:
    unsigned long startTime;
    unsigned long interval;
    bool running;

  public:
    Timer(unsigned long interval) {
      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;
    }
};

class Delay {
  private:
    unsigned long startDTime;
    unsigned long delayTime;
    bool running;

  public:
    Delay(unsigned long delayTime) {
      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[] = {Timer(Intervals[0]), Timer(Intervals[1]), Timer(Intervals[2]), Timer(Intervals[3]), Timer(Intervals[4]), Timer(Intervals[5])};

// Create timer Delays for each relay
Delay Delays[] = {Delay(Offsets[0]), Delay(Offsets[1]), Delay(Offsets[2]), Delay(Offsets[3]), Delay(Offsets[4]), Delay(Offsets[5])};


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(200);
  }
delay(500);
  // Clear screen after loading animation
  lcd.clear();
  delay(750);


  // Set the relay pins as output
  for(int i = 0; i < 6; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], relayStates[i]); // Ensure all relays start off
  }

  // Begin serial communication
  Serial.begin(9600);

  //Read and print EEPROM values
  // Selonoid 1
 Serial.println("------------------------------------------------");
 Serial.print("Selenoid 1 Opening Time [ "); 
 Serial.print(EEPROM.read(0)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(18)*10);
 Serial.println(" ]");
  //Selonoid 2
   Serial.print("Selenoid 2 Opening Time [ "); 
 Serial.print(EEPROM.read(3)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(21)*10);
 Serial.println(" ]");
  //Selonoid 3
   Serial.print("Selenoid 3 Opening Time [ "); 
 Serial.print(EEPROM.read(6)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(24)*10);
 Serial.println(" ]");
  //Selonoid 4
   Serial.print("Selenoid 4 Opening Time [ "); 
 Serial.print(EEPROM.read(9)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(27)*10);
 Serial.println(" ]");
  //Selonoid 5
   Serial.print("Selenoid 5 Opening Time [ "); 
 Serial.print(EEPROM.read(12)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(30)*10);
 Serial.println(" ]");
  //Selonoid 6
   Serial.print("Selenoid 6 Opening Time [ "); 
 Serial.print(EEPROM.read(15)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(33)*10);
 Serial.println(" ]");
 Serial.println("------------------------------------------------");

  // Attach interrupts to the buttons
  attachInterrupt(digitalPinToInterrupt(buttonUp), moveCursorUp, RISING);
  attachInterrupt(digitalPinToInterrupt(buttonDown), moveCursorDown, RISING);
  attachInterrupt(digitalPinToInterrupt(buttonSelect), toggleEditMode, RISING);
}

void loop() {
  // Check for serial input
  if(Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim(); // Remove any trailing whitespace
    //--------------------------------------------HELP COMMAND IN SERIAL MONITOR--------------------------------------------
    if(command == "HELP") {
    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 Selonoids.");
    //-----------------------------------------------------------------------------------------------------------------------
    } else if (command == "S") {
      //Report Switch on Serial Monitor
      Serial.println("Switch activated");
      // Start all delays
      for(int i = 0; i < numSolenoids; i++) {
        Delays[i].start();
      }
    } else if (command == "6") {
      moveCursorUp();
    } else if (command == "4") {
      moveCursorDown();
    } else if (command == "8") {
      toggleEditMode();
    } else if(command == "Default") {
    // Save Default offsets and intervals to the EEPROM
    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
  
    } else if (command == "EEPROM RESET") {
      // 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
      }
//------------------Print values on command
      } else if (command == "Print") {
 Serial.println("------------------------------------------------");
 Serial.print("Selenoid 1 Opening Time [ "); 
 Serial.print(EEPROM.read(0)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(18)*10);
 Serial.println(" ]");
  //Selonoid 2
   Serial.print("Selenoid 2 Opening Time [ "); 
 Serial.print(EEPROM.read(3)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(21)*10);
 Serial.println(" ]");
  //Selonoid 3
   Serial.print("Selenoid 3 Opening Time [ "); 
 Serial.print(EEPROM.read(6)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(24)*10);
 Serial.println(" ]");
  //Selonoid 4
   Serial.print("Selenoid 4 Opening Time [ "); 
 Serial.print(EEPROM.read(9)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(27)*10);
 Serial.println(" ]");
  //Selonoid 5
   Serial.print("Selenoid 5 Opening Time [ "); 
 Serial.print(EEPROM.read(12)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(30)*10);
 Serial.println(" ]");
  //Selonoid 6
   Serial.print("Selenoid 6 Opening Time [ "); 
 Serial.print(EEPROM.read(15)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(33)*10);
 Serial.println(" ]");
 Serial.println("------------------------------------------------");
         updateLCD = true; // Set the flag to update the LCD
//-----------------------------------------

    }
  }

  // Update the relays
  for(int i = 0; i < numSolenoids; i++) {
    if(Delays[i].event()) {
      // Start the timer after the delay
      timers[i].start();
      relayStates[i] = HIGH; // Turn on the relay
      digitalWrite(relayPins[i], relayStates[i]);
      Delays[i].stop(); // Stop the delay after one cycle
    }

    if(timers[i].event()) {
      // Change the state of the relay
      relayStates[i] = LOW; // Turn off the relay
      digitalWrite(relayPins[i], relayStates[i]);
      timers[i].stop(); // Stop the timer after one cycle
    }
  }

// Update the LCD if needed
  if (updateLCD) {
    lcd.clear();
    lcd.setCursor(5, 0);
    lcd.print("Selonoid ");
    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 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 = max(0, 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
    cursorPosition = min(numSolenoids - 1, 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;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("*******************");
      lcd.setCursor(5, 1);
      lcd.print("Selonoid ");
      lcd.print(cursorPosition + 1);
      lcd.setCursor(7, 2);
      lcd.print("Saved!");
      lcd.setCursor(0, 3);
      lcd.print("*******************");
      //---------------------First try EEPROM UPDATE
    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.");
      //--------------------------------------------
      delay(2000); // Time on screen
    }
  } else {
    // Enter edit mode
    editMode = true;
  }
  updateLCD = true; // Set the flag to update the LCD
}

Video of latest code in action

Have you written programs to exercise the LCD? Have you written a program to exercise the relay module? If not, why not?

Hello kornelijusuk

Welcome to the world's best Arduino forum ever.

Post a timing diagram that shows all relay states and the switching dependencies of whatever.

Have a nice day and enjoy coding in C++.

1 Like

You want it coded? Request to have this topic moved to:

Thats a great Idea. Ill try and whip a code to test out the logic behind this. Mind if i post code here if i get stuck ?

no, that is the task of this forum.

This seems to be working. have a look where i might improve. I still need to add ability o edit Values for offset and Open duration

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Define the relay pins
const int relay1Pin = 22;
const int relay2Pin = 23;
const int relay3Pin = 24;
const int relay4Pin = 25;
const int relay5Pin = 26;
const int relay6Pin = 27;

// Define the relay states
int relay1State = HIGH;
int relay2State = HIGH;
int relay3State = HIGH;
int relay4State = HIGH;
int relay5State = HIGH;
int relay6State = HIGH;

//Start value for Offset modification
unsigned long OffsetTime1 = 800;
unsigned long OffsetTime2 = 1000;
unsigned long OffsetTime3 = 0;
unsigned long OffsetTime4 = 1000;
unsigned long OffsetTime5 = 0;
unsigned long OffsetTime6 = 1000;

// Define the previous time variables
unsigned long previousTime1 = OffsetTime1;
unsigned long previousTime2 = OffsetTime2;
unsigned long previousTime3 = OffsetTime3;
unsigned long previousTime4 = OffsetTime4;
unsigned long previousTime5 = OffsetTime5;
unsigned long previousTime6 = OffsetTime6;

// Define the interval (in milliseconds)
unsigned long interval1 = 1000; 
unsigned long interval2 = 1000; 
unsigned long interval3 = 1000; 
unsigned long interval4 = 1000; 
unsigned long interval5 = 1000; 
unsigned long interval6 = 1000; 


void setup() {
  //LCD
  lcd.init();
  lcd.backlight();
  // Set the relay pins as output
  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);
  pinMode(relay3Pin, OUTPUT);
  pinMode(relay4Pin, OUTPUT);
  pinMode(relay5Pin, OUTPUT);
  pinMode(relay6Pin, OUTPUT);
}

void loop() {
  // Get the current time
  unsigned long currentTime = millis();

  // Check if it's time to update relay1
  if( currentTime - previousTime1 >= interval1) {
    // Save the current time
    previousTime1 = currentTime;

    // Change the state of relay1
    relay1State = !relay1State;
    digitalWrite(relay1Pin, relay1State);
  }

  // Check if it's time to update relay2
  if(currentTime - previousTime2 >= interval2) {
    // Save the current time
    previousTime2 = currentTime;

    // Change the state of relay2
    relay2State = !relay2State;
    digitalWrite(relay2Pin, relay2State);
  }

    // Same for 3
  if(currentTime - previousTime3 >= interval3) {
    // Save the current time
    previousTime3 = currentTime;

    // Change the state of relay3
    relay3State = !relay3State;
    digitalWrite(relay3Pin, relay3State);
  }

    // Same for 4th
  if(currentTime - previousTime4 >= interval4) {
    // Save the current time
    previousTime4 = currentTime;

    // Change the state of relay4
    relay4State = !relay4State;
    digitalWrite(relay4Pin, relay4State);
  }

    // Same for 5th
  if(currentTime - previousTime5 >= interval5) {
    // Save the current time
    previousTime5 = currentTime;

    // Change the state of relay5
    relay5State = !relay5State;
    digitalWrite(relay5Pin, relay5State);
  }

    // Same for 6th
  if(currentTime - previousTime6 >= interval6) {
    // Save the current time
    previousTime6 = currentTime;

    // Change the state of relay6
    relay6State = !relay6State;
    digitalWrite(relay6Pin, relay6State);
  }

  //HEADER
  lcd.setCursor(3, 0);
  lcd.print("Selonoid  List");
  // Display the relay activation times on the LCD
  lcd.setCursor(0, 1);
  lcd.print("Sel 1: ");
  lcd.print(interval1 / 100);
  lcd.setCursor(0, 2);
  lcd.print("Sel 2: ");
  lcd.print(interval2 / 100);
  lcd.setCursor(0, 3);
  lcd.print("Sel 3: ");
  lcd.print(interval3 / 100);
  lcd.setCursor(10, 1);
  lcd.print("Sel 4: ");
  lcd.print(interval4 / 100);
  lcd.setCursor(10, 2);
  lcd.print("Sel 5: ");
  lcd.print(interval5 / 100);
  lcd.setCursor(10, 3);
  lcd.print("Sel 6: ");
  lcd.print(interval6 / 100);
}

To do so a button manager is needed to navigate through a menu using a UP/DOWN/+/-/select button.

Your current sketch has the potential to be optimzed by using the STRUCT{}
and ARRAY[] instructions.

i think i tried that before but just couldn't get it to work the way i wanted. and one different value in timing affected all relays behaving weirdly. could've been my code. ill try again and see how it comes up. will try and add edit functions. but will need a lot of help with EEPEOM stuff as that's new territory for me

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Define the relay pins
const int relayPins[] = {22, 23, 24, 25, 26, 27};
const int buttonPin = 2; //Momentary switch or a button

// 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 delayTimes[] = {0, 500, 0, 500, 0, 500};

// Define the interval (in milliseconds)
unsigned long intervals[] = {100, 100, 100, 100, 100, 100};

// Define the previous time variables
unsigned long previousTimes[] = {0, 0, 0, 0, 0, 0};

bool buttonPressed = false; //Flag for button

void setup() {
  // Initialize the LCD
  lcd.init();
  lcd.backlight();

  // Set the relay pins as output
  for(int i = 0; i < 6; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], relayStates[i]); // Ensure all relays start off
  }

    pinMode(buttonPin, INPUT); // Button as inpiut


  // Begin serial communication
  Serial.begin(9600);

  // Output relay presets
  for(int i = 0; i < 6; i++) {
    Serial.print("Solenoid ");
    Serial.print(i+1);
    Serial.print(": Offset ");
    Serial.print(delayTimes[i]);
    Serial.print(" Opening time ");
    Serial.println(intervals[i] / 10);
  }
}

void loop() {
  // Get the current time
  unsigned long currentTime = millis();

  // Check if the button is pressed
  if(digitalRead(buttonPin) == HIGH) {
    buttonPressed = true;
    delay(1000);
     Serial.println("Momentary push");
  }

  // Update the relays
  for(int i = 0; i < 6; i++) {
    if(relayStates[i] == LOW && currentTime - previousTimes[i] >= delayTimes[i]) {
      // Turn on the relay
      relayStates[i] = HIGH;
      digitalWrite(relayPins[i], relayStates[i]);
      previousTimes[i] = currentTime; // Update the time
    } else if(relayStates[i] == HIGH && currentTime - previousTimes[i] >= intervals[i]) {
      // Turn off the relay
      relayStates[i] = LOW;
      digitalWrite(relayPins[i], relayStates[i]);
      buttonPressed = false;
    }
  }

   //HEADER
  lcd.setCursor(3, 0);
  lcd.print("Selenoid List");
  // Display the relay activation times on the LCD
  lcd.setCursor(0, 1);
  lcd.print("Sel 1: ");
  lcd.print(intervals[0] / 10);
  lcd.setCursor(0, 2);
  lcd.print("Sel 2: ");
  lcd.print(intervals[1] / 10);
  lcd.setCursor(0, 3);
  lcd.print("Sel 3: ");
  lcd.print(intervals[2] / 10);
  lcd.setCursor(10, 1);
  lcd.print("Sel 4: ");
  lcd.print(intervals[3] / 10);
  lcd.setCursor(10, 2);
  lcd.print("Sel 5: ");
  lcd.print(intervals[4] / 10);
  lcd.setCursor(10, 3);
  lcd.print("Sel 6: ");
  lcd.print(intervals[5] / 10);
  
}

I tried. Cant seem to get my relays activate only for 1 sequance when "momentary button" is pressed. when it boots up it goes straight to cycling non stop

Need to find a way to share video here but dont yet know how

UPDATE found a way

Video

Hello kornelijusuk

You can use the delay() function when running a single task on the Arduino. Mixing the delay() function with a millis() based timer leads to unstable system behaviour.

If you have serval tasks with a dedicated timing behaiviour it is usefull to have time slices based on millis() function.

Take the BlinkWithOutDelay example of the IDE and use this example to design your timer() function.

This timer() function might provide the following methodes:

  • start();
  • stop(),
  • event();
  • isRunning();

Have a nice day and enjoy coding in C++.

1 Like
#include <LiquidCrystal_I2C.h>
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[] = {0, 440, 0, 440, 0, 440};

// Define the interval (in milliseconds)
unsigned long Intervals[] = {60, 60, 60, 60, 60, 60};

class Timer {
  private:
    unsigned long startTime;
    unsigned long interval;
    bool running;

  public:
    Timer(unsigned long interval) {
      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;
    }
};

class Delay {
  private:
    unsigned long startDTime;
    unsigned long delayTime;
    bool running;

  public:
    Delay(unsigned long delayTime) {
      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[] = {Timer(Intervals[0]), Timer(Intervals[1]), Timer(Intervals[2]), Timer(Intervals[3]), Timer(Intervals[4]), Timer(Intervals[5])};

// Create timer Delays for each relay
Delay Delays[] = {Delay(Offsets[0]), Delay(Offsets[1]), Delay(Offsets[2]), Delay(Offsets[3]), Delay(Offsets[4]), Delay(Offsets[5])};

void setup() {
  // Initialize the LCD
  lcd.init();
  lcd.backlight();

  // Set the relay pins as output
  for(int i = 0; i < 6; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], relayStates[i]); // Ensure all relays start off
  }

  // Begin serial communication
  Serial.begin(9600);
}

void loop() {
  // Check for serial input
  if(Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim(); // Remove any trailing whitespace

    if(command == "S") {
      // Start all delays
      for(int i = 0; i < 6; i++) {
        Delays[i].start();
      }
    }
  }

  // Update the relays
  for(int i = 0; i < 6; i++) {
    if(Delays[i].event()) {
      // Start the timer after the delay
      timers[i].start();
      relayStates[i] = HIGH; // Turn on the relay
      digitalWrite(relayPins[i], relayStates[i]);
      Delays[i].stop(); // Stop the delay after one cycle
    }

    if(timers[i].event()) {
      // Change the state of the relay
      relayStates[i] = LOW; // Turn off the relay
      digitalWrite(relayPins[i], relayStates[i]);
      timers[i].stop(); // Stop the timer after one cycle
    }
  }

  // Display the relay activation times on the LCD
  lcd.setCursor(3, 0);
  lcd.print("Solenoid List");
  lcd.setCursor(0, 1);
  lcd.print("Sel 1: ");
  lcd.print(Intervals[0] / 10);
  lcd.setCursor(0, 2);
  lcd.print("Sel 2: ");
  lcd.print(Intervals[1] / 10);
  lcd.setCursor(0, 3);
  lcd.print("Sel 3: ");
  lcd.print(Intervals[2] / 10);
  lcd.setCursor(10, 1);
  lcd.print("Sel 4: ");
  lcd.print(Intervals[3] / 10);
  lcd.setCursor(10, 2);
  lcd.print("Sel 5: ");
  lcd.print(Intervals[4] / 10);
  lcd.setCursor(10, 3);
  lcd.print("Sel 6: ");
  lcd.print(Intervals[5] / 10);
}

I have managed to get 2 classes in and its doing what i want now.

I need ability to amend Values through LCD and Serial Monitorn for debuging. And have them values saved in EEPROM (I have never ever done anything with eeprom so this would be a thick forest).

Probably good idea to add serial command to imitate 3 buttons I will use (UP DOWN SELECT) so i can see how LCD behaves.

And i think would need a quick bootloader on the screen to allow code to load up values properly no ? a quick 2 second boot up screen will do just that i think.

The EEPROM part is (relatively) easy.

  EEPROM.get(0, Intervals);
  EEPROM.get(sizeof(Intervals), Offsets);

This will read Intervals and Offsets from EEPROM. This assumes that they are in consecutive locations (N intervals first followed by M offsets).
You can set / update them using

  EEPROM.get(0, Intervals);

and/or

  EEPROM.get(sizeof(Intervals), Offsets);

One problem is that the size of those two arrays changes if you add/or remove relays and the stored data is no longer correct.

Your bigger problem however will be to configure the Timers and Delays with the values from the EEPROM; you can not use the values that you read from EEPROM in the constructors as you should not access hardware (EEPROM in this case) before setup(). You will need to add a a method to each to set the Timer's interval and the Delay's delayTime.

Assuming that you have those additional methods in place, the basics of setup() related to this are below; I called the methods set().

void setup()
{
  // run this only once to get some initial values
  //EEPROM.put(0, Intervals);
  //EEPROM.put(sizeof(Intervals), Offsets);

  EEPROM.get(0, Intervals);
  EEPROM.get(sizeof(Intervals), Offsets);

  Serial.println(F("Setting timers"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(timers); cnt++)
  {
    timers[cnt].set(Intervals[cnt]);
  }
  Serial.println(F("Setting intervals"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(Delays); cnt++)
  {
    Delays[cnt].set(Offsets[cnt]);
  }
}

I have an elementary solution for configuration at startup; it has one minor flaw but I can post it if you're interested.

I was playing a lot with EEPROM functions outside this code just to see how everything works. then slowly tried implementing it to the code here and was trial and error obviously but made it work. might be the LONG WAY but it works.

Will upload a video link soon to show up what I've done so far. Still waiting for them bloody buttons to come so currently using Serial commands to imitate button presses.

Is there a way to communicate with it, send / receive Serial data over the phone. so I can connect to it on the go if need be ?

//“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>
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[] = {EEPROM.read(18)*10, EEPROM.read(21)*10, EEPROM.read(24)*10, EEPROM.read(27)*10, EEPROM.read(30)*10, EEPROM.read(33)*10};

// Define the interval (in milliseconds)
unsigned long Intervals[] = {EEPROM.read(0)*10, EEPROM.read(3)*10, EEPROM.read(6)*10, EEPROM.read(9)*10, EEPROM.read(12)*10, EEPROM.read(15)*10};

// Define the button pins
const int buttonUp = 2;
const int buttonDown = 3;
const int buttonSelect = 4;

// 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;

class Timer {
  private:
    unsigned long startTime;
    unsigned long interval;
    bool running;

  public:
    Timer(unsigned long interval) {
      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;
    }
};

class Delay {
  private:
    unsigned long startDTime;
    unsigned long delayTime;
    bool running;

  public:
    Delay(unsigned long delayTime) {
      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[] = {Timer(Intervals[0]), Timer(Intervals[1]), Timer(Intervals[2]), Timer(Intervals[3]), Timer(Intervals[4]), Timer(Intervals[5])};

// Create timer Delays for each relay
Delay Delays[] = {Delay(Offsets[0]), Delay(Offsets[1]), Delay(Offsets[2]), Delay(Offsets[3]), Delay(Offsets[4]), Delay(Offsets[5])};


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(200);
  }
delay(500);
  // Clear screen after loading animation
  lcd.clear();
  delay(750);


  // Set the relay pins as output
  for(int i = 0; i < 6; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], relayStates[i]); // Ensure all relays start off
  }

  // Begin serial communication
  Serial.begin(9600);

  //Read and print EEPROM values
  // Selonoid 1
 Serial.println("------------------------------------------------");
 Serial.print("Selenoid 1 Opening Time [ "); 
 Serial.print(EEPROM.read(0)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(18)*10);
 Serial.println(" ]");
  //Selonoid 2
   Serial.print("Selenoid 2 Opening Time [ "); 
 Serial.print(EEPROM.read(3)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(21)*10);
 Serial.println(" ]");
  //Selonoid 3
   Serial.print("Selenoid 3 Opening Time [ "); 
 Serial.print(EEPROM.read(6)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(24)*10);
 Serial.println(" ]");
  //Selonoid 4
   Serial.print("Selenoid 4 Opening Time [ "); 
 Serial.print(EEPROM.read(9)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(27)*10);
 Serial.println(" ]");
  //Selonoid 5
   Serial.print("Selenoid 5 Opening Time [ "); 
 Serial.print(EEPROM.read(12)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(30)*10);
 Serial.println(" ]");
  //Selonoid 6
   Serial.print("Selenoid 6 Opening Time [ "); 
 Serial.print(EEPROM.read(15)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(33)*10);
 Serial.println(" ]");
 Serial.println("------------------------------------------------");

  // Attach interrupts to the buttons
  attachInterrupt(digitalPinToInterrupt(buttonUp), moveCursorUp, RISING);
  attachInterrupt(digitalPinToInterrupt(buttonDown), moveCursorDown, RISING);
  attachInterrupt(digitalPinToInterrupt(buttonSelect), toggleEditMode, RISING);
}

void loop() {
  // Check for serial input
  if(Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim(); // Remove any trailing whitespace
    //--------------------------------------------HELP COMMAND IN SERIAL MONITOR--------------------------------------------
    if(command == "HELP") {
    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 Selonoids.");
    //-----------------------------------------------------------------------------------------------------------------------
    } else if (command == "S") {
      //Report Switch on Serial Monitor
      Serial.println("Switch activated");
      // Start all delays
      for(int i = 0; i < numSolenoids; i++) {
        Delays[i].start();
      }
    } else if (command == "6") {
      moveCursorUp();
    } else if (command == "4") {
      moveCursorDown();
    } else if (command == "8") {
      toggleEditMode();
    } else if(command == "Default") {
    // Save Default offsets and intervals to the EEPROM
    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
  
    } else if (command == "EEPROM RESET") {
      // 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
      }
//------------------Print values on command
      } else if (command == "Print") {
 Serial.println("------------------------------------------------");
 Serial.print("Selenoid 1 Opening Time [ "); 
 Serial.print(EEPROM.read(0)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(18)*10);
 Serial.println(" ]");
  //Selonoid 2
   Serial.print("Selenoid 2 Opening Time [ "); 
 Serial.print(EEPROM.read(3)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(21)*10);
 Serial.println(" ]");
  //Selonoid 3
   Serial.print("Selenoid 3 Opening Time [ "); 
 Serial.print(EEPROM.read(6)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(24)*10);
 Serial.println(" ]");
  //Selonoid 4
   Serial.print("Selenoid 4 Opening Time [ "); 
 Serial.print(EEPROM.read(9)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(27)*10);
 Serial.println(" ]");
  //Selonoid 5
   Serial.print("Selenoid 5 Opening Time [ "); 
 Serial.print(EEPROM.read(12)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(30)*10);
 Serial.println(" ]");
  //Selonoid 6
   Serial.print("Selenoid 6 Opening Time [ "); 
 Serial.print(EEPROM.read(15)*10);
 Serial.print(" ]  Offset [ ");
 Serial.print(EEPROM.read(33)*10);
 Serial.println(" ]");
 Serial.println("------------------------------------------------");
         updateLCD = true; // Set the flag to update the LCD
//-----------------------------------------

    }
  }

  // Update the relays
  for(int i = 0; i < numSolenoids; i++) {
    if(Delays[i].event()) {
      // Start the timer after the delay
      timers[i].start();
      relayStates[i] = HIGH; // Turn on the relay
      digitalWrite(relayPins[i], relayStates[i]);
      Delays[i].stop(); // Stop the delay after one cycle
    }

    if(timers[i].event()) {
      // Change the state of the relay
      relayStates[i] = LOW; // Turn off the relay
      digitalWrite(relayPins[i], relayStates[i]);
      timers[i].stop(); // Stop the timer after one cycle
    }
  }

// Update the LCD if needed
  if (updateLCD) {
    lcd.clear();
    lcd.setCursor(5, 0);
    lcd.print("Selonoid ");
    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 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 = max(0, 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
    cursorPosition = min(numSolenoids - 1, 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;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("*******************");
      lcd.setCursor(5, 1);
      lcd.print("Selonoid ");
      lcd.print(cursorPosition + 1);
      lcd.setCursor(7, 2);
      lcd.print("Saved!");
      lcd.setCursor(0, 3);
      lcd.print("*******************");
      //---------------------First try EEPROM UPDATE
    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.");
      //--------------------------------------------
      delay(2000); // Time on screen
    }
  } else {
    // Enter edit mode
    editMode = true;
  }
  updateLCD = true; // Set the flag to update the LCD
}

Found a bug....Values update in EEPROM but when i try to call them every Cycle they dont update in the actual cycle. Only after restart.... I know my code is a bit all over the place but as i said before coding is not realy my thing. Please help..

else if (command == "S") {
      //--------------Bug fix to load values every time in case of any changes
  for (int i = 0; i < numSolenoids; i++) {
    Offsets[i] = EEPROM.read(18 + i * 3) * 10;
    Intervals[i] = EEPROM.read(i * 3) * 10;
  }
    Serial.println("Values updated for cycle");
  

Tried this, Serial print that values did update for cycle but for a fact i know its not updating. Because i play with delay on screen and i do 1 irregular, try to zero it in with the others and it doesnt do that. so I am making mistakes somewhere in the code..

I have found the Solution. Spent a good hour cleaning up my mess, organising. Looks like i fixed the bug. Added some bits, removed some bits. Please go through it and I will take any advice how to improve, or any aditional functionalities. Altho writing this now i figured i do need 2 more options

  1. Open selonoids << ON >> OFF
  2. Simulatiuon ON << OFF >> (Simulates S function every 1 Second)

Will work on that but here is my latest code

//--------------------------------------------------------------------------------------------------------------------------------------------------------
// 
//    ______   _                         _____                   _                    _               __        ___      _              _           
//   |  ____| | |                       / ____|                 | |                  | |             /_ |      / _ \    | |            | |          
//   | |__    | |   ___   __      __   | |        ___    _ __   | |_   _ __    ___   | |     __   __  | |     | | | |   | |__     ___  | |_    __ _ 
//   |  __|   | |  / _ \  \ \ /\ / /   | |       / _ \  | '_ \  | __| | '__|  / _ \  | |     \ \ / /  | |     | | | |   | '_ \   / _ \ | __|  / _` |
//   | |      | | | (_) |  \ 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];

// Define the button pins
const int buttonUp = 2;
const int buttonDown = 3;
const int buttonSelect = 4;

// 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;

// 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
  }

  // 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() {
  // 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") {
    toggleEditMode();
  } else if (command == "DEFAULT") {
    setDefaultValues();
  } else if (command == "EEPROM RESET") {
    resetEEPROMValues();
  } else if (command == "Print") {
    printEEPROMValues();
  }
}

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 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.");
}

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
    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();
      saveValuesToEEPROM();
      eepromUpdated = true;
      delay(2000); // Time on screen
    }
  } 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
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.