Avoid Delay

Hello,

I'm just looking for some help with the syntax on a program with a delay.

THE PROGRAM: when the motion sensor is triggered a motor runs for 15 seconds while timer runs. When the timer ends the motor stops. If motion sensed during the 15 seconds the timer resets.

This is what I have based on 'Avoid Delay' tutorial on Arduino Playground (which was quite helpful - thanks!)
I'm waiting on a sensor and can't test it as yet. I looked for a working example of the Avoid Delay on this forum and can't find the answer (only the Blink without Delay direction with previous/current/etc). I figured an example of this might be helpful for others as well. Does the syntax look right? Thanks.

int sensorPin = 5;
int motorPin = 6;

void setup()
{
Serial.begin(9600);
pinMode (sensorPin, INPUT);
pinMode (motorPin, OUTPUT);

digitalWrite(sensorPin, HIGH); // sensor triggers low

}

void loop()
{
unsigned long timer;

if(digitalRead(sensorPin)==LOW){
timer = millis();
digitalWrite(motorPin, HIGH);
}
if(millis()-timer >= 15000UL){ // 15 second delay
digitalWrite(motorPin, LOW);
}

} // end of loop

Use the code tags not the quote tags, it is the # next to the quote.

Your timer variable needs either to be global ( declared outside the function definitions ) or declared as static unsigned long

Think of how you use a calendar or meeting reminders - what people dont do is this:

Look to see the time of the next appointment, subtract the current time from that
and then fall asleep for that duration.

What people actually do is:

Check to see if they should be somewhere yet, and if so start going there,
if not then continue with the current "background task" and keep checking
(or be interrupted by an alarm).

So the way to code this second model is to code a series of checks for when
its time to start each particular activity, and keep running around loop() doing
the checks. Other checks might not be in terms of time, but checking for inputs
from buttons or serial or whatever - again you keep running around loop()
doing the checks.

Each time a check succeeds you do the next step for that activity, updating
various variables that record the state of that activity (and perhaps updating
the time at which to deal with the activity again).

So you have to breakdown a complex activity into parts, use some "state variables"
to keep track of where you are in the sequence (including time values if delays
are required).
[ in your case there is clearly a check for the sensor, a check for the delay
finishing (conditional on whether you are in the part that waits) ]

Of course the simple case, where there is only one thing happening, is easy as
you can use delay since it wont block progress for anything else (as there isn't
anything else).

Thanks for the responses. Both helpful.
I've rewritten it here. I think this would work unless I still need an additional state. Also assuming that if sensor is not triggered for a long time there won't be an issue with the endless counting of currentMillis

int sensorPin = 5; 
int motorPin = 6;


long timerDelay = 15000;

void setup()
{
  Serial.begin(9600);
  pinMode (sensorPin, INPUT);
  pinMode (motorPin, OUTPUT);
  
  digitalWrite(sensorPin, HIGH); // sensor triggers low
  
  
}

void loop()
{
unsigned long currentMillis = millis();


  if(digitalRead(sensorPin)==LOW){ // sensor triggered
    currentMillis = 0;
  }
  
  if(currentMillis >= timerDelay){
    digitalWrite(motorPin, HIGH);
  } else {
    digitalWrite(motorPin, LOW);
  }
}

The code in the previous post will turn on the motorpin 15 seconds after the program starts and it will stay turned on when sensorPin is HIGH.
15 seconds after startup, currentMillis will always be > timerDelay when if the sensorPin is HIGH.
Rethink a few things.

Right. I have the less than / greater than mixed up. Should be as follows (I'd think):

int sensorPin = 5; 
int motorPin = 6;


long timerDelay = 15000;

void setup()
{
  Serial.begin(9600);
  pinMode (sensorPin, INPUT);
  pinMode (motorPin, OUTPUT);
  
  digitalWrite(sensorPin, HIGH); // sensor triggers low
  
  
}

void loop()
{
unsigned long currentMillis = millis();


  if(digitalRead(sensorPin)==LOW){ // sensor triggered
    currentMillis = 0;
  }
  
  if(currentMillis <= timerDelay){
    digitalWrite(motorPin, HIGH);
  } else {
    digitalWrite(motorPin, LOW);
  }
}

Do you realize millis() returns the time in milliseconds since the Arduino has been on.
Therefore at the 16 second mark millis() will be 16000 and the value will be > than 15000, this happens until it overflows in ~50 days.
You have:
unsigned long currentMillis = millis();
And:
if(currentMillis <= timerDelay) this will be true only for the first 15 seconds after restart and if the switch is not pressed.
Now:
if(digitalRead(sensorPin)==LOW)
currentMillis = 0;
Will turn off the motor for as long as you the sensor is triggered. The moment the sensor goes false unsigned long currentMillis = millis(); makes currentMillis equal to millis which could be > 15000 for example if the Arduino has been on for an hour it would be 60000.
Obviously, the motor would turn off immediately, when you want it to turn on for time timeDelay.
Rethink your sketch

I did know that the millis() returns in milliseconds but I did not realize the millis() counter could not be reset to begin count at 0 again.

My leaning towards using millis() over a for statement was that it would be easier to measure the duration of the time, where as with the for statement the timing (to my knowledge) would be measured more by trial and error. I'm also reluctant to throw something like a delay(15) to slow down the ++ just in case I miss the sensor switch.

Here's what I have using 'for'. If millis() can be reset to begin counting at 0 when the sensor is triggered that would be better.

int sensorPin = 5; 
int motorPin = 6;

void setup()
{
  Serial.begin(9600);
  pinMode (sensorPin, INPUT);
  pinMode (motorPin, OUTPUT);
  
  digitalWrite(sensorPin, HIGH); // sensor triggers low
  
  int time = 0;
  int timeDelayed = 3000;
}

void loop()
{
  if(digitalRead(sensorPin)==LOW){ // sensor triggered
   time = 0;
  }
  
  if(time <= timeDelayed){
    digitalWrite(motorPin, HIGH);
  } else {
    digitalWrite(motorPin, LOW);
  }
    time++;
}

Look at the attached code is there anything here you don't understand?

int sensorPin = 5; 
int motorPin = 6;
long timerDelay = 15000;
unsigned long currentMillis;
boolean trigger; //flag indicating sensor was triggered

void setup()
{
  Serial.begin(9600);
  pinMode (sensorPin, INPUT);
  pinMode (motorPin, OUTPUT);
  digitalWrite(sensorPin, HIGH); // sensor triggers low
  trigger = false;  //disable flag at startup
}

void loop()
{
  if(digitalRead(sensorPin)==LOW)   
  { // sensor triggered
    currentMillis = millis();  //re-intialize the time
    trigger = true;//enable the flag, sensor was triggered
  }

  if(millis() - currentMillis <= timerDelay && trigger)
  {  //runs only when the sensor was triggered
    digitalWrite(motorPin, HIGH);  //Motor ON
  } 
  else 
  {
    digitalWrite(motorPin, LOW);   //Motor OFF
    trigger = false; //disables the triggered flag
  }
}

Right. So millis() continues on but the 'timeDelayed' can be measured from the gap between millis() and currentmillis.

Thanks! :slight_smile:

millis() keeps marching on, that is it keeps incrementing with.
When the sensor detects something the code locks the current millis value into the currentmillis variable.
Lets say millis was 60000 then at lock in currentmillis 60000.
The difference is zero ( < 15000) so the motor is turned on.
Each time through loop() millis becomes larger but currentmillis stays at 60000.
At some point (15000 milliseconds) the difference between them will be > timerDelay at which point the motor is turned OFF.

Tell me the purpose of the trigger flag?