Timer with If Statements Code Problems

The code gathers data from a proximity sensor that sets off a light. Then the code is supposed to set off a buzzer after 20 seconds if the sensor is not "deactivated" before the buzzer goes off. But right now it just goes in its loop and sets the buzzer off every couple of seconds.

I think I would like the time to restart every time the code cycles through. Obviously the code isn't doing that now. Any suggestions?

I will humbly accept any corrections/ideas/help/links to proper coding/etc. I have probably over complicated this code.

#define IR A0
int reading;
const int lightPin = 5;
const int buzzPin = 6;
const int threshold = 500;
boolean flagA = 0;
boolean flagB = 0;
unsigned long starTime = 0;  // global variable
unsigned long interval = 20000; // 20 seconds

void setup() {
 // put your setup code here, to run once:
 pinMode(A0, INPUT);
 pinMode(5, OUTPUT);
 pinMode(6, OUTPUT);
 Serial.begin(9600);
}
void loop()
{

 reading = analogRead(IR);
 Serial.println(reading);


if (reading < threshold && flagA == 0){
digitalWrite(lightPin, HIGH);
delay(1000);
Serial.println("Light ON!!!!");
 flagA = 1;

}
else{
 flagA = 0;
 }

// Buzzer Timer
if (reading < threshold && flagA == 1 && (millis() - starTime >= interval)){
 // current time - start time >= 5 seconds
 delay(1000);
 digitalWrite(lightPin, HIGH);
 digitalWrite(buzzPin, HIGH);
 Serial.println("Buzz Buzz Buzz");
 delay(1000);
 digitalWrite(buzzPin, LOW);
 delay(2000);
 flagA = 0;
}
if (reading > threshold && flagA == 0)
{
 // TURN LIGHT OFF
 digitalWrite(lightPin, LOW);
 Serial.println("LIGHTS OUT!!!!");
 delay(2000);
 flagA = 0;
 flagB = 0;
 }
}

delay(2000) wastes the considerable power of your Arduino for two seconds and makes it very difficult to do anything else at the same time.

Learn to use millis().

This should work ... notice how I broke everything down into simpler components ... that's the key to preventing making spaghetti and splattering it all over the walls ... :wink:

Simplifying your loop is the key to everything.

Also notice how the "script" of the loop verbally describes the steps that need to be taken ... make it readable, break things down into smaller components ... think like a computer. :slight_smile:

#include <Arduino.h>

#define IR A0

const int lightPin = 5;
const int buzzPin = 6;
const int threshold = 500;

unsigned long alertTriggeredTime;
unsigned long timeSinceAlertTriggered;

bool buzzerEnabled = false;
bool alertMode = false;

void buzzOn() {digitalWrite(buzzPin, HIGH);}
void buzzOff () {digitalWrite(buzzPin, LOW);}
void buzzBuzzer() {
    buzzOn();
    delay(1000);
    buzzOff();
    delay(2000);
}
void lightOn() {digitalWrite(lightPin, HIGH);}
void lightOff() {digitalWrite(lightPin, LOW);}
void enableBuzzer(){buzzerEnabled = true;}
void disableBuzzer() {buzzerEnabled = false;}
bool someoneDetected() {return analogRead(IR) < threshold;}
bool standDownAlert() {return !alertMode && !someoneDetected();}
bool buzzerShouldBuzz() {return buzzerEnabled && alertMode;}
void silenceBuzzer() {
    buzzOff();
    disableBuzzer();
}

void setup() {
    Serial.begin(9600);
    pinMode(IR, INPUT);
    pinMode(lightPin, OUTPUT);
    pinMode(buzzPin, OUTPUT);
}

void loop()
{

    if (someoneDetected() && !alertMode) {
        alertTriggeredTime = millis();
        alertMode = true;
    }
    
    if (!someoneDetected() && alertMode) {alertMode = false;}

    if (alertMode) {
        timeSinceAlertTriggered = millis() - alertTriggeredTime;
        if (timeSinceAlertTriggered >= 20000) {
            enableBuzzer();
        }
    }
    
    if (buzzerShouldBuzz()) {
        lightOn();
        buzzBuzzer();
    }
    
    else if (standDownAlert()) {
        lightOff();
        silenceBuzzer();
    }

}

vaj4088 - Thanks. I will look into some tutorials for using millis() because I am obviously not using them correctly. Thank you!

vaj4088:
delay(2000) wastes the considerable power of your Arduino for two seconds and makes it very difficult to do anything else at the same time.

Learn to use millis().

EasyGoing1 - Wow..wow..wow. Thank you! I have been looking over your new code today. I wanted to let you know that I am studying it and trying to figure out what you did before I comment further. I also tried plugging it into my Arduino and it is not working. So I am going to spend some more time studying it, learning, and let you know what I come up with.

Thank you for taking the time to write that and give feedback!!! I'll try to "think like a computer". :wink:

EasyGoing1:
This should work ... notice how I broke everything down into simpler components ... that's the key to preventing making spaghetti and splattering it all over the walls ... :wink:

Simplifying your loop is the key to everything.

Also notice how the "script" of the loop verbally describes the steps that need to be taken ... make it readable, break things down into smaller components ... think like a computer. :slight_smile:

#include <Arduino.h>

#define IR A0

const int lightPin = 5;
const int buzzPin = 6;
const int threshold = 500;

unsigned long alertTriggeredTime;
unsigned long timeSinceAlertTriggered;

bool buzzerEnabled = false;
bool alertMode = false;

void buzzOn() {digitalWrite(buzzPin, HIGH);}
void buzzOff () {digitalWrite(buzzPin, LOW);}
void buzzBuzzer() {
    buzzOn();
    delay(1000);
    buzzOff();
    delay(2000);
}
void lightOn() {digitalWrite(lightPin, HIGH);}
void lightOff() {digitalWrite(lightPin, LOW);}
void enableBuzzer(){buzzerEnabled = true;}
void disableBuzzer() {buzzerEnabled = false;}
bool someoneDetected() {return analogRead(IR) < threshold;}
bool standDownAlert() {return !alertMode && !someoneDetected();}
bool buzzerShouldBuzz() {return buzzerEnabled && alertMode;}
void silenceBuzzer() {
    buzzOff();
    disableBuzzer();
}

void setup() {
    Serial.begin(9600);
    pinMode(IR, INPUT);
    pinMode(lightPin, OUTPUT);
    pinMode(buzzPin, OUTPUT);
}

void loop()
{

if (someoneDetected() && !alertMode) {
        alertTriggeredTime = millis();
        alertMode = true;
    }
   
    if (!someoneDetected() && alertMode) {alertMode = false;}

if (alertMode) {
        timeSinceAlertTriggered = millis() - alertTriggeredTime;
        if (timeSinceAlertTriggered >= 20000) {
            enableBuzzer();
        }
    }
   
    if (buzzerShouldBuzz()) {
        lightOn();
        buzzBuzzer();
    }
   
    else if (standDownAlert()) {
        lightOff();
        silenceBuzzer();
    }

}

derieux:
EasyGoing1 - Wow..wow..wow. Thank you! I have been looking over your new code today. I wanted to let you know that I am studying it and trying to figure out what you did before I comment further. I also tried plugging it into my Arduino and it is not working. So I am going to spend some more time studying it, learning, and let you know what I come up with.

Thank you for taking the time to write that and give feedback!!! I'll try to "think like a computer". :wink:

I banged that out in just a few minutes. I don't have a motion sensor to test it, but I'll simulate one with a button or something to see what's going on with the code.

Concerning the millis() function ... all that does is return a number that represents the number of milliseconds that have passed since the Arduino was powered on. But since it returns an additional thousand every second, it's useful for measuring timed events. It's preferable to use the millis() marker instead of relying on the delay() function because delay() puts interrupts to sleep and can make your code behave in undesired ways as your code gets more advanced.

Execution of code in an Arduino is continuous in the loop() function. When you power on, any variable declarations you have prior to the setup() function get initialized, then the setup() routine runs ONCE, then everything goes to loop and that's all it does, is loop ... so in order to keep the loop moving, we like to mark a variable (specifically an unsigned long variable) with the millis() function, then check back elsewhere in the code to determine how much time has elapsed since that millis() read was taken. For example:

void timedCode() {
  static unsigned long startTime;
  static bool resetTime = true;

  if (resetTime) {
    startTime = millis();
    resetTime = false;
  }

  elapsedTime = millis() - startTime;

  if (elapsedTime >= 2000) {
    //Do something here
    resetTime = true;
  }
}

void loop() {

  timedCode();
  someOtherFunction();

}

That code would cause your //Do something here code to execute every 2 seconds without interfering with what might be happening in someOtherFunction(). Where as if you simply used delay(2000), then the someOtherFunction() would never happen during that 2 seconds, which in many cases could be a problem. Also, if you have an interrupt event happen within that two seconds, it would not be "seen" until after the delay() function was done executing, so delay() tends to be somewhat of a 'service interrupter' so it's best not to use it. Utilize millis() for timed events instead of delay() ... is a good habit to get into. You have to be mindful of the fact that loop is constantly running which is a different way of thinking in terms of programming in general.

By declaring startTime and resetTime as static, they will not get refreshed every time we call timedCode(); It's like having a global variable that is unique to only that function. So they never change unless we cause change on them to happen within that function as I did in the example. They are quite handy. The first time that function is called, the resetTime boolean is initialized to true, then after that, it only gets changed when the other conditions are met.

It's quite possible that the timedCode() function will be executed thousands of times within that two-second period that we're waiting for so there are plenty of opportunities to do other things while we wait whereas we could not do other things if we simply used delay().

Hopefully that clarifies millis() for you in terms of what it does and how it's typically used.

:slight_smile:

derieux:
I also tried plugging it into my Arduino and it is not working.

OK,

I tested the code and re-worked it a little...

Copy the code to your Arduino without making any changes (Make sure that buzzPin and lightPin are correct though) and just run it.

Here is what it will do:

When the IR beam is triggered, it will wait 20 seconds, then make noise and lights... what I wasn't clear on from your original post was whether or not you wanted the detection status to change based on whether or not someone was continuously standing in the beam or if you just wanted it to start a 20-second timer the moment the beam is interrupted then make noise after 20 seconds.

If you only wanted to make noise and light if the person is standing in the beam for 20 consecutive seconds, then change the first line in the loop to checkForPersonContinuously(). What that method does, is it changes the personDetected variable based solely on whether or not the beam is interrupted in that moment, but it only sets the mark time ONCE - and only when the beam was first interrupted, which happens when the personDetected variable goes from false to true. Remember that all the methods are constantly being run because of loop, so here is why that matters:

We have to set an initial mark time of when someone was detected. If the method that detects someone looked like this:

personDetected = analogRead(IR) < threshold;
alertTriggeredTime = millis();

Then the alert trigger time mark would be changing constantly (potentially a thousand times per second), whether or not the beam was interrupted, so we have to build in a safeguard to make sure that the time mark is set when someone is detected, but then is not changed again until the status of that variable goes back to false, then true again... you should be able to make sense of that method if you mentally step through it.

But remember, with that method ... if someone is standing in the beam for even 19.999 seconds, then moves out of it, nothing is going to happen in terms of sound and light. But that may be what you intended ... I wasn't sure.

Anyways, assuming that we've detected someone and 20 seconds later the alarm and light go off ...

10 seconds after the alarm starts, the light will turn off.

The buzzer will "bounce" a few times (beep, beep, beep, etc.) I left an alternate method in there to have the buzzer just turn on for however many seconds you designate ... not sure how you wanted it to sound, but obviously there are an infinite number of ways to play the buzzer, and depending on your buzzing speaker, you could even variate the pitch and play a show tune ... lol ...

OK, here's the code, then I have a few more comments

#include <Arduino.h>

#define IR A0

const int buzzPin = 6;
const int lightPin = 5;
const int threshold = 500;

//Used for bounceBuzzer()
const int buzzOnTime = 150;
const int buzzOffTime = 100;

//Number of seconds for continuous buzz
const int buzzSeconds = 5;

//This is the number of seconds to wait once someone is detected before alarming
const int secondsSinceDetection = 20;

//number of seconds to leave light on
const int lightOnTime = 10;

unsigned long alertTriggeredTime;
unsigned long lightTriggerTime;

bool personDetected = false;
bool lightIsOn = false;

//"Back End" methods
void buzzOn() {digitalWrite(buzzPin, HIGH);}
void buzzOff () {digitalWrite(buzzPin, LOW);}
void lightOn() {digitalWrite(lightPin, HIGH);lightIsOn = true;lightTriggerTime = millis();}
void lightOff() {digitalWrite(lightPin, LOW);lightIsOn = false;}
void allClear() {personDetected = false;}
bool lightHasBeenOnFor(int seconds) {return ((millis() - lightTriggerTime) / 1000) > seconds;}


//Loop Methods
void turnLightOffIn(int seconds) {
    if (lightHasBeenOnFor(seconds)) lightOff();
}

void buzzFor (int seconds) {
    long milliSeconds = seconds * 1000;
    lightOn();
    buzzOn();
    delay(milliSeconds);
    buzzOff();
    allClear();
}

void bounceBuzzer(int repeatInterval) {
    lightOn();
    for (int x=0; x<repeatInterval; x++) {
        buzzOn();delay(buzzOnTime);
        buzzOff();delay(buzzOffTime);
    }
    allClear();
}

bool secondsPassedSinceDetection(int seconds) {
    unsigned long millisPassed = millis() - alertTriggeredTime;
    unsigned long secondsPassed = millisPassed / 1000;
    return secondsPassed > seconds;
}

void checkForPersonContinuously() {
    static bool lastDetection = false;
    personDetected = analogRead(IR) < threshold;
    if (personDetected != lastDetection) {
        if (personDetected) alertTriggeredTime = millis();
        lastDetection = personDetected;
    }
}

void checkForPersonOneTime(){
    if (!personDetected) {
        if (analogRead(IR) < threshold) {
            personDetected = true;
            alertTriggeredTime = millis();
        }
    }
}

void setup() {
    Serial.begin(9600);
    pinMode(IR, INPUT);
    pinMode(lightPin, OUTPUT);
    pinMode(buzzPin, OUTPUT);
}
void loop()
{
    checkForPersonOneTime();
    if (personDetected && secondsPassedSinceDetection(secondsSinceDetection)) {
        bounceBuzzer(6);
        //Optional you could use BuzzFor(buzzSeconds) to do one continuous sound.
        //buzzFor(buzzSeconds);
    }
    if (lightIsOn){
        turnLightOffIn(lightOnTime);
    }
}

Notice I used the delay() function with the buzzer methods... I did this because it's simpler and in this application we don't need to do anything else while the buzzer is buzzing ... if we do, then we need to make changes obviously. I assumed simplicity so I went ahead and used it, which in this application is perfectly acceptable ... normally when you're sounding an alarm in a simple application like this, you don't need to multitask, you just want noise. :slight_smile:

Also notice at the top I added some constant integers that let you easily change some of the parameters of the events.... like how long the buzzer will stay on then off in the bounceBuzzer method, or how long the light needs to stay on after it's turned on etc.

Let me know how it goes.

Mike

Wow. The last couple of days I have been watching videos and getting on this forum to better understand what you did. Thanks for taking the time.

Let me clarify what the Arduino is used for. Its actually being used to notify employees that a paper has been printed on a printer. I have had the Arduino set up with the IR sensor and light for months. As soon as I wanted to add a buzzer it got very complicated. So what I am trying to do is have the light go off immediately after the Arduino detects the paper. If it is still detecting paper for a length of time I would like a buzzer to go off.

Edit: It would be nice if the buzzer would continue in the loop and go off for that set time until the paper is gone. For example: If the buzzer is set to go off for 20 seconds then after it is initialized the buzzer will continue to go off every 20 seconds until the paper is off the printer. Then both the light and the buzzer are silenced.

That's the idea.

But your post was very helpful. Thank you!! When I get a chance today I am going to try and work with it. Thank you for taking the time! I have learned a lot .

derieux:
Wow. The last couple of days I have been watching videos and getting on this forum to better understand what you did. Thanks for taking the time.

Let me clarify what the Arduino is used for. Its actually being used to notify employees that a paper has been printed on a printer. I have had the Arduino set up with the IR sensor and light for months. As soon as I wanted to add a buzzer it got very complicated. So what I am trying to do is have the light go off immediately after the Arduino detects the paper. If it is still detecting paper for a length of time I would like a buzzer to go off.

Edit: It would be nice if the buzzer would continue in the loop and go off for that set time until the paper is gone. For example: If the buzzer is set to go off for 20 seconds then after it is initialized the buzzer will continue to go off every 20 seconds until the paper is off the printer. Then both the light and the buzzer are silenced.

That's the idea.

But your post was very helpful. Thank you!! When I get a chance today I am going to try and work with it. Thank you for taking the time! I have learned a lot .

OK, that's a lot simpler than what I was thinking ... ignore the code I sent in response to your PM, this code does exactly what you described here. I tested it and it works.

#include <Arduino.h>

#define IR A0

const int BUZZ_PIN = 6;
const int LIGHT_PIN = 5;

const int THRESHOLD = 500;

const int BUZZ_ON_TIME = 100; //in milliseconds (100 = .1 seconds)
const int BUZZ_OFF_TIME = 200; //in milliseconds (200 = .2 seconds)
const int BUZZ_PLAY_COUNT = 5; //number of times to consecutively turn buzzer on then off

const int SECONDS_BETWEEN_BUZZING = 20;

bool paperDetected = false;

#define RESET true


void buzzOn() {digitalWrite(BUZZ_PIN, HIGH);}
void buzzOff () {digitalWrite(BUZZ_PIN, LOW);}


void keepLightOn() {
    digitalWrite(LIGHT_PIN, HIGH);
}

void turnLightOff() {
    digitalWrite(LIGHT_PIN, LOW);
}

void chatterBuzz() {
    for (int x=0; x< BUZZ_PLAY_COUNT ; x++) {
        buzzOn();
        delay(BUZZ_ON_TIME);
        buzzOff();
        delay(BUZZ_OFF_TIME);
    }
}

void playBuzzerOnTime (boolean reset = false) {
    static unsigned long startTime;
    static long millisecondsToWait = SECONDS_BETWEEN_BUZZING * 1000;
    if (reset) startTime = millis();
    if ((millis() - startTime) >= millisecondsToWait) {
        startTime = millis();
        chatterBuzz();
    }
}

bool irTriggered() {
    return analogRead(IR) < THRESHOLD;
}

void checkForPaper(){
    if (!paperDetected) {
        if (irTriggered()) {
            paperDetected = true;
            playBuzzerOnTime(RESET);
        }
    }
    if (!irTriggered()) {
        paperDetected = false;
    }
}

void setup() {
    Serial.begin(9600);
    pinMode(IR, INPUT);
    pinMode(LIGHT_PIN, OUTPUT);
    pinMode(BUZZ_PIN, OUTPUT);
}

void loop()
{
    checkForPaper();
    if (paperDetected) {
        keepLightOn();
        playBuzzerOnTime();
    }
    else {
        turnLightOff();
    }
}

Notice I also put in there, the time between buzzing which is now set at 20 seconds, but you can change that value and it will respond accordingly. You can also change the buzz on and off time and how many times to buzz on then off in a row ... you'll understand when you hear it how the numbers affect it. Test it and see how it feels and pick the values that work for you.

This next sketch will let you see what the threshold is of that sensor. It will only send numbers to Serial when the value changes in either direction by 10 or more, so it doesn't flood your screen with repeating values all the time. I don't know what the normal ranges are of that sensor, so if 10 is too much, you can adjust that number in the loop.

I would run this while connected to the IR sensor with a computer monitoring the serial port and just stick paper in the beam and then pull it out and see what the values are ...

I would also look at the values over some time when there is no paper in the beam to see how much the values fluctuate ... based on your original code, it looked like the values get lower when the beam is interrupted ... but let's say that the value is around 700 when there's nothing in the beam ... but it drops to 300 when something is in the beam ... I would then set the threshold to 500 in that specific case. You want that threshold to have some margin in it ... you don't want to set it at the exact value that you read when the paper is in the beam you want it like at halfway between the values at no paper present and paper present ... hopefully, that makes sense.

Whichever threshold you decide on, obviously you plug that number into the main program.

#include <Arduino.h>

#define IR A0

long lastValue = 0;
long currentValue;
long difference;

void setup() {
    Serial.begin(9600);
    pinMode(IR, INPUT);
}

void loop()
{
    currentValue = analogRead(IR);
    difference = abs(currentValue - lastValue);
    
    if (difference >= 10) {
        Serial.println(currentValue);
        lastValue = currentValue;
    }
}