0

I'm trying to create an Arduino project with a light sensor and a buzzer. The idea is that when the sensor detects that the lights have gone off, the buzzer will sound for 5 seconds. At some point, the lights will come back on, but when they go back off, I need the buzzer to sound again.

At the moment the buzzer does sound the first time the lights are turned off but after the lights have come back on and then off again, it won't sound and I can't figure out why.

int lightSensor = 2;
int buzzer = 3;
long startTime,stopTime;
long myDesiredTime = 5000;

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

void loop() {
  // put your main code here, to run repeatedly:
  int sensorState = digitalRead(lightSensor);

  switch(sensorState)
  {
  case 1:
    while (sensorState == 1)
    {
      if(stopTime-startTime<=myDesiredTime && sensorState == 1) 
      {
        runBuzzer();
        stopTime=millis();
      }
    }
  }
}

void runBuzzer()
{
  tone(buzzer, 1000); // Send 1KHz sound signal...
  delay(1000);        // ...for 1 sec
  noTone(buzzer);     // Stop sound...
  delay(1000); 
}
1

You have two major issues here:

void loop() {
  // put your main code here, to run repeatedly:
  int sensorState = digitalRead(lightSensor);

  switch(sensorState)
  {
  case 1:
    while (sensorState == 1)
    {
      if(stopTime-startTime<=myDesiredTime && sensorState == 1) 
      {
        runBuzzer();
        stopTime=millis();
      }
    }
  }
}

The first is that you never re-read sensorState. The second is that you are comparing stopTime before setting a value to it. Try re-arranging like this:

void loop() {
  // put your main code here, to run repeatedly:
  int sensorState = digitalRead(lightSensor);

  switch(sensorState)
  {
  case 1:
    while (sensorState == 1)
    {
      stopTime=millis();
      if(stopTime - startTime <= myDesiredTime) 
      {
        runBuzzer();
      }
    sensorState = digitalRead(lightSensor);  // read sensorState again
    }
  }
}

In your example, the buzzer does not sound when the light has been switch on and then back off again.

Good point. Well, we can simplify somewhat:

void loop() {

  if (digitalRead(lightSensor) == HIGH)
    {
    startTime = millis ();  // remember when we started
    while (digitalRead(lightSensor) == HIGH &&
           millis () - startTime <= myDesiredTime) 
      {
      runBuzzer();
      }  // end of while switch still closed and time not up
    while (digitalRead(lightSensor) == HIGH) { } // wait for sensor go to LOW
    delay (200);  // debounce
    }    // end of switch being closed
}      // end of loop
  • In your example, the buzzer does not sound when the light has been switch on and then back off again. – connersz Jan 8 '18 at 14:11
  • See amended answer. – Nick Gammon Jan 8 '18 at 20:22
  • Thanks, I've tried it, but now it doesn't stop after 5 seconds and only goes off when the lights come back on. The theory should be like this: Light Detected= No Buzzer, No Light Detected= Buzzer for 5 seconds max then reset. So the next time that light is not detected, it will go off again for 5 seconds. – connersz Jan 8 '18 at 22:40
  • Hmm. So you want the buzzer to beep for a maximum of 5 minutes? I've added another 2 lines to my last example. Once 5 minutes is up it waits for the sensor to go LOW again. – Nick Gammon Jan 9 '18 at 3:55
0

Think of it like this. Any time you use a while loop your code has the rains until its done its job.

It's important to remember that the loop() function in Arduino should not be blocked. This line:

while (sensorState == 1)

will block forever. The condition is never changed from within the while loop. Theres a simple way you could fix this which would be to set sensorState and update it inside the while loop. But lets not do that. Because thats inefficient and you'll likely run into this problem again in more complex programs and that solution here won't still be a solution.

What you're trying to do is called concurrency. Your program should have obvious units of execution. Your two units are: "run a buzzer for 5 seconds" and "set off an event when a sensor reads true." where the first is depended on the second. So its conditional. That sounds like a job for if

When you code in { and } it is a block. It's not quite a context but is a distinct mode of execution.

So technically this:

void loop() {
    if( ... ) {
      ...
    }
    if ( ... ) {
      ...
    }
}

Is two modes of execution. Since loop() is executed repeatedly. With that in mind when you program you can then use a variable to control the what gets executed as needed. So:

void loop() {
    int sensorState = digitalRead(lightSensor);
    if( sensorState == 1 && buzzer_enabled == 0) { // set off an event when a sensor reads true.
        buzzer_enabled = 1;
        startTime = millis();
    } 
    if ( buzzer_enabled == 1 ) { // run a buzzer for 5 seconds
        endTime = millis();
        if(stopTime - startTime <= myDesiredTime) {
            runBuzzer();
        } else {
            buzzer_enabled = 0;
        }
    }
}

We could stop here. But the use of delay is also blocking in the loop function and it is only for perception. So here's the whole thing without delays using a check against the modulus of diffTime.

int lightSensor = 2;
int buzzer = 3;

long startTime,stopTime;
long myDesiredTime = 5000;

bool buzzer_enabled = false;
bool buzzer_active = false;

int sensorState;
int prevSensorState = 0;

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


void loop() {
    sensorState = digitalRead(lightSensor);
    if( sensorState == 1 && prevSensorState == 0 ) { // set off an event when a sensor reads true.
        buzzer_enabled = true;
        startTime = millis();
    }
    prevSensorState = sensorState;
    if ( buzzer_enabled == true ) { // run a buzzer for 5 seconds
        stopTime = millis();
        long diffTime = stopTime - startTime;
        if( (diffTime % 2000) < 1000 ) {
            if(buzzer_active == false) {
                buzzer_active = true;
                //tone(buzzer, 1000);
            }
        } else {
            if(buzzer_active == true) {
                buzzer_active = false;
                //noTone(buzzer);
            }
        }
        if(diffTime > myDesiredTime) {
            buzzer_enabled = false;
        }
    }
}

No need for delay >:3 And all for free you got a new feature which is that if you set off the event again (the light goes off) the buzzer is extended another 5 seconds. If you don't want that all you have to do is add && buzzer_enabled == false to the first if.

  • I'm afraid I tried your example and the buzzer did not run at all. – connersz Jan 8 '18 at 14:08
  • You need to read the code. I didn't intend for you to just run it as is. I'm not sure what your function tone and noTone actually do so they are left commented where i think they should be. – jdwolf Jan 9 '18 at 1:45
0

Understand that your while (sensorState == 1) in your switch() is an infinite loop, with no way out (even if internally it gives the desired 5 seconds of sound).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.