Little Help for the offset time setting

Hi Guys,

Can you kindly help me with my project please. I already have successful code and setup to control the synchronization of two solenoid valves: both open and close together. Now I want to modify the code so that the first valve opens first, after an offset time of 0.2 second, the second valve opens. After a period time, the two valves close simultaneously together.

I tried to use the delay function, but it did not work. The delay code actually delayed the whole action together: the two valves still open and close together after the delay period. Can you help me with this please ? Here is my original code:

// Assign Pins 
const int Solenoid1outPin = 7;  // the output pin to control solenoid1
const int Solenoid2outPin = 6;  // the output pin to control solenoid2
const int ButtoninPin = 2;      // the input pin to read voltage (button)
const int Led1outPin = 12;      // the output pin to supply voltage (green LED)
const int Led2outPin = 11;      // the output pin to supply voltage (red LED)        

// Define variables 
int Led1State = LOW;      // the current state of green LED1
int Led2State = HIGH;     // the current state of red LED2
int ButtonState = LOW;
int Solenoid1State = LOW;  // the current state of solenoid1
int Solenoid2State = LOW;  // the current state of solenoid2
int Reading;              // the current reading from the input pin (button)
int LastReading = LOW;    // the previous reading from the input pin (button)
unsigned long CurrentTime = 0;     // set the clock
unsigned long ButtonTime = 0;
unsigned long Led1Time = 0;         // the last time LED1 was toggled 
unsigned long Led2Time =0;          // the last time LED2 was toggled
unsigned long Solenoid1Time = 0;    // the last time the output pin (solenoid1) was toggled 
unsigned long Solenoid2Time = 0;    // the last time the output pin (solenoid2) was toggled 
unsigned long DebounceTime = 200;   // the button debounce time, increase if the output flickers
unsigned long DischargeTime = 1000; // discharge time period

// Setup Pin mode
void setup()
{
  pinMode(Led1outPin, OUTPUT);
  pinMode(Led2outPin, OUTPUT);
  pinMode(Solenoid1outPin, OUTPUT);
  pinMode(Solenoid2outPin, OUTPUT);
  pinMode(ButtoninPin, INPUT_PULLUP);
}

// Setup Loop
void loop(){
  CurrentTime = millis();
  ButtonRead();
  Solenoid();
  LED();
  Switch();
}

void ButtonRead(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - ButtonTime > DebounceTime)
  {
    ButtonTime = millis();
    ButtonState = HIGH;
  }
  if ( Reading == HIGH && CurrentTime - ButtonTime > DischargeTime)
  {
    ButtonTime = millis();
    ButtonState = LOW;
  }
}

void Solenoid(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - Solenoid1Time > DebounceTime)
  {
    Solenoid1State = HIGH;
    Solenoid1Time  = millis();
    Solenoid2State = HIGH;
    Solenoid2Time  = millis();
  }
  if ( CurrentTime - Solenoid1Time > DischargeTime )
  {
    Solenoid1State = LOW;
    Solenoid1Time = millis();
    Solenoid2State = LOW;
    Solenoid2Time = millis();
  }
}

void LED(){
  if (ButtonState == HIGH)
  {
    Led1State = HIGH;
    Led2State = LOW;
  }
  if ( ButtonState == LOW )
  {
    Led1State = LOW;
    Led2State = HIGH;
  }
}

void Switch(){
  digitalWrite(Led1outPin, Led1State);
  digitalWrite(Led2outPin, Led2State);
  digitalWrite(Solenoid1outPin, Solenoid1State);
  digitalWrite(Solenoid2outPin, Solenoid2State);
}

Thank you very much for help !

Best, Jeff

You should not read buttoninPin in the function Solenoid(). Just use the value you already have. Keep the different activities in their separate functions.

Where you update a value for timing purposes eg

Solenoid1Time  = millis();

use, instead use

Solenoid1Time = CurrentTime;

so that all the timinings use the same base.

I can't figure out how your code is supposed to work - can you provide a description in plain language.

If you want a gap between one event and another just record millis() (or save CurrentTime) when the first event happens and use the usual

 if (CurrrentTime - eventAstartMillis >= eventGapMillis) {

to identify when the second event should start.

...R

Robin2:
I can't figure out how your code is supposed to work - can you provide a description in plain language.

Thank you for the reply. My objective is: only when press a push button, the first valve is activated, then after a defined time interval, the second valve is activated in sequence. Now both valves are activated. After another defined time interval following the activation of the second valve, both valve are de-activated / closed. I hope this is clear.

I wrote another code as below, now two valves can be activated in sequence, but they can not be closed after my defined time interval... Can you see why ?

void setup()
{
  pinMode(Led1outPin, OUTPUT);
  pinMode(Led2outPin, OUTPUT);
  pinMode(Solenoid1outPin, OUTPUT);
  pinMode(Solenoid2outPin, OUTPUT);
  pinMode(ButtoninPin, INPUT_PULLUP);
}

// Setup Loop
void loop(){
  CurrentTime = millis();
  ButtonRead();
  Solenoid1();
  Solenoid2();
  LED();
  Switch();
}

void ButtonRead(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - ButtonTime > DebounceTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = HIGH;
  }
  if ( Reading == HIGH && CurrentTime - ButtonTime > DischargeTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = LOW;
  }
}

void Solenoid1(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - Solenoid1Time >= DebounceTime)
  {
    Solenoid1State = HIGH;
    Solenoid1Time  = CurrentTime;
  }
  if ( CurrentTime - Solenoid2Time >= DischargeTime )
  {
    Solenoid1State = LOW;
    Solenoid1Time = CurrentTime;
  }
}

void Solenoid2(){
  if ( Solenoid1State == HIGH && CurrentTime - Solenoid1Time >= OffsetTime)
  {
    Solenoid2State= HIGH;
    Solenoid2Time = CurrentTime;
  }
  if ( Solenoid1State == HIGH && CurrentTime - Solenoid2Time >= DischargeTime )
  {
    Solenoid2State = LOW;
    Solenoid2Time = CurrentTime;
  }
}

void LED(){
  if (ButtonState == HIGH)
  {
    Led1State = HIGH;
    Led2State = LOW;
  }
  if ( ButtonState == LOW )
  {
    Led1State = LOW;
    Led2State = HIGH;
  }
}

void Switch(){
  digitalWrite(Led1outPin, Led1State);
  digitalWrite(Led2outPin, Led2State);
  digitalWrite(Solenoid1outPin, Solenoid1State);
  digitalWrite(Solenoid2outPin, Solenoid2State);
}

You can ignore the LED settings. Actually the LED is working as expected except the valves can not be closed.. Thank you very much.

Regards,
Jeff

Use of millis for controlling multiple things is the recommended way - it lets you keep your system responsive to user input while controlling whatever your outputs are.

However, if your full requirement is as described in the OP, this is trivial using delay. No need for debounce or recording times. When you detect a button press, activate the first solenoid. Delay 200ms. activate the second solenoid. Delay again. Deactivate them both. Done.

Post your code that didn't work with delay if you still have it.

wildbill:
Use of millis for controlling multiple things is the recommended way - it lets you keep your system responsive to user input while controlling whatever your outputs are.

However, if your full requirement is as described in the OP, this is trivial using delay. No need for debounce or recording times. When you detect a button press, activate the first solenoid. Delay 200ms. activate the second solenoid. Delay again. Deactivate them both. Done.

Post your code that didn't work with delay if you still have it.

Thank you for help here. I thought about delay but it did not work, I think I put the delay at the wrong place in the loop. Another reason that I prefer to use millis is that in my project, I need to change the defined delay time after every time I run the valves, so I prefer to learn to use millis. Can you see why code is not working ?

The two valves can be activated in sequence as the defined 'offset time', but after the 'discharge time', they are not closed....

Thank you.

Hard to say - your latest version is missing all your const stuff from the first post and I can't see OffsetTime defined anywhere.

At this point, I'd start putting serial.println statements in the button & solenoid functions to find out where the execution goes.

Jeff27:
Thank you for help here. I thought about delay but it did not work, I think I put the delay at the wrong place in the loop. Another reason that I prefer to use millis is that in my project, I need to change the defined delay time after every time I run the valves, so I prefer to learn to use millis. Can you see why code is not working ?

The two valves can be activated in sequence as the defined 'offset time', but after the 'discharge time', they are not closed....

Thank you.

wildbill:
Hard to say - your latest version is missing all your const stuff from the first post and I can't see OffsetTime defined anywhere.

At this point, I'd start putting serial.println statements in the button & solenoid functions to find out where the execution goes.

Sorry I should post all my code. Actually I have been thinking my code just now, and with my latest version, I think it will work now (I will test it tomorrow). Please see my code:

// Assign Pins 
const int Solenoid1outPin = 7;  // the output pin to control solenoid1(gate valve)
const int Solenoid2outPin = 6;  // the output pin to control solenoid2(pump valve)
const int ButtoninPin = 2;      // the input pin to read voltage (button)
const int Led1outPin = 12;      // the output pin to supply voltage (green LED)
const int Led2outPin = 11;      // the output pin to supply voltage (red LED)        


// Define variables 
int Led1State = LOW;      // the current state of green LED1
int Led2State = HIGH;     // the current state of red LED2
int ButtonState = LOW;
int Solenoid1State = LOW;  // the current state of solenoid1(gate valve)
int Solenoid2State = LOW;  // the current state of solenoid2(pump valve)
int Reading;              // the current reading from the input pin (button)
int LastReading = LOW;    // the previous reading from the input pin (button)
unsigned long CurrentTime = 0;     // set the clock
unsigned long ButtonTime = 0;
unsigned long Led1Time = 0;         // the last time LED1 was toggled 
unsigned long Led2Time =0;          // the last time LED2 was toggled
unsigned long Solenoid1Time = 0;    // the last time the output pin (solenoid1) was toggled 
unsigned long Solenoid2Time = 0;    // the last time the output pin (solenoid2) was toggled 
unsigned long OffsetTime = 500;       // the offset time between valve 1 and valve 2 
unsigned long DebounceTime = 200;   // the button debounce time, increase if the output flickers
// Setup desired valve's openning time period before each run 
unsigned long DischargeTime = 4000; // discharge time period

// Setup Pin mode
void setup()
{
  pinMode(Led1outPin, OUTPUT);
  pinMode(Led2outPin, OUTPUT);
  pinMode(Solenoid1outPin, OUTPUT);
  pinMode(Solenoid2outPin, OUTPUT);
  pinMode(ButtoninPin, INPUT_PULLUP);
}

// Setup Loop
void loop(){
  CurrentTime = millis();
  ButtonRead();
  Solenoid1();
  Solenoid2();
  LED();
  Switch();
}

void ButtonRead(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - ButtonTime >= DebounceTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = HIGH;
  }
  if ( Reading == HIGH && CurrentTime - ButtonTime >= DischargeTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = LOW;
  }
}

void Solenoid1(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - Solenoid1Time >= DebounceTime)
  {
    Solenoid1State = HIGH;
    Solenoid1Time  = CurrentTime;
  }
  if ( CurrentTime - Solenoid2Time >= DischargeTime )
  {
    Solenoid1State = LOW;
    Solenoid1Time = CurrentTime;
  }
}

void Solenoid2(){
  if ( Solenoid1State == HIGH && CurrentTime - Solenoid1Time >= OffsetTime)
  {
    Solenoid2State= HIGH;
    Solenoid2Time = CurrentTime;
  }
  if ( CurrentTime - Solenoid2Time >= DischargeTime )
  {
    Solenoid2State = LOW;
    Solenoid2Time = CurrentTime;
  }
}

void LED(){
  if (ButtonState == HIGH)
  {
    Led1State = HIGH;
    Led2State = LOW;
  }
  if ( ButtonState == LOW )
  {
    Led1State = LOW;
    Led2State = HIGH;
  }
}

void Switch(){
  digitalWrite(Led1outPin, Led1State);
  digitalWrite(Led2outPin, Led2State);
  digitalWrite(Solenoid1outPin, Solenoid1State);
  digitalWrite(Solenoid2outPin, Solenoid2State);
}

If the code is executed in sequence, e.g. loop goes through code Solenoid1 first, then moves to code Solenoid2 (which I think is the case), then I think I should get this right this time.....

I hope you can see my initiative now: when press button, valve 1 is activated, after the offset time, valve 2 is activated. After elapsing the discharge time since the activation of the valve 2, both valves are shut off.

Thank you very much.

... OMG I am not so sure now, maybe my latest code still wont work, I am so fukced up with the Time.

Because when I ran my previous code early today:

void Solenoid1(){
Reading = digitalRead(ButtoninPin);
if ( Reading == LOW && CurrentTime - Solenoid1Time >= DebounceTime)
{
Solenoid1State = HIGH;
Solenoid1Time = CurrentTime;
}
if ( CurrentTime - Solenoid2Time >= DischargeTime )
{
Solenoid1State = LOW;
Solenoid1Time = CurrentTime;
}
}

void Solenoid2(){
if ( Solenoid1State == HIGH && CurrentTime - Solenoid1Time >= OffsetTime)
{
Solenoid2State= HIGH;
Solenoid2Time = CurrentTime;
}
if ( Solenoid1State == HIGH && CurrentTime - Solenoid2Time >= DischargeTime )
{
Solenoid2State = LOW;
Solenoid2Time = CurrentTime;
}
}

The two valves could be activated as expected, but after that they would not turn off. Now I compare my latest code with this early one above, I still don't know why the valves especially the valve 1 can not be turned off. Since they open nicely as expected, I believe the Solenoid2Time is updated to the CurrentTime when is activated. So why when CurrentTime - Solenoid2Time >= DischargeTime, the valve 1 can not be turned off ?

wildbill:
Hard to say - your latest version is missing all your const stuff from the first post and I can't see OffsetTime defined anywhere.

At this point, I'd start putting serial.println statements in the button & solenoid functions to find out where the execution goes.

Can you please tell me how to put the serial.printIn in the statement ?

If you still want a response to Reply #2, please let me know.
I notice that you did not take up my suggestion to remove Reading = digitalRead(ButtoninPin); from the Solenoid1() function.

It was a fairly fundamental suggestion as it would have forced you to reassess other bits - which might have led to success.

...R

Robin2:
If you still want a response to Reply #2, please let me know.
I notice that you did not take up my suggestion to remove Reading = digitalRead(ButtoninPin); from the Solenoid1() function.

It was a fairly fundamental suggestion as it would have forced you to reassess other bits - which might have led to success.

...R

Hi Robin, thanks for helping.

My initiative is that when press the button the first valve is activated, after a time interval the second valve is activated in sequence. Then after both valves been activated from a time interval, they all close at the same time.

Removing the variable Reading=digitalRead(ButtonPin) from the solenoid, do you suggest using the iF ButtonState == LOW instead ? what is the difference here ? Can you see any mistakes in my latest code ?

Thank you.

At its most simple, you can just do:

Serial.println("1");

At intervals in your call, varying the numbers of course.
Put one in each of the blocks controlled by if statements in buttonpress and the solenoid functions. It might help to print currentTime there too.

wildbill:
At its most simple, you can just do:

Serial.println("1");

At intervals in your call, varying the numbers of course.
Put one in each of the blocks controlled by if statements in buttonpress and the solenoid functions. It might help to print currentTime there too.

There is no Serial.printIn statement (shows error when I write this statement in the if blocks), only Serial.print. When I tried the latter, it only gives me 0 and 1 digits..

void setup()
{
  pinMode(Led1outPin, OUTPUT);
  pinMode(Led2outPin, OUTPUT);
  pinMode(Solenoid1outPin, OUTPUT);
  pinMode(Solenoid2outPin, OUTPUT);
  pinMode(ButtoninPin, INPUT_PULLUP);
  Serial.begin(9600);
}

// Setup Loop
void loop(){
  CurrentTime = millis();
  ButtonRead();
  Solenoid1();
  Solenoid2();
  LED();
  Switch();
}

void ButtonRead(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - ButtonTime >= DebounceTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = HIGH;
    Serial.print("5");
  }
  if ( Reading == HIGH && CurrentTime - ButtonTime > DischargeTimeTotal)
  {
    ButtonTime = CurrentTime;
    ButtonState = LOW;
    Serial.print("6");
  }
}

void Solenoid1(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW )
  {
    Solenoid1State = HIGH;
    Solenoid1Time  = CurrentTime;
    Serial.print("1");
  }
  if ( CurrentTime - Solenoid1Time >= DischargeTimeTotal )
  {
    Solenoid1State = LOW;
    Solenoid1Time = CurrentTime;
    Serial.print("2");
  }
}

void Solenoid2(){
  if ( Solenoid1State == HIGH && CurrentTime - Solenoid1Time >= OffsetTime)
  {
    Solenoid2State= HIGH;
    Solenoid2Time = CurrentTime;
    Serial.print("3");
  }
  if ( CurrentTime - Solenoid1Time >= DischargeTime )
  {
    Solenoid2State = LOW;
    Solenoid2Time = CurrentTime;
    Serial.print("4");
  }
}

I know I did not do this correctly.

Jeff27:
Can you see any mistakes in my latest code ?

There seem to be at least two different conversations going on here. I'm not certain, but I think the other conversation is using a different approach.

I am happy to stay clear if you are getting satisfactory advice from others. There does not seem to be any point in solving your problem twice.

However if you prefer the direction I was going, let me know.

...R

Robin2:
There seem to be at least two different conversations going on here. I'm not certain, but I think the other conversation is using a different approach.

I am happy to stay clear if you are getting satisfactory advice from others. There does not seem to be any point in solving your problem twice.

However if you prefer the direction I was going, let me know.

...R

Hi Robin, I definitely appreciate your help here. And I have finally got my code working (I am in the lab testing it now). My final code is:

void loop(){
  CurrentTime = millis();
  ButtonRead();
  Solenoid1();
  Solenoid2();
  LED();
  Switch();
}

void ButtonRead(){
  Reading = digitalRead(ButtoninPin);
  if ( Reading == LOW && CurrentTime - ButtonTime >= DebounceTime)
  {
    ButtonTime = CurrentTime;
    ButtonState = HIGH;
    Serial.print("5");
  }
  if ( Reading == HIGH && CurrentTime - ButtonTime > DischargeTimeTotal)
  {
    ButtonTime = CurrentTime;
    ButtonState = LOW;
    Serial.print("6");
  }
}

void Solenoid1(){
  Reading = digitalRead(ButtoninPin);
  if (  Reading == LOW && CurrentTime - Solenoid1Time >= DebounceTime)
  {
    Solenoid1State = HIGH;
    Solenoid1Time  = CurrentTime;
    Solenoid2Time = CurrentTime;
    Serial.print("1");
  }
  if ( Reading == HIGH && CurrentTime - Solenoid1Time >= DischargeTimeTotal )
  {
    Solenoid1State = LOW;
    Solenoid1Time = CurrentTime;
    Serial.print("2");
  }
}

void Solenoid2(){
  if ( Solenoid1State == HIGH && CurrentTime - Solenoid1Time >= OffsetTime)
  {
    Solenoid2State= HIGH;
    Serial.print("3");
  }
  if ( Solenoid1State == LOW && CurrentTime - Solenoid2Time >= DischargeTimeTotal )
  {
    Solenoid2State = LOW;
    Solenoid2Time = CurrentTime;
    Serial.print("4");
  }
}

void LED(){
  if (ButtonState == HIGH)
  {
    Led1State = HIGH;
    Led2State = LOW;
  }
  if ( ButtonState == LOW )
  {
    Led1State = LOW;
    Led2State = HIGH;
  }
}

void Switch(){
  digitalWrite(Led1outPin, Led1State);
  digitalWrite(Led2outPin, Led2State);
  digitalWrite(Solenoid1outPin, Solenoid1State);
  digitalWrite(Solenoid2outPin, Solenoid2State);
}

It took me 2 hours to realize that I just need to relocate the statement Solenoid2Time to the Solenoid1 Control loop. So basically toggle the Solenoid2Time when the solenoid 1 is activated, and only update Solenoid2Time when solenoid 2 is turned off.

Although it seems that I have solved my problem for now, I am also very interested to know all the approaches. But I just don't know if I remove the digitalRead(ButtoninPIn) from the solenoid 1 loop, how can I activate the solenoid valves at the right time when I press the button ? Because if I use the ButtonState == HIGH, that Button state is always HIGH during the discharge time, does this mean that the code will try to update the Solenoid 1 time all the time during the discharge time ? Maybe your different approach is not like this, but can you give me a example by modifying my code ?

Regards,
Jeff

Jeff27:
if I remove the digitalRead(ButtoninPIn) from the solenoid 1 loop, how can I activate the solenoid valves at the right time when I press the button ? Because if I use the ButtonState == HIGH, that Button state is always HIGH during the discharge time, does this mean that the code will try to update the Solenoid 1 time all the time during the discharge time

If you look at the demo several things at a time and the Thread planning and implementing a program you will see how I have a function for reading buttons and saving their values and other functions that work based on the values saved.

The buttons are read on every iteration of loop() - probably thousands of times per second - so the button values are always up to date.

...R