How to "delay" an IR sensor OFF state or set timer based on IR sensor trigger ON

Hi all,

So you guys helped me with my very basic train crossing flasher. Relays and bulbs. I wanted to rewrite the sketch using miilis as most suggested I do.

I watched the video “blink Without Delay” twice. I will admit, while I perfectly understand what they are trying to accomplish, as well as the physical connections, I still cannot yet grasp the code.

I copied that sketch and began to try (successfully) to modify it to meet my needs.

Let me digress for a second and explain exactly what I want to happen and what this circuit is.

I collect and run Post War Lionel trains. 1945-1969. I wanted to find a way to change red/green signals, turn signals on/off and to be able to flash crossing signals (mechanical solution back then) with a sensor vs insolating and isolating a block of negative rail as a trigger.

This sketch will probably be the most complex sketch in regard to this layout, so if I can figure this out, then everything else should fall into place for other signals.

So here is what is happening:

  • Boot with 2 relays in OFF state
  • Train goes over/by IR sensor and sensor is triggered
  • IR sensor turns on relay2 which completes the circuit by turning on ground
  • IR sensor also turns on second relay which cycles back and forth every 600ms. This cycles the + back and forth between the 2 bulbs causing the bulbs to flash back and forth.
  • Finally, I want to delay or set a timer so that this all continues to happen after the train has passed the sensor. I don’t want the crossing signal to turn off before the train has gone by.

So for example, the sensor is triggered and stays triggered for 10 seconds even though nothing is triggering the sensor because the train has already gone by the sensor.

The last piece is where I am having trouble. I have tried a few different things, but cannot make the “delay” work how I want.

The sketch is working, using millis, other than adding a sensor “delay” piece.

Any help would be greatly appreciated!! I am so close!!

// constants to set pin numbers
int sensor = A0;
int relay2 = 3;
const int relayPin = 2;    // the number of the relay pin

// Variables will change
int relayState = HIGH;   //relayState for relay
long previousMillisRed = 0;    //last time Relay was updated

// must be long to prevent overflow
long relayInterval = 600;    //interval to blink relay (milliseconds)
unsigned long currentMillis = 0;

void setup() {
  // set the pins to output mode
  pinMode(relay2, OUTPUT);
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayState);

  currentMillis = millis();
}

void loop() {


  // capture the current time
  currentMillis = millis();
  manageRelay();

}

void manageRelay() {

  int val = analogRead(sensor);

  if (val < 500) {
    digitalWrite(relay2, HIGH);// Sets trigger (Ground wire) relay to ON

  }

  //check if it's time to change the Relay yet
  if (currentMillis - previousMillisRed > relayInterval) {
    //store the time of this change
    previousMillisRed = currentMillis;
    relayState = (relayState == HIGH) ? LOW : HIGH;
    digitalWrite(relayPin, relayState);

    //Sets both relays to OFF when sensor > 500
    if (val > 500) {
      digitalWrite(relayPin, LOW);
      digitalWrite(relay2, LOW);

    }
  }
}

consider (using previousMillisRed as a state)

// constants to set pin numbers
int sensor = A0;
int relay2 = 3;
const int relayPin = 2;    // the number of the relay pin

// Variables will change
int relayState = HIGH;   //relayState for relay
long previousMillisRed = 0;    //last time Relay was updated

// must be long to prevent overflow
long relayInterval = 600;    //interval to blink relay (milliseconds)
unsigned long currentMillis = 0;

void setup() {
    // set the pins to output mode
    pinMode(relay2, OUTPUT);
    pinMode(relayPin, OUTPUT);
    digitalWrite(relayPin, relayState);
    currentMillis = millis();
}

void loop() {
    // capture the current time
    currentMillis = millis();
    manageRelay();
}

void manageRelay() {
    int val = analogRead(sensor);
    if (val < 500) {
        digitalWrite(relay2, HIGH);// Sets trigger (Ground wire) relay to ON
    }

    //check if it's time to change the Relay yet
    if (currentMillis - previousMillisRed > relayInterval) {
        //store the time of this change
        previousMillisRed = currentMillis;
        relayState = (relayState == HIGH) ? LOW : HIGH;
        digitalWrite(relayPin, relayState);
        //Sets both relays to OFF when sensor > 500
        if (val > 500) {
            digitalWrite(relayPin, LOW);
            digitalWrite(relay2, LOW);
        }

    }

}

Thank you for the response, but I do not understand what that means.

What I am really hoping for is for somebody to show me the piece of sketch I need to delay the sensor and explain what it means so I can learn.

I have tried googling and looking at different sketches, but no luck so far.

sorry, i posted the wrong code

// constants to set pin numbers
#if 0
int sensor = A0;
int relay2 = 3;
const int relayPin = 2;    // the number of the relay pin

#else    // my hardware
const int sensor   = A1;
const int relay2   = 13;
const int relayPin = 12;
#endif

enum { Off = HIGH, On = LOW };

// Variables will change
int  relayState;
long previousMillisRed = 0;    //last time Relay was updated

// must be long to prevent overflow
unsigned long relayInterval = 600;    //interval to blink relay (milliseconds)
unsigned long currentMillis = 0;

void setup () {
    // set the pins to output mode
    pinMode (relay2,   OUTPUT);
    pinMode (relayPin, OUTPUT);
    digitalWrite (relay2,   Off);
    digitalWrite (relayPin, Off);

    pinMode (sensor,   INPUT_PULLUP);   // have button
}

void loop () {
    // capture the current time
    currentMillis = millis ();
    manageRelay ();
}

#define DelayInterval   2000
unsigned long msecDelay = 0;

void manageRelay () {
    int val = analogRead(sensor);
    if (val < 500) {
        digitalWrite(relay2, HIGH);// Sets trigger (Ground wire) relay to ON
        msecDelay = currentMillis;
    }
    else if (currentMillis - msecDelay > DelayInterval) {
        digitalWrite(relay2,   LOW);  // off
        digitalWrite(relayPin, LOW);  // off
        previousMillisRed = currentMillis;  // inhibit toggling
    }

    //check if it's time to change the Relay yet
    if (currentMillis - previousMillisRed > relayInterval) {
        previousMillisRed = currentMillis;

        digitalWrite(relayPin, ! digitalRead (relayPin));
    }
}

Hi,
I tried the sketch, but it does not work. I fixed the relay pins as mine are 2 and 3 not 12 and 13, but it still does not work.

On boot it activates the relays for about 2 seconds without the sensor being triggered and that's it. Sensor does not trigger anything after that.

My original sketch works perfect except I need it to stay going for "x" amount of time once the sensor is triggered.

So:

  • Boot with relays OFF //this works
  • Train triggers sensor and relay2 turns ON and second relay goes back and forth every 600ms making signal blink //This works
  • Train passes sensor un-triggering sensor and relays turn OFF //How it currently works

What I want to happen:

  • Boot with relays OFF //This works
  • Train triggers sensor and sensor stays triggered for "x" amount of time.
    OR
  • Train passes sensor and sensor then stays triggered for "x" amount of time.

It does not matter which way that happens. The point is to have the crossing signal keep working until the train passes.

As an example, you would not see crossing gates lift up while a train was still passing.

I could also add a second sensor so that the front of the train triggers the second sensor before the end of the train passes the first sensor. I just figured adding a timer on the sensor trigger makes more sense than adding a second sensor.

Thank you again for the help!

i think it’s clear what you want – a delayed off

i don’t have your hardware, so i tested it with a momentary switch a some LEDs.

your code turns on the relay when the input (sensor) is pulled low. i added code for the case when the sensor is not pulled low and after a delay, DelayInterval.

i also removed the code from you flashing case where you turn the relays off (LOW) when val > 500. this is the wrong place for this

whenever the sensor is pulled low, i reset msecDelay. when the sensor is not pulled low, msecDelay delays turning the relays off for the DelayInterval.

reseting previousMillsRed just inhibits the other code from running which should have no effect, but will prevent the relay from clicking

hopefully this explains the code so you you can find the bug

   if (val < 500) {
        digitalWrite(relay2, HIGH);// Sets trigger (Ground wire) relay to ON
        msecDelay = currentMillis;
    }
    else if (currentMillis - msecDelay > DelayInterval) {
        digitalWrite(relay2,   LOW);  // off
        digitalWrite(relayPin, LOW);  // off
        previousMillisRed = currentMillis;  // inhibit toggling
    }

gcjr:
i think it’s clear what you want – a delayed off

i don’t have your hardware, so i tested it with a momentary switch a some LEDs.

your code turns on the relay when the input (sensor) is pulled low. i added code for the case when the sensor is not pulled low and after a delay, DelayInterval.

i also removed the code from you flashing case where you turn the relays off (LOW) when val > 500. this is the wrong place for this

whenever the sensor is pulled low, i reset msecDelay. when the sensor is not pulled low, msecDelay delays turning the relays off for the DelayInterval.

reseting previousMillsRed just inhibits the other code from running which should have no effect, but will prevent the relay from clicking

hopefully this explains the code so you you can find the bug

   if (val < 500) {

digitalWrite(relay2, HIGH);// Sets trigger (Ground wire) relay to ON
       msecDelay = currentMillis;
   }
   else if (currentMillis - msecDelay > DelayInterval) {
       digitalWrite(relay2,   LOW);  // off
       digitalWrite(relayPin, LOW);  // off
       previousMillisRed = currentMillis;  // inhibit toggling
   }

Thank you!! I will see what I can do. :slight_smile:

vrdaddy:
Thank you!! I will see what I can do. :slight_smile:

I'm sorry, I'm really not trying to be a dunce. lol. I am really trying to understand.

One thing I am confused about is why 1 relay is delayed.

In my sketch when the sensor is triggered, one relay turns on and the other flip flops or "blinks" back and forth until I move my hand away from the sensor
.
I want all that to continue for "x" amount of time once the sensor is triggered.

I do not understand why there would not be a line or more of code that has to do with the sensor staying triggered for "x" amount of time.

In my simple mind I see it like this:

if sensor triggered, keep doing everything you were already doing for "x" amount of time even if sensor is no longer triggered.

I want the whole sketch to keep running for "x" amount of time once sensor triggered.

I do not understand why there would not be a line or more of code that has to do with the sensor staying triggered for "x" amount of time.

assume you're referring to the following code. it it what is done when the sensor is no longer active. it will turn off both relays after a delay

    else if (currentMillis - msecDelay > DelayInterval) {
        digitalWrite(relay2,   LOW);  // off
        digitalWrite(relayPin, LOW);  // off
        previousMillisRed = currentMillis;  // inhibit toggling
    }

gcjr:
assume you're referring to the following code. it it what is done when the sensor is no longer active. it will turn off both relays after a delay

    else if (currentMillis - msecDelay > DelayInterval) {

digitalWrite(relay2,  LOW);  // off
        digitalWrite(relayPin, LOW);  // off
        previousMillisRed = currentMillis;  // inhibit toggling
    }

I see. Ok. When I boot up the relays do their thing for 2 seconds, turn off and then the sensor does not trigger anything.
I also do not see where I can adjust how long the sensor stays triggered for.
You said you were testing with LED's and a momentary.
This is how what I am trying to do would work with your test setup:

  • Sensor/momentary button triggered and below continues to happen for "x" amount of time
  • 1 LED stays on solid
  • 1 LED blinks every 600ms
  • Sensor un-triggered but LED's still on/blinking because "x" time has not passed.
  • "x" time has passed LED's turn off.

i’m using a multifunction board to test the logic. it as momentary switches connected to the analog inputs: A1, A2 and A3 and LEDs on pins D10, D11, D12 and D13. the buttons ground the input and the LEDs are turned on when the pins go LOW

when I run the code both LED are On, the pins are set LOW. A1 is pulled HIGH.

when I press the button, A1 is pulled low – val < 500. LED 13 turns off and LED 12 begins flashing.

when i release the button, A1 goes high. LED 13 remains off and LED 12 continues to flash for the DelayInterval which is 2000 (2 sec), then both LEDs turn on.

Ok, thank you! That is helpful. I will try some things and report back.

Sorry I did not include my hardware earlier:

Elegoo Nano
generic relay x 2
generic sensor

5v and Ground on Nano powering the 2 relays and sensor.
Relays connected to D2 and D3.
Sensor connected to A0.

I think you need a little state machine. Fortunately, it only needs two states, so an if else will suffice. Try this:

// constants to set pin numbers
int sensor = A0;
int relay2 = 3;
const int relayPin = 2;    // the number of the relay pin

// Variables will change
int relayState = HIGH;   //relayState for relay
long previousMillisRed = 0;    //last time Relay was updated

// must be long to prevent overflow
long relayInterval = 600;    //interval to blink relay (milliseconds)
unsigned long currentMillis = 0;
bool flashing = false;
unsigned long FlashingStartTime;
unsigned long CrossingStartTime;
unsigned long CrossingInterval=10000;

void setup()
{
  // set the pins to output mode
  pinMode(relay2, OUTPUT);
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayState);
  currentMillis = millis();
}

void loop()
{
  // capture the current time
  currentMillis = millis();
  manageRelay();
}

void manageRelay()
{
if(flashing)
  {
  if (currentMillis - FlashingStartTime > relayInterval)
    {
    FlashingStartTime = currentMillis;
    relayState = (relayState == HIGH) ? LOW : HIGH;
    digitalWrite(relayPin, relayState);
    }
  if (currentMillis - CrossingStartTime > CrossingInterval)
    {
    digitalWrite(relayPin, LOW);
    digitalWrite(relay2, LOW);
    flashing = false;  
    }
  }
else
  {
  if (analogRead(sensor) < 500)
    {
    digitalWrite(relay2, HIGH); // Sets trigger (Ground wire) relay to ON
    flashing=true;
    FlashingStartTime = CrossingStartTime = millis();
    }      
  }
}

wildbill:
I think you need a little state machine. Fortunately, it only needs two states, so an if else will suffice. Try this:

// constants to set pin numbers

int sensor = A0;
int relay2 = 3;
const int relayPin = 2;    // the number of the relay pin

// Variables will change
int relayState = HIGH;  //relayState for relay
long previousMillisRed = 0;    //last time Relay was updated

// must be long to prevent overflow
long relayInterval = 600;    //interval to blink relay (milliseconds)
unsigned long currentMillis = 0;
bool flashing = false;
unsigned long FlashingStartTime;
unsigned long CrossingStartTime;
unsigned long CrossingInterval=10000;

void setup()
{
  // set the pins to output mode
  pinMode(relay2, OUTPUT);
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayState);
  currentMillis = millis();
}

void loop()
{
  // capture the current time
  currentMillis = millis();
  manageRelay();
}

void manageRelay()
{
if(flashing)
  {
  if (currentMillis - FlashingStartTime > relayInterval)
    {
    FlashingStartTime = currentMillis;
    relayState = (relayState == HIGH) ? LOW : HIGH;
    digitalWrite(relayPin, relayState);
    }
  if (currentMillis - CrossingStartTime > CrossingInterval)
    {
    digitalWrite(relayPin, LOW);
    digitalWrite(relay2, LOW);
    flashing = false; 
    }
  }
else
  {
  if (analogRead(sensor) < 500)
    {
    digitalWrite(relay2, HIGH); // Sets trigger (Ground wire) relay to ON
    flashing=true;
    FlashingStartTime = CrossingStartTime = millis();
    }     
  }
}

Hi WildBill,

This worked perfectly! Thank you!

Since I am trying to learn, can you please explain the difference between your sketch and gcjr’s sketch?

Do you know why his sketch started with the sensor thinking it was triggered? I just want to learn enough right now to be able to write my own sketches for my simple train projects. Maybe in the future I will try a more complex sketch for my HO layout once I have a better grasp of this.

Also huge thanks to gcjr for spending so much time trying to help me! I think you were VERY close. When I really started messing with it I found that on boot it was working, just that it thought the sensor was triggered. I couldn’t figure out how to resolve that.

The critical point is that you only flash the crossing light while the crossing delay is active.

wildbill:
The critical point is that you only flash the crossing light while the crossing delay is active.

Ok, thank you!