Turn bunch of devices on and off with specific patterns.

I am working on a project that requires me to tun on and off about 12 solenoid valves with different patterns. I wrote the code that does this and now I am trying to implement a functionality that will allow me to switch between these patterns by using a IR remote, but I cant figure out how to write a code for this. Here is the code that I have so far.

#include <IRremote.h>
int input_pin = 28; //set D10 as input signal pin
IRrecv irrecv(input_pin);
decode_results signals;

class Flasher
{

  public:
  Flasher(int pin, long on, long off)
  {
  ledPin = pin;
  pinMode(ledPin, OUTPUT);     
    
  OnTime = on;
  OffTime = off;
  
  ledState = LOW; 
  previousMillis = 0;

  digitalWrite(ledPin, HIGH);
  }
  
  
  void setTimes(long newOn, long newOff);

 
 
  void Update()
  {
    // check to see if it's time to change the state of the LED
    unsigned long currentMillis = millis();
     
    if((ledState == LOW) && (currentMillis - previousMillis >= OnTime))
    {
      ledState = HIGH;  // Turn it off
      previousMillis = currentMillis;  // Remember the time
      digitalWrite(ledPin, ledState);  // Update the actual LED
    }
    else if ((ledState == HIGH) && (currentMillis - previousMillis >= OffTime))
    {
      ledState = LOW;  // turn it on
      previousMillis = currentMillis;   // Remember the time
      digitalWrite(ledPin, ledState);   // Update the actual LED
    }
  }

 
  
  private:
  int ledPin;    
  long OnTime;  
  long OffTime;   
 

  int ledState;              
  unsigned long previousMillis;   
  
};

void Flasher::setTimes(long newOn, long newOff)
{
  OffTime = newOff;
  OnTime = newOn;
}



 
 unsigned long time;

 

 int onT = 2500;
 int offT = 2500;
 
 
  //Flasher led1(portNo, OnTime, OffTime)
Flasher led1(22, onT, offT);
Flasher led2(23, onT, offT);
Flasher led3(24, onT, offT);
Flasher led4(25, onT, offT);
Flasher led5(26, onT, offT);
Flasher led6(27, onT, offT);

 
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // enable input from IR receiver
}


int chooseOption =0;
 
void loop()
{
	
time = millis();
  
    if (irrecv.decode(&signals)) 
    {
      if(signals.value == 0x9716BE3F)
      {
        chooseOption = 1;
       
      }
      else if(signals.value == 0x3D9AE3F7)
      {
        chooseOption = 2;

      }

      irrecv.resume(); // get the next signal
    }

 //So here for example I can do something like


if(time > 5000)
  {
    led1.Update();
    led3.Update();
    led5.Update();
  
  }
  if(time >7500)
{
     led2.Update();
     led4.Update();
     led6.Update();


//and pumps 1 3 and 5 are on for 2.5 secs and then pumps 2,4 and 6 are on and so on.


     
     
}

Now lets say I want to be able to have all pumps on when chooseOption == 1 so I will do something like

if(chooseOption == 1)
{
    led1.Update();
    led3.Update();
    led5.Update();
    led2.Update();
    led4.Update();
    led6.Update();

}

and I want the above mentioned alternating configuration when chooseOption is 2 lets say. How can I do this?

Thank you.

A simple way is to create one function per low level pattern (a unit) which takes a pointer to the start time and a Boolean as input Parameter and tracks its inner timing against that start time to decide what to do when called. That is in the function you subtract to current time the start time, that gives you an absolute standard time reference - time T=0 start of time. you call the function and each time you call T will be a few milliseconds later and based on this reference time decide what to do in the pattern. If you need a memory local to that function that is persistent across calls, make sure to declare those variables as static within the pattern function (and reset it only when pattern function is finished for next new start)

Plan the case of emergency stop, that's the purpose of the second parameter: if false, run as normal if true implement an emergency stop procedure like turning off all your valves. The pattern function needs to return a Boolean stating if it is finished with executing the pattern or not. (Return false if it has more things to do, return true if the pattern is finished).

If your pattern needs to run continuously until being asked to stop by the IR remote, then just never return true. To make it easy to repeat endlessly, You can set the startTime to current time at the end of your Pattern and return false (= I'm not finished), this way the next time your pattern function is called, the timing calculation will be like you just started. That's why you pass the address of the startTime and not just the startTime so that you can modify the value in memory (alternatively don't pass start time at all and make that a global variable any function can play with).

When you want to do more complicated patterns which are combinations of unit patterns, just create a similar function as unit pattern and deal with time inside that meta pattern. That lets you build higher and higher level of abstraction. Low level patterms can just deal with 1 valve or LED, more complex ones combine low level actions and time.

You need to define a special key on your remote for emergency stop.

Your main loop just tracks the IR click and decides what to do:

=======
Have a variable to track state, initial value should be IDLE
Have a variable Boolean emergencyStop to false

Check click
If a click is done

If it's not the emergency stop button
If the state is not idle call the current pattern with the emergencyStop Flag set to finish previous pattern
set state to PATTERN1 or PATTERN2 based on click recognized, record the click time as startTime and set emergencyStop to false

If it's the emergency stop and state is not IDLE then set emergencyStop to true

do a switch case on pattern

Case state is IDLE Do nothing

Case state is PATTERN1
Call pattern1(&startTime, emergencyStop) and check the value returned. If the value is true then you know that that pattern is done, reset state to IDDLE to not call that pattern again at next loop.

Case state is PATTERN2
Call pattern2(&startTime, emergencyStop) and check the value returned. If the value is true then you know that that pattern is done, reset state to IDDLE to not call that pattern again at next loop.

Etc...

=====

The main loop does not need to know if you call a unit pattern or meta pattern, you implement that into a pattern function. As you can see the cases are all the same, so it's easy to build an array with a key and matching pattern to call and instead of doing a big case function you can just lookup the key and find the associated function to call, so that adding patterns to key is just a matter of modifying an array in memory, not your code. This way you can even have a user interface where you define yourself which key is associated to which pattern :slight_smile:

Hope this helps.

Extra work: plan for panic mode, what happens if your arduino looses power and you have valves open with water running. What will close the valves?

This is unlikely to work as you want

 //So here for example I can do something like

if(time > 5000)
  {
 
  }
  if(time >7500)
{

     
}

Because if the second one is true so will the first one. Perhaps something like

if (time > 7500) {

}
else if (time > 5000) {

}

If you want to have a series of optional patterns maybe you could store then in an array - something like
patterns = {{1,3,6,8}, {2,4,2,2})
(I probably don't have the syntax exactly right - but you should get the idea)
and then you can change the behaviour by selecting from the array and you just need a single function to produce all the patterns. Something like this pseudo code

void implementPattern(patternSelected) {
    ledA.update(patterns[patternSelected][0];
    ledB.update(patterns[patternSelected][1];
    // etc
}

if there were an array of Leds you could probably do the whole thing with a single line of code.

...R

if(time > 500)
{

}
if(time > 7500)
{

}

is exactly what I need in some cases actually. I was doing this so that all my pumps are on and they turn on and turn off with difference of 2.5 seconds.