How can I call class fxns sequentially? Can I call class objects with variable?

Hello, thanks in advance for any help you can provide.

I have 6 solenoid valves to control so I have set up a class to manage each valve, called “Valve”, with an object for each valve which designates the pins ( ie first.Valve(int open_pin, int close_pin)).

Then I have member functions to do stuff such as open the valve (ie first.open) or close the valve (first.close), etc.

I’m trying to get the valves to open sequentially for a specified time ie valve 1 for 1 min, valve 2 for 1 min, valve 3 for one min. I am familiar with the millis method for timing, but this turns into a lot of code if I have to set up an if statement for the timing of each of the 6 valves.

open valve 1
if time has elapsed
close valve 1
open valve 2
if time has elapsed
close valve 2
open valve 3
etc.

There are many different sequences that will be needed, so I’m looking for a better way.

I envisioned something like this:

class:
Valve

Objects: Each represents a different physical valve
1.Valve
2.Valve
3.Valve
4.Valve
5.Valve
6.Valve

Member functions:
open();
close();

Then use a single “if millis” type statement and somehow use a variable to progress to the next valve (next member function) such as:

void loop()

if (cycle_running = true)
{
cycle();
}

void cycle()

if (run_once == false) // the first time cycle() runs, it will set the x variable and open the first valve
{
x=1;
run_once = true; // Ensure first if statement isn’t run again
x.open(); //initially will open valve 1 (1.open, 2.open, etc)
}

if (x<= 6) // stop the cycle from running if all 6 valves have been run
{

if currentmillis >= timeperiod //check time
{
x.close(); // When time has elapsed, close the valve (1.close, 2.close, etc)
x++; // Advance x by one number
x.open(); // open the next valve
}

}

else
{
cycle_running = false; // Stops the loop function from calling cycle after the 6 valves have run
}

but it doesn’t seem that you can use a number for a member name (1.valve), or use a variable in a call to a function ie x.function.

I also thought about using a for loop with a variable for the specific member function.

for (x=1; x<=6; x++)
{

x.open();
delay (60000);
x.close();
}

but again, this doesn’t work due to using a variable for a function call. And, the delay function halts the processor.

So, is there some trick to use a variable in a the call to a function, or another way to progress from one member function to another, or another established method for to achieve my goal?

Sounds like you just need an array of Valve objects.

You can't have a number for a name and you can't expect your names to make any sense at runtime. So that's why arrays were created.

Thanks for the help, I’ve been playing with arrays, but can’t figure out how to call specific functions.
I think part of the problem is that I’m struggling to figure out the nomenclature for classes. I’ve been searching for a tutorial that clearly lays out the names for the various parts.

If I have

Class:
Valve (int open_pin, int close_pin)

Functions:

open();
close();

Then in my main sketch, before my ‘void setup()’ I initialize the class:

Valve Water(1, 2) // construct valve one ← What are these called? Are these Objects? Or instances of the
class?
Valve Milk(3 ,4) // construct valve two
Valve Tea(5, 6)

In my ‘void loop’ I can call the function

water.open(); // ← What is this called? Is this the “water” object function? Or the “Water” instance function?
delay(1000);
water.close();

I’ve found methods using arrays to call functions such as

typedef void (*FuncPtr)(void);
FuncPtr myFunctions={&myFunction1, &myFunction2, &myFunction3,&myFunction4, &myFunction5,&myFunction6};

from https://forum.arduino.cc/index.php?topic=172300.0

But I can’t figure out how to specify which object the function is called from.
ie, I can call Valve::open, but how do I specify water.open vs tea.open using an array.

Here’s a mockup of my code.

Valve.h
########################
#ifndef Valve_h
#define Valve_h

#include "Arduino.h"


typedef void (*O_array) ();   
extern O_array O_call[];

typedef void (*C_array) ();
extern C_array C_call[];


class Valve
{
   public:
       Valve(int Opin, int Cpin);
   
      void Open();
      void Close();
      
private:

  int open_pin;
  int close_pin;

};

#endif
Valve.cpp
#########################

#include "Arduino.h"
#include "Valve.h"



int Opin;
int Cpin;




Valve::Valve(int Opin, int Cpin)
{
  

  open_pin = Opin;
  close_pin = Cpin; 

     pinMode(open_pin, OUTPUT);     
     pinMode(close_pin, OUTPUT); 


}

void Valve::Open()
{
  
      digitalWrite(open_pin, HIGH);  // Start the pulse to open the valve
      delay(500);
      digitalWrite(open_pin, LOW);
}


void Valve::Close()
{
  
      digitalWrite(close_pin, HIGH);  // Start the pulse to open the valve
      delay(500);
      digitalWrite(close_pin, LOW);
}

}
Main Sketch
##############################

#include "Valve.h"

// Initialize three parts of the Valve class
Valve water(1, 2);
Valve milk(3, 4);
Valve tea(5, 6);

O_array O_call[] = {water.Open, milk.Open, tea.Open};     //establishes the open array
C_array C_call[] = {water.Close, milk.Close, tea.Close};  //establishes the close array

int x;

void setup() {

// Set up all the things
}




void loop() {

// I want this to run water.open, delay 1 min, run water.close, then run milk.open, delay, etc

for (x=1; x<=3; x++)
{

O_call[x]();    // Calls the open function from the array
delay (60000);
C_call[x]();    // Calls the close function from the array

}

}

This give me the error cannot convert ‘Valve::Open’ from type ‘void (Valve::)()’ to type ‘O_array {aka void (*)()}’

Since I don’t know what these suptypes of functions are called (water.open, milk.close etc) I haven’t been able to effectively search out the answer on my own so any direction anyone can offer would be appreciated.

You're WAY overthinking this. You need an array of your Valve objects.

Valve myValves[] = {water, milk, tea};

(myValves[1]).open(); //. Open the milk valve

Or to save a little memory make an array of pointers to the various Valve objects. But either way just make an array of the objects you already have so you can number them.

Cemtes:
There are many different sequences that will be needed, so I'm looking for a better way.

To clarify, does this mean a sequence could be 1234, or 1342, or etc.? Or is it just the timings which vary?

Delta_G:
You're WAY overthinking this. You need an array of your Valve objects.

Valve myValves[] = {water, milk, tea};

(myValves[1]).open(); //. Open the milk valve




Or to save a little memory make an array of pointers to the various Valve objects. But either way just make an array of the objects you already have so you can number them.

Wow, I feel like an idiot. That is so much simpler than I was making it. Thanks a lot for your help, I've spent days trying to figure this out and you just solved it for me.

I've been reading a bit about arrays and pointers, but I'm still wrapping my head around it. Would you be willing to give me an example using my mock code?

dougp:
To clarify, does this mean a sequence could be 1234, or 1342, or etc.? Or is it just the timings which vary?

Right now, just the timing will change, but eventually, I might be changing the order as well. The 'for loop' in my mock code isn't the actual method I'm using, just an easy example for illustration. My actual code is a nasty pile of nested 'if' statements but I didn't want people to waste time sorting through it when it's not really relevant to my underlying question.

Did you have a solution in mind? I'm curious to learn as many approaches to this as possible.

Cemtes:
Did you have a solution in mind? I'm curious to learn as many approaches to this as possible.

Not particularly. It's just that your description didn't quite align with your examples. However, if the sequence is fixed the switch/case construct might be worth a look.

Valve::Valve(int Opin, int Cpin)
{
  

  open_pin = Opin;
  close_pin = Cpin; 

     pinMode(open_pin, OUTPUT);     
     pinMode(close_pin, OUTPUT); 


}

Don't call pinMode from the constructor. The constructor could run before init sets up the hardware. If you need to do hardware things then you need a begin or init method that you can call from setup.

Cemtes:
I’ve been reading a bit about arrays and pointers, but I’m still wrapping my head around it. Would you be willing to give me an example using my mock code?

I felt like I gave it all to you there. If you really need it written into the code here it is. I don’t see what’s different here…

Main Sketch
##############################

#include "Valve.h"

// Initialize three parts of the Valve class
Valve water(1, 2);
Valve milk(3, 4);
Valve tea(5, 6);

Valve* myValves[] = {&water, &milk, &tea);

int x;

void setup() {

// Set up all the things
}




void loop() {

// I want this to run water.open, delay 1 min, run water.close, then run milk.open, delay, etc

for (x=0; x<3; x++)
{
myValves[x]->open();
delay (60000);
myValves[x]->close();
}

}

Note the fix to the for loop. How you loop over arrays is very important. You have three elements in this array. they are numbered 0, 1, and 2.

Delta_G:
I felt like I gave it all to you there. If you really need it written into the code here it is. I don't see what's different here...

Thank you, you did give it all, your first post was a great example of arrays and allowed me to complete my code.

I should have been more clear in my followup question, I was specifically looking for how to add pointers to the array to save memory. I'd read about pointers, but wasn't sure how to combine them with arrays, and I thought maybe there was something special I had to do for the memory savings.

With your example and subsequent reading, I think I have a better grasp of this.

Thanks for your time and multiple examples. Without your help, I wouldn't have been able to complete my project.

Edit
Implementing pointers throughout my code decreased my dynamic memory use from 74% to 59%. That's amazing. I can't tell you how much you've helped me. Thanks again!