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); 
}
Michel Keijzers
13k7 gold badges41 silver badges58 bronze badges
asked Jan 7, 2018 at 23:23

3 Answers 3

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
answered Jan 8, 2018 at 6:01
4
  • In your example, the buzzer does not sound when the light has been switch on and then back off again. Commented Jan 8, 2018 at 14:11
  • See amended answer. Commented Jan 8, 2018 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. Commented Jan 8, 2018 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. Commented Jan 9, 2018 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.

answered Jan 8, 2018 at 4:00
2
  • I'm afraid I tried your example and the buzzer did not run at all. Commented Jan 8, 2018 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. Commented Jan 9, 2018 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).

sa_leinad
3,2182 gold badges23 silver badges51 bronze badges
answered Jan 8, 2018 at 3:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.