IR sensor, relay and millis confusion

Hi guys,

I have been trying for hours to get this sketch working. It is so simple, but I just cannot wrap my head around millis. It is very frustrating! I have my sketch working with delay easily, but need to have 2 functions happen at the same time. I have searched the interwebs and watched videos. I just don't get it.

It would be so awesome if you could just do millis(x); and have it not stop everything like delay does.

All I am trying to do is have an IR sensor control a relay and have a delay on the relay turning off once triggered. That's it!

For an example: My hand triggers sensor, relay trips and stays tripped for "x" amount of time.

I need to do this with 2 sets of relays and sensors. I could do this with delay and individual Nano's, but that seems silly when 1 Nano can do the job.

Here is my "working" sketch with delay. I just need the sensors and relays to work at the same time, but not necessarily at the exact same time.

int sensor0=A0;
int sensor1=A1;
int relay1=2;
int relay2=3;

void setup(){
  Serial.begin(9600);
  pinMode(relay1,OUTPUT);
  pinMode(relay2,OUTPUT);
}

void loop(){
int valA0 = analogRead(sensor0);
int valA1 = analogRead(sensor1);
Serial.println(valA0);

if (valA0<500){
  digitalWrite(relay1,HIGH);
  delay(6000);
}

if (valA1<500){
  digitalWrite(relay2,HIGH);
  delay(6000);
  
}

if (valA0>500){
  digitalWrite(relay1,LOW);

}

if (valA1>500){
  digitalWrite(relay2,LOW);
  
}
}

I have tried watching videos, copying sketches and trying to change them and trying to write my own. I just don't get it.
Any help would be greatly appreciated!!!!!!!!!!!!!!!!!

consider

enum { Off = HIGH, On = LOW, };

#define ON_THRESH      500
#define MSEC_TIMEOUT   2000


byte sensors [] = { A1, A2 };
byte relays  [] = { 10, 11 };
#define N   sizeof(sensors)

unsigned long msecRlys [N] = {};

// -----------------------------------------------------------------------------
void setup (void)
{
    Serial.begin (115200);

    for (unsigned n = 0; n < N; n++)  {
        digitalWrite (relays [n], Off);
        pinMode (relays [n], OUTPUT);
    }
}

// -----------------------------------------------------------------------------
void loop (void)
{
    unsigned long msec = millis ();

    for (unsigned n = 0; n < N; n++)  {
        if (ON_THRESH >= analogRead (sensors [n]))  {
            msecRlys [n] = msec;
            digitalWrite (relays [n], On);
        }
        else if (0 < msecRlys [n] && msec - msecRlys [n] > MSEC_TIMEOUT)  {
            msecRlys [n] = 0;
            digitalWrite (relays [n], Off);
        }
    }
}

@gcjr: very compact code that does what was asked for. But very hard to understand for beginners.

You packed in everything that makes the code more compact:
arrays, enumeration, automatically calculating of max-index-numbers, leaving out comments.

to explain how it is done for two or more relays:

There has to be running on big loop very fast (at least hundreds of times per second often 10.000 times per second) which is really no problem because your arduino is running at 16 Million Herz = one command executed rough estimation once every 4 microseconds. 250.000 commands per second.

This "loop" is the function loop.

The loop is watching out for a triggerpulse and if it has detected one several things must be done

  • making a snapshot of time.
  • switching on the relay (of course)
  • set a flagvariable true

further down in the loop there is an if-condition checking how much time has passed by since taking the snapshot of time

if the result of calculating how much time has passed by since snapshot is greater than a timeout-limit
then

  • switch off relay
  • set flagvariable false

as an allday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()
You have to understand the difference first and then look into the code.
otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.

imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes

You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch 13:02 not yet time
watch 13:03 not yet time
watch 13:04 not yet time 13:04 - 13:02 = 2 minutes is less than 3 minutes
watch 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven

New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)

You did a repeated comparing how much time has passed by
This is what non-blocking timing does

In the code looking at "How much time has passed by" is done

currentTime - startTime >= bakingTime

bakingTime is 10 minutes

13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!

if (currentTime - previousTime >= period) {

it has to be coded exactly this way because in this way it manages the rollover from Max back to zero
of the function mills() automatically

best regards Stefan

Here is a code-example which does all explicit

unsigned long currentMillis = 0; 

unsigned long snapShotSensor1 = 0; 
unsigned long snapShotSensor2 = 0; 

unsigned long serialOutputTimer = 0;

unsigned long timeOut1 = 5000; 
unsigned long timeOut2 = 6000; 

boolean sensor1Triggered = false;
boolean sensor2Triggered = false;

const int OnBoard_LED = 13;

const int sensor1_Pin = A0;
const int sensor2_Pin = A1;

int sensorVal1 = analogRead(sensor1_Pin);
int sensorVal2 = analogRead(sensor2_Pin);

const int relay1 = 2;
const int relay2 = 3;


void setup() {
  pinMode(relay1,OUTPUT);
  digitalWrite(relay1,LOW);

  pinMode(relay2,OUTPUT);
  digitalWrite(relay2,LOW);

  Serial.begin(115200);
  Serial.println("Setup-Start");

  pinMode(OnBoard_LED, OUTPUT);
  digitalWrite(OnBoard_LED, LOW);
}

void loop() {
  currentMillis = millis();
  sensorVal1 = analogRead(sensor1_Pin);
  sensorVal2 = analogRead(sensor2_Pin);
  
  // if sensor is HIGH and the program has not yet deteced it
  // which is indicatet by variable  sensor0Triggered
  if (sensorVal1 > 500 && sensor1Triggered == false) {
    snapShotSensor1 = currentMillis;
    sensor1Triggered = true;
    digitalWrite(relay1,HIGH);
    Serial.print("Sensor1 has value ");
    Serial.print(sensorVal1);    
    Serial.println(" Sensor1 trigger detected. => relay1 is switched ON");
  }

  // checking time makes only sense if sensor has been triggered lately
  if (sensor1Triggered) {
    // check if sensor has been triggered lately and 
    //if more time as timeOut1 has passed by
    if (currentMillis - snapShotSensor1 >= timeOut1 && sensor1Triggered) {
      sensor1Triggered = false;
      digitalWrite(relay1,LOW);     
      Serial.print("currentMillis - snapShotSensor1=");
      Serial.print(currentMillis - snapShotSensor1);
      Serial.println(" timeOut1 is reached. => relay1 is switched OFF");
    }
  }


  // same procedure for sensor 2
  if (sensorVal2 > 500 && sensor2Triggered == false) {
    snapShotSensor2 = currentMillis;
    sensor2Triggered = true;
    digitalWrite(relay2,HIGH);
    Serial.print("Sensor2 has value ");
    Serial.print(sensorVal2);    
    Serial.println(" Sensor2 trigger detected. => relay2 is switched ON");
  }

  // checking time makes only sense if sensor has been triggered lately
  if (sensor2Triggered) {
    // check if sensor has been triggered lately and 
    //if more time as timeOut2 has passed by
    if (currentMillis - snapShotSensor2 >= timeOut2) {
      sensor2Triggered = false;
      digitalWrite(relay2,LOW);     
      Serial.print("currentMillis - snapShotSensor2=");
      Serial.print(currentMillis - snapShotSensor2);
      Serial.println(" timeOut2 is reached. => relay2 is switched OFF");
    }
  }    
}

best regards Stefan

Here is a version that does a lot of serial output so most details of what is happening inside the code is shown

// variables of type elapsedMillis get automatically incremented each millisecond
unsigned long currentMillis = 0; 

unsigned long snapShotSensor1 = 0; 
unsigned long snapShotSensor2 = 0; 

unsigned long serialOutputTimer = 0;

unsigned long timeOut1 = 10000; 
unsigned long timeOut2 = 20000; 

boolean sensor1Triggered = false;
boolean sensor2Triggered = false;

const int OnBoard_LED = 13;

const int sensor1_Pin = A0;
const int sensor2_Pin = A1;

int sensorVal1 = analogRead(sensor1_Pin);
int sensorVal2 = analogRead(sensor2_Pin);

const int relay1 = 2;
const int relay2 = 3;


void setup() {
  pinMode(relay1,OUTPUT);
  digitalWrite(relay1,LOW);

  pinMode(relay2,OUTPUT);
  digitalWrite(relay2,LOW);

  Serial.begin(115200);
  Serial.println("Setup-Start");

  pinMode(OnBoard_LED, OUTPUT);
  digitalWrite(OnBoard_LED, LOW);
}

void loop() {
  currentMillis = millis();
  sensorVal1 = analogRead(sensor1_Pin);
  sensorVal2 = analogRead(sensor2_Pin);
  
  giveInsightOfWhatIsHappening();

  // if sensor is HIGH and the program has not yet deteced it
  // which is indicatet by variable  sensor0Triggered
  if (sensorVal1 > 500 && sensor1Triggered == false) {
    snapShotSensor1 = currentMillis;
    sensor1Triggered = true;
    digitalWrite(relay1,HIGH);
    Serial.print("Sensor1 has value ");
    Serial.print(sensorVal1);    
    Serial.println(" Sensor1 trigger detected. => relay1 is switched ON");
  }

  // checking time makes only sense if sensor has been triggered lately
  if (sensor1Triggered) {
    // check if sensor has been triggered lately and 
    //if more time as timeOut1 has passed by
    if (currentMillis - snapShotSensor1 >= timeOut1 && sensor1Triggered) {
      sensor1Triggered = false;
      digitalWrite(relay1,LOW);     
      Serial.print("currentMillis - snapShotSensor1=");
      Serial.print(currentMillis - snapShotSensor1);
      Serial.println(" timeOut1 is reached. => relay1 is switched OFF");
    }
  }


  // same procedure for sensor 2
  if (sensorVal2 > 500 && sensor2Triggered == false) {
    snapShotSensor2 = currentMillis;
    sensor2Triggered = true;
    digitalWrite(relay2,HIGH);
    Serial.print("Sensor2 has value ");
    Serial.print(sensorVal2);    
    Serial.println(" Sensor2 trigger detected. => relay2 is switched ON");
  }

  // checking time makes only sense if sensor has been triggered lately
  if (sensor2Triggered) {
    // check if sensor has been triggered lately and 
    //if more time as timeOut2 has passed by
    if (currentMillis - snapShotSensor2 >= timeOut2) {
      sensor2Triggered = false;
      digitalWrite(relay2,LOW);     
      Serial.print("currentMillis - snapShotSensor2=");
      Serial.print(currentMillis - snapShotSensor2);
      Serial.println(" timeOut2 is reached. => relay2 is switched OFF");
    }
  }    
}

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();  
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

void giveInsightOfWhatIsHappening() {

  // serial output at maximum speed is unreadable
  // print serial output only every 1000 milliseconds
  if (TimePeriodIsOver (serialOutputTimer,2000)) {  

    Serial.print("Relay1 is switched ");
    if (digitalRead(relay1) == HIGH) {
      Serial.println(" ON");      
    }
    else {
      Serial.println(" OFF");            
    }

    Serial.print("Relay2 is switched ");
    if (digitalRead(relay2) == HIGH) {
      Serial.println(" ON");      
    }
    else {
      Serial.println(" OFF");            
    }
    
    Serial.print("sensorVal1=");
    Serial.print(sensorVal1);
    Serial.print("  sensorVa2=");
    Serial.print(sensorVal2);
    Serial.println();
    
    if (sensor1Triggered == false) {
      Serial.println("sensor1Triggered is false no timechecking needed");
    }
    else {
      Serial.println("sensor1Triggered TRIGGERED DO timechecking");
      Serial.println("if (currentMillis - snapShotSensor1 >= timeOut1");
      
      Serial.print("if (   ");
      Serial.print(currentMillis);
      Serial.print("     -       ");
      Serial.print(snapShotSensor1);
      Serial.print(" = ");
        
      Serial.print(currentMillis - snapShotSensor1);
      Serial.print(" >= ");
      Serial.print(timeOut1);
      Serial.print(" which means result is ");
      if (currentMillis - snapShotSensor1 >= timeOut1) {
        Serial.print("true");
      }
      else {
        Serial.print("false");      
      }        
    }

/*      
    if (sensor2Triggered == false) {
      Serial.println(" sensor2Triggered is false no timechecking needed");
    }  
    Serial.println();
    Serial.println();
    
    Serial.println("if (sensorVal1 > 500 && sensor1Triggered == false) ");
    
    Serial.print("if (");
    Serial.print(sensorVal1);
    Serial.print(" > 500 && ");
    Serial.print(sensor1Triggered);
    Serial.print(" == false) which means the result is ");
    if (sensor1Triggered == false) {
      Serial.print("true");
    }
    else {
      Serial.print("false");      
    }
    Serial.println();
    Serial.println();
  
*/  
    Serial.println();
    Serial.println();
  }    
}

best regards Stefan

Thank you so much for the help and the explanations guys!!!!

I truly, truly appreciate it!!

You have no idea how frustrating it is to be able to understand all the physical aspects of what I want to do, but not be able to make the code work. I can even accomplish mechanically what I am trying to accomplish with the Nano and sensors, but It is a lot more work.

I understand relays and sensors and pretty much anything mechanical and electrical. It comes somewhat naturally for me.

I build and fly RC planes, I build and fly sophisticated, long range, twin engine FPV planes, I build model railroads, I do live and studio music recording and mixing, I can do construction, electrical and most forms of plumbing, I can work on cars and rebuild different types of engines, IT is my profession, but this code thing is just so darn difficult for me.

I have always been able to work through stuff and figure things out on my own, but this, this is something different. lol.

Again, thank you guys for all the help you have provided with this sketch and the previous sketch!!! It is greatly appreciated! I spent hours and hours and hours yesterday trying to do this, because I wanted to figure it out for myself.

Have a very happy and blessed New Year!!

Mike