Multiple Solenoid Valve control using Serial Monitor

So basically, I am planning on controlling probably 8 solenoid valves for a irrigation related project through relays using an Arduino. For simplicity, Im trying to code first the first valve. Basically, I want to release different amounts of water every hour for an amount of time so that would mean different amount time that valves will be open. I thought of using an array set containing the different duration the valve will be open hence the WaterDistime.
Here is mycode :

unsigned long WaterDisttime[]= {6666, 4444, 7777, 2222, 3333}; //Solenoid Valve Open Time
unsigned long previousMillis = 0;
int i;
static unsigned long previousMillisinner = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");

}

  
void loop() 
{
    Valve1();
}

void Valve1() {
  for (i=0; i<5,i++;)
{
  const long Minute = 5000; //Constant Per hour intervals between opening times (Used 5secs for the moment for checking in the serial monitor)
  unsigned long now = millis();
  if ( now - previousMillis > Minute) 
  {
    unsigned long nowin = millis();
    if ( nowin - previousMillisinner < WaterDisttime[i])
    {
      Serial.println("On"); //refers to pin high but want to check serial monitor if working
    }
    if ( nowin - previousMillisinner > WaterDisttime[i])
    {
      Serial.println("Offffffffffffffffffffffff");
      previousMillis = now;
      previousMillisinner = now;
    }
  }
}
}

Upon uploading this code I only get the printed Start string. Am I using the array thing wrong? Would like some guidance.

Not really a problem with the arrays. More with the logic I think.

To get things straight, what you want is to:

  • Open de valve every interval (what you called 'Minute')
  • Open it for 'WaterDisttime[0]' time
  • close it
  • wait for the next interval but use the next timing?

For that, completely separate the open and close part. To requirements for opening have noting to do with the requirements for closing it. Open it is only related to the valve state (is it closed) and the last time the vale opened. Closing is only related to the valve state (it's open) and the time it opened.

Also, you don't need the for-loop, the loop() is already looping :wink:

And two big tips:

  • Use wayyy more useful names. Don't try to shorten thing to much. Aka
    'updateVales' instead of 'Valvel',
    'ValveInterval' instead of 'Minute',
    'valveIntervalMills' instead of 'previousMillis',
    'valveOpenMillis' instead of 'previousMillisinner',
    'WaterTimings' instead of 'WaterDisttime'
    and 'currentTiming' instead of the terrible 'i'.
unsigned long WaterDisttime[]= {6666, 4444, 7777, 2222, 3333}; //Solenoid Valve Open Time
unsigned long previousMillis = 0;
int i=0;
static unsigned long previousMillisinner = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");

}

  
void loop() 
{
  while(i<5){
    Valve1();
  }
}

void Valve1(){
const long Minute = 5000; //Constant Per hour intervals between opening times (Used 5secs for the moment for checking in the serial monitor)
  unsigned long now = millis();
  if ( now - previousMillis > Minute) 
  {
    unsigned long nowin = millis();
    if ( nowin - previousMillisinner < WaterDisttime[i])
    {
      Serial.println("On"); //refers to pin high but want to check serial monitor if working
    }
    if ( nowin - previousMillisinner > WaterDisttime[i])
    {
      Serial.println("Offffffffffffffffffffffff");
      i++;
      previousMillis = now;
      previousMillisinner = now;
    }
  }
}

I think I may have found the problem due to my for loop inside the loop(). But anyways, is there any way I can approach this code. Would love to hear some ideas for a neater program. And would this code work when used in 8 relays?

septillion:
To get things straight, what you want is to:

  • Open de valve every interval (what you called 'Minute')
  • Open it for 'WaterDisttime[0]' time
  • close it
  • wait for the next interval but use the next timing?

Yes yes. That would be the plan

septillion:
For that, completely separate the open and close part. To requirements for opening have noting to do with the requirements for closing it. Open it is only related to the valve state (is it closed) and the last time the vale opened. Closing is only related to the valve state (it's open) and the time it opened.

Sorry I got lost here in your statement :frowning: Mind if you can expound it more?

septillion:
Also, you don't need the for-loop, the loop() is already looping :wink:

Yes yes. Haha I kinda overlooked on this detail. My bad. And thanks for the response

Will the timing for all relays be the same? And will they all open at the same time? Or is the timing shifted between them? Etc.

septillion:
Will the timing for all relays be the same? And will they all open at the same time? Or is the timing shifted between them? Etc.

The relays will have different timings. So that would be probably different arrays for each of them. There could be multiple relays that will be open at the same time and such.

kladenstien:
So that would be probably different arrays for each of them.

Please no :smiley: That would do away with the benefit of an array. Just add a dimension :slight_smile: Will they all have the same number of timings?

But will the timing between the opening be the same for all? Aka, all open every minute?

Here my start for a single:

//Solenoid Valve Open Time
const unsigned long ValveTimings[]  = {6666, 4444, 7777, 2222, 3333};
const byte NrValveTimings = sizeof(ValveTimings)/sizeof(ValveTimings[0]);
const unsigned long ValveInterval = 5000; //time between valve openings

unsigned long valveOpenMillis;
byte currentValveTiming;
bool valveState;

void setup(){
  Serial.begin(115200);
  Serial.println(F("Started"));
}

void loop(){
    updateValves();
}

void updateValves(){
  if(valveState){
    updateValvesClosing();
  }
  else{
    updateValeOpening();
  }
}

void updateValeOpening(){
  const unsigned long MillisNow = millis();
  if(MillisNow - valveOpenMillis > ValveInterval){
    valveOpenMillis = MillisNow;
    
    valveState = true;
    Serial.println("Valve On");
  }
}

void updateValvesClosing(){
  const unsigned long MillisNow = millis();
  if(MillisNow - valveOpenMillis > ValveTimings[currentValveTiming]){
    valveState = false;
    Serial.println("Valve Off");
    
    currentValveTiming++;
    if(currentValveTiming >= NrValveTimings){
      currentValveTiming = 0;
    }
  }
}

septillion:
Please no :smiley: That would do away with the benefit of an array. Just add a dimension :slight_smile:

I'll take note of it. Thanks :smiley:

septillion:
Will they all have the same number of timings?

But will the timing between the opening be the same for all? Aka, all open every minute?

Was planning for other relays to be on different timing like 45 minutes. 30 minutes etc...

septillion:

//Solenoid Valve Open Time

const unsigned long ValveTimings  = {6666, 4444, 7777, 2222, 3333};
const byte NrValveTimings = sizeof(ValveTimings)/sizeof(ValveTimings[0]);
const unsigned long ValveInterval = 5000; //time between valve openings

unsigned long valveOpenMillis;
byte currentValveTiming;
bool valveState;

void setup(){
  Serial.begin(115200);
  Serial.println(F("Started"));
}

void loop(){
    updateValves();
}

void updateValves(){
  if(valveState){
    updateValvesClosing();
  }
  else{
    updateValeOpening();
  }
}

void updateValeOpening(){
  const unsigned long MillisNow = millis();
  if(MillisNow - valveOpenMillis > ValveInterval){
    valveOpenMillis = MillisNow;
   
    valveState = true;
    Serial.println("Valve On");
  }
}

void updateValvesClosing(){
  const unsigned long MillisNow = millis();
  if(MillisNow - valveOpenMillis > ValveTimings[currentValveTiming]){
    valveState = false;
    Serial.println("Valve Off");
   
    currentValveTiming++;
    if(currentValveTiming >= NrValveTimings){
      currentValveTiming = 0;
    }
  }
}

Thanks! Appreciate it. Will ask if a question come up! :slight_smile:

The reason that I was planning to use arrays is that I was hoping if it is possible that I can input each arrays for each relays like once in a day to change their timing since water demands changes and such. Would this be possible?

Yeah, why not? You can use an array just like any other variable. Only with the addition you can numerical access the different variables which makes it easy to loop over them to do the same stuff multiple times without having to write the same over and over.

I was planning to have the array as serial input (if this is possible?), an input will be done once in a day. If no input was detected that day, the code shall make use of a default array,

No, not directly. Serial is just a (pretty much randomly spaced in time) bunch or bytes. It has no start, it has no end, it's a stream. It has no notion of arrays or anything else than a byte.

But you can use that data stream by parsing it and assign it to the variable you want, which can be an array. Like I said, nothing you can do with a variable you can't do with an array (member). An example would be Robin2's excellent Serial Input Basics

Once again, Thank you for the response. Really helpful.

I changed the plan and instead was planning to have 4 inputs on this project. This is the "WaterDist" input but just an integer instead of array. Namely input1, input2, input3 and input4. The input1 corresponds to the time that valve1 will open. Input2 for Valve2 and so on. Is there any way that I can change the values of input2 or any values of the input without interrupting the running code or without changing the value on the code itself?

Say the situation is like this.
input1 has value of being open for 15minutes
input2 has value of being open for 20minutes
input3 has value of being open for 10minutes
input4 has value of being open for 5minutes

But I want to change that 20 minutes to 3 minutes on serial monitor. Without changing the values for others. And also without restarting the entire code since it's time sensitive?

if you now have variables called input1, input2, input3 and input4, change them to an array :wink:

Also, change there name to there function, not how you got it.

Yeah, you can change variables. But without all the code it's hard to say what it would require.

An example would be to send he string "2;3\n" to be interpreted as "set the time for valve 2 to 3 minutes".

Thanks for the tip :slight_smile:
Really appreciate it! Read the Serial Input Basics and used the example code that I read since i think it was the appropriate one. This is what I have so far. So basically I want to input which valve then how much water needed. Ex. <Valve1,3.2> .The amount of water is Distributed in the span of 24 hours. Converts distributed water to timing of opening the valves.

The valves wont update even after entering <Valve1,3.2> I cant seem to find the problem.

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use by strtok() function

// variables to hold the parsed data
char messageFromPC[numChars] = {0};
boolean newData = false;

// variables for amount of water
float WaterDist[] = {};
float AmountofWater=10; //default 10 upon starting
const byte dayhours=24;
byte i=0;

//
unsigned long ValveTimings[] = {};
const unsigned long ValveInterval = 5000; //time between valve openings
unsigned long valveOpenMillis;
byte currentValveTiming=0;
bool valveState;


//============

void setup() {
    Serial.begin(9600);
    Serial.println("Enter data in this style <Valve#,AmountofWater> Ex. <Valve1,3.2> ");
    Serial.println("Default amount of water is 10 upon starting.  < ,10> ");
}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) { 
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() replaces the commas with \0
        parseData(); 
        newData = false;
    }
    WaterDistribution();
    if (strcmp(messageFromPC, "Valve1") == 0)  // Check if string is equal to valve1
   { 
      Serial.print("Updating Valve1");
       updateValves();
   } 
   
}

//============ Receive AmountofWater data

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {

      // split the data into its parts
    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
  
    strtokIndx = strtok(NULL, ",");
    AmountofWater = atof(strtokIndx);     // convert this part to a float
    i=0;
}

//============ Amount of water will be distributed in the 24-hour span.
void WaterDistribution()
{
  while(i<dayhours)
    {
      if (i<1){
        WaterDist[i]=AmountofWater*0.6L; //60% of water on the first hour
        AmountofWater=AmountofWater-WaterDist[i];
        Serial.println(WaterDist[i]);
        i++;
      }
      else{
    WaterDist[i]=AmountofWater*0.2L; //20% of water on the remaining hours
    AmountofWater=AmountofWater-WaterDist[i];
    Serial.println(WaterDist[i]);
    i++;
      }
    }
}

//============
void updateValves(){
  if(valveState){
    updateValvesClosing();
  }
  else{
    updateValveOpening();
  }
}

//============
void updateValveOpening(){
  const unsigned long MillisNow = millis();
  if(MillisNow - valveOpenMillis > ValveInterval){
    valveOpenMillis = MillisNow;
    valveState = true;
    Serial.println("Valve On");
  }
}

//============
void updateValvesClosing(){
  const unsigned long MillisNow = millis();
  ValveTimings[currentValveTiming] = WaterDist[currentValveTiming] * (0.0111) * 60 * 1000; 
  //From the amount of water distributed, this will be converted to millisecond time. (Please dont mind the formula)
  if(MillisNow - valveOpenMillis > ValveTimings[currentValveTiming]){
    valveState = false;
    valveOpenMillis = MillisNow;
    Serial.println("Valve Off");    
    currentValveTiming++;
    
    if(currentValveTiming >= dayhours){
      currentValveTiming = 0;
    }
  }
}

//============

I have not looked at it in detail but:

float WaterDist[] = {};
unsigned long ValveTimings[] = {};

defines an empty arrays! But C has fixed size arrays so it will never grow. Indexing any item in that array will be invalid / an overflow.

It's fine not to initialize the values of the array but you need to give it at least a size.

septillion:
I have not looked at it in detail but:

float WaterDist[] = {};

unsigned long ValveTimings = {};



defines an empty arrays! But C has fixed size arrays so it will never grow. Indexing any item in that array will be invalid / an overflow.

It's fine not to initialize the values of the array but you need to give it at least a size.

Ohh, I never knew it until now. That kinda fixed my problem. Now it's sort of working now. Thanks! Appreciate the tips. Would like to ask guidance if now If I edit this code to operate 4 valves, with same concept.

Through analyzing my code, problem arises when I add another valve. When I choose for example Valve2 on the input, the Valve1 will stop updating I supposed. Kinda trap here.

Post your latest version.