I am new to programming in general and to Arduino as well. I am working on a small project to improve my ability to write complete and kind of useful programming sketches.
I am facing a problem. I am using a few things in my circuit like 7-segment display and relays and several indicating LEDs and a buzzer.
Let's sat that when a condition occurs I want one LED to blink while everything still works fine and sends data to the display and controls relays, and I solved that with the millis
function.
But the problem came when I tried to implement the same concept to the buzzer (which in my case I want to buzz for a pre-set amount of time, and then stop, without keeping buzzing all the time which might be too long), without using the delay
function.
I tried many many code but nothing would work for me so far. Here is the simple code of my sketch. I hope you guys will help me find a solution
const byte buzzer = 13; // buzzer_PIN
const byte switch1 = 3; // PUSH-BUTTON PIN
/* please NOTE:
that the switch in my project will actually represent a condition to start the buzzer alarm, then maybe that condition will not exist, and after that the condition might come back and need the buzzer again
*/
unsigned const int delaytime = 500; // delay time needed between on and off states for the buzzer
unsigned long previousBuzz = 0; // Variable for millis
unsigned long currentTime = millis();
boolean buzzPreState = true; // Boolean made to avoid repetition for the buzzer when not needed
void setup() {
pinMode(buzzer,OUTPUT);
pinMode(switch1,INPUT_PULLUP);
}
void loop() {
unsigned long currentTime = millis();
int switchVal = digitalRead(switch1);
if (switchVal == LOW && currentTime - previousBuzz > delaytime && buzzPreState == true) {
previousBuzz = currentTime;
buzzPreState = false;
buzzerStart(5);
buzzPreState = true;
}
}
void buzzerStart(int repeat) {
for (byte i=0; i<repeat; i++) {
digitalWrite(buzzer, !digitalRead(buzzer)); // to switch buzzer ON and OFF alternatively
}
}
1 Answer 1
You are missing a delay here:
for (byte i = 0; i < repeat; i++) {
digitalWrite(buzzer, !digitalRead(buzzer));
}
Without the delay, the whole loop terminates so fast that you cannot hear anything.
Now, if you want non-blocking code, you should instead use the approach of the Blink Without Delay tutorial:
- have the program remember the current state
- when it's time to switch states (as determined by
millis()
), update the current state.
The difficulty is that, whereas the program state in that tutorial is trivial (either the LED is off or it's on), in your case it is more complex. In addition to the time of the last toggle, you have to keep track of:
- whether the buzzer is off or on
- whether it is supposed to be making sound
- a counter for the buzzing cycles.
The approach I would use is to have a counter (let's call it
buzzer_remaining
) telling how many cycles it still has to complete.
The counter would decrement when the pin goes LOW
. When it reaches
zero, the buzzing is done. Note that this choice implies that, when the
condition for buzzing becomes true, we will start at the middle of a
buzzing cycle.
Here is a tentative implementation of this approach:
const uint32_t buzzer_half_period = 500; // 500 ms in each state
const uint8_t buzzer_count = 5; // buzz for 5 cycles
uint8_t buzzer_remaining; // remaining buzzing cycles
uint8_t buzzer_state; // LOW or HIGH
uint32_t buzzer_last_toggle; // millis() at last toggle time
void buzzer_start() {
// Ignore request to start if we are already buzzing.
if (buzzer_remaining > 0)
return;
// Initialize the buzzer state.
buzzer_remaining = buzzer_count;
buzzer_state = HIGH;
digitalWrite(buzzer, buzzer_state);
buzzer_last_toggle = millis();
}
// Call this periodically from loop().
void buzzer_update() {
// Do nothing if we are not supposed to be buzzing.
if (buzzer_remaining == 0)
return;
// Do nothing if it is too early to switch state.
uint32_t now = millis();
if (now - buzzer_last_toggle < buzzer_half_period)
return;
// Update the buzzer state.
if (buzzer_state == LOW) {
buzzer_state = HIGH;
} else {
buzzer_state = LOW;
buzzer_remaining--;
}
digitalWrite(buzzer, buzzer_state);
buzzer_last_toggle = now;
}
I resisted the temptation to put all this inside a class. It would be cleaner, but I think this "plain C" style of programming might be easier on a newcomer.
Note that calling buzzer_start()
when the program is already buzzing
has no effect: the request to start the buzzer is ignored. This is not
the only possible choice: one could instead want to reset the counter in
order for the buzzing to last longer. The choice I made is arbitrary,
and I only made it because, in your question, the program requirements
are not fully specified.
tone()
function where you can specify the duration. it doesn't block execution. arduino.cc/reference/en/language/functions/advanced-io/tonebuzzerStart()
? Doesn't make much sense to me. An active buzzer just has to be turned on and off (viadigitalWrite()
). So you could replacebuzzerStart(5)
with a singledigitalWrite(buzzer, !digitalRead(buzzer);