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);
}
3 Answers 3
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– connersz2018年01月08日 14:11:02 +00:00Commented Jan 8, 2018 at 14:11
-
See amended answer.2018年01月08日 20:22:57 +00:00Commented 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.connersz– connersz2018年01月08日 22:40:26 +00:00Commented 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.2018年01月09日 03:55:23 +00:00Commented Jan 9, 2018 at 3:55
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– connersz2018年01月08日 14:08:58 +00:00Commented 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.jdwolf– jdwolf2018年01月09日 01:45:32 +00:00Commented Jan 9, 2018 at 1:45
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).