Help programing - Turn on/off based on input

Can someone help me with coding? Below is what I have, but I would like to replace the delay() function to millis()

Any help would be appreciated :slight_smile:

int relayPin = 13; // relay connecte to digital pin 13
int inPin = 7; // sensor input source connecte to digital pin 7
int val = 0; // variable to store the read value

void setup()
{
// initializes
pinMode(relayPin, OUTPUT); // sets the digtial pin 13 as output
pinMode(inPin, INPUT); // sets the digital pin 7 as input
}

void loop()
{
val = digitalRead(inPin); // read the input pin

if(val == HIGH)
{
delay(10000); //wait 10secs.
digitalWrite(relayPin, HIGH);
}
else
{
delay(2000); //remain on for 2secs
digitalWrite(relayPin, LOW);
}

}

Firstly you want to record the times at which things are to happen, not actually wait.

  if (val == HIGH)
  {
    relay_turn_on = true ;
    relay_on_time = millis () + 10000 ;
  }
  if (val == LOW && relay_turn_on)
  {
    relay_turn_off = true ;
    relay_off_time = millis () + 2000 ;
  }

And you also need to check if these events are due

  if (relay_turn_on && millis () - relay_on_time >= 0)
  {
     digitalWrite (relayPin, HIGH) ;
     relay_turn_on = false ;
  }
  if (relay_turn_off && millis () - relay_off_time >= 0)
  {
    digitalWrite (relayPin, LOW) ;
    relay_turn_off = false ;
    relay_turn_on = false ;  // cancel pending turn on event since we've just turned it off.
  }

Something like that - although I haven't tested this and I'm a bit wary about how the events interact as they are not independent - I've assumed that the need to turn off the relay is only if its already scheduled to turn on - but I'm not sure exactly what you want the behaviour to be.

Here is the basic idea what I wanted the program to do. I have sensor that monitor the water level, High in air and Low in water.

So when water low; sensor is High, turn on relay (pump) but before it turn on, because water fluctuate delay for 10secs before it really turn on. Once it is on remain on for 2secs and then shut off relay.

I also would like a the relay to stay on for 30secs after that program should stop execute until manually Reset.

Not quite sure I understand the requirement. Is it "if the sensor is confirmed high over a ten second period, run the pump for 2 seconds, but limit total pumping until a restart to 30 seconds?"

That would certainly give some protection against a flood caused by the level sensor going bad. Once you have that working, you might consider instituting a daily (or longer) pumping limit so you don't have to do the manual restart so frequently. Also consider whether you need to defend against the arduino getting reset if the power goes out.

2 seconds plus whatever time it take for the pump to fill up the water to reach the sensor (sensor read Low). In other words after sensor comfirmed High, run the pump, fill up the water, sensor read Low...remain on for 2 seconds before shut off.

The 30 seconds (just a make up number for now) is the fault protection is for the bad sensor or the water reservoir empty, this to prevent the pump running too long when water is empty.

So in theory, I dont have to reset as often...unless water run out or sensor go bad.

I think you're going to need some extra logic to handle the sensor bad/out of water condition. Otherwise, when one of those things occurs, you're going to run the pump for 30 seconds every time you check the level sensor, possibly burning out the pump once it exhausts the reservoir.

Well 30 seconds is the cut off time, if pump still on (meaning no water in the reservoir) shut it off. Assuming it take 20 seconds to fill up the water, so it never have to reach the 30 seconds limit.

Does that make sense?

Not entirely. Now I think it's "if the sensor is confirmed high over a ten second period, run the pump until sensor is low and then for another 2 seconds. If that pumping is still happening after 30 seconds, stop the pump and use the infinite loop to stop all subsequent operations until reset"

Yes! that is pretty much what I wanted to do :slight_smile:

Requirement is defined (pretty much) so it's time to look at state machines - you'll find examples in the forums. Enumerate your states (I count five), figure out what you have to do in each state, what causes transitions between them and what to do when you transition. Loop() becomes a switch statement with the states as cases. Add in some variables to tell you when timed events started and you're most of the way there.

To use a timer instead of a delay, you use code like MarkT showed already.

If you're short on space, one way to manage is to use a variable for each item's timer, and use zero to represent an unused event (that is, not currently active).

For example, say you want a light on for 5 seconds, then turn off. Use a variable 'timerLight' to hold the milliseconds to stop in the future:

unsigned long timerLight=0;
void loop()
{
  unsigned long msec=millis();
  if ( ourNotYetWrittenHelperFunctionToDetectIfWeWantTheLightOn() )
  {
    timerLight=msec+5000; // when to stop in the future

// now turn on light

  }
  if ( timerLight && timerLight<msec ) // time to turn off?
  {
    timerLight=0;// turn off timer so we don't check again
        
// turn off light (somehow)
    
  }
}

Now the code loop will stay active without delays, yet still catch events.

You can also gang up the events - for example, after you turn off one light, you could turn on another:

unsigned long timerLight1=0, timerLight2=0;
void loop()
{
  unsigned long msec=millis();
  if ( ourNotYetWrittenHelperFunctionToDetectIfWeWantTheLightOn() )
  {
    timerLight1=msec+5000; // when to stop in the future

// now turn on light

  }
  if ( timerLight1 && timerLight1<msec ) // time to turn off 1st light?
  {
    timerLight1=0;// turn off timer so we don't check again
        
// turn off light1 (somehow)

    // now next light - turn it off in 10 seconds
    timerLight2=msec+10000; // when to stop #2 in the future

// turn ON light2 (somehow)

    
  }
  if ( timerLight2 && timerLight2<msec ) // time to turn off 2nd light?
  {
    timerLight2=0;// turn off timer so we don't check again
        
// turn off light2 (somehow)
    
  }
}

The result is a you can a number of events in whichever order you want - just remember to add on and off code in the appropriate sections.

Thanks for the feedback guys.

My computer currently not working, so will have to wait few days to test out the codes.

Ok! I got this codes to do what I wanted, but I need a timeout code within this codes. I want to say if "val" still read HIGH after 1 minute, write LOW to relayPin.

Can someone help me?

unsigned long relay_on_time;
unsigned long loopTime;

int relayPin = 13; // relay connected to digital pin 13
int inPin = 7; // sensor input source connected to digital pin 7
int val = 0; // variable to store the read value

void setup()
{
relay_on_time = millis();
loopTime = relay_on_time;

pinMode(relayPin, OUTPUT); // sets the digtial pin 13 as output
pinMode(inPin, INPUT); // sets the digital pin 7 as input
}

void loop()
{
relay_on_time = millis();

if (relay_on_time >= (loopTime + 10000))
{
val = digitalRead(inPin);

if (val == HIGH)
{
digitalWrite(relayPin, HIGH);
}

if (val == LOW)
{
digitalWrite (relayPin, LOW);
}
loopTime = relay_on_time;
}

}

Perhaps the (untested) sketch below will get you closer.
see the BlinkWithoutDelay example sketch if you need more on using millis to determine a timeout period

int relayPin = 13;   // relay connected to digital pin 13
int waterLowPin = 7; // pint goes high when water level is low
int val = 0;         // variable to store the read value

long relay_on_time;

void setup()
{
  pinMode(relayPin, OUTPUT);       // sets the digtial pin 13 as output
  pinMode(waterLowPin, INPUT);     // sets the digital pin 7 as input
}

void loop()
{ 
  if( digitalRead(waterLowPin))
  {
    // here if the water level is low
    delay(1000 * 10); // wait ten seconds
    if( digitalRead(waterLowPin))  // check again
    {
       // here if water level is still low   
       digitalWrite(relayPin, HIGH); // turn on relay       
       relay_on_time = millis();
       while( millis() - relay_on_time < (1000 * 30) )
       {   
         if( digitalRead(waterLowPin) == false)  // check if level is now high
         {
            delay(1000 * 2); // wait two seconds
            digitalWrite(relayPin, LOW); // turn off relay
            break; // exit the while loop
         }
       }
       // here if pump on for more than 30 seconds 
       digitalWrite(relayPin, LOW); // turn off relay
    }
  }
}

What your code is doing is checking the value every 10 seconds, and turning a relay high or low. What you want is to make sure it doesn't stay on for over 30 seconds. You can add a 'deadman switch' test:

unsigned long timeToTurnOff=0; // << change
void loop()
{
  relay_on_time = millis();
  if (relay_on_time >= (loopTime + 10000))
  { 
    val = digitalRead(inPin);
    if (val == HIGH)
    {
      digitalWrite(relayPin, HIGH);
      if (0==timeToTurnOff)  // << change: first time on since turned off?
        timeToTurnOff=relay_on_time+30000;  // << change: store max time on
    }
    if ( val == LOW || timeToTurnOff>=relay_on_time )  // << change: off or out of time? then turn off
    {
      digitalWrite (relayPin, LOW);
      timeToTurnOff=0; // << change: turn off our test
    }
    loopTime = relay_on_time;
  }
}

'timeToTurnOff' is used to make sure you shut down after a predetermined time.

David,

I tried your codes and nothing happen.

Xenia2:
David, I tried your codes and nothing happen.

Oops- I had the test backwards:

 if ( val == LOW || timeToTurnOff<=relay_on_time )  // << change: off or out of time? then turn off

I'm not sure how you are testing this - you'd need to force things on for over thirty seconds for this to kick in.

By the way, I was looking over your posts - at one point, you wanted it to be 60 seconds, not 30; at another, you wanted to go into an infinite loop until a manual reset. Am I misunderstanding the requirements?

That's correct, after 60 seconds write LOW to relayPin, but stay LOW until manual reset.

Your codes is now working, how do I modify the codes so its go into infinite loop until reset?

An infinite loop is easy.

while(1) {}

Add this wherever you want the Arduino to stop doing anything useful.