Button Start Stop Loop Play Audio

Firstly I am new here, so Hi :). I am starting out with Arduino and I am using a UNO R3 as my first device. I am doing a simple project that I want to play a sound when button is pressed after a delay and then stop it once it has finished by pressing the button again. My eventual goal is to play an MP4 or WAV file, but for now I am testing with a melody via pitches.h.

At the moment if I press the button the melody plays, but then it doesn't want to stop if I press the button again. If I remove the buzzer() command and the start and stop with the button works fine. Any ideas on what I am doing wrong or is it just a timing thing with the Arduino?

#include <ezButton.h>
#include "pitches.h"

#define LOOP_STATE_STOPPED 0
#define LOOP_STATE_STARTED 1

ezButton button(7);  // create ezButton object that attach to pin 7;
int buzzer_pin = 3;
int loopState = LOOP_STATE_STOPPED;

// notes in the melody:
int melody[] = {

  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {

  4, 8, 8, 4, 4, 4, 4, 4
};

void setup() {
  Serial.begin(9600);
  button.setDebounceTime(100); // set debounce time to 50 milliseconds
}

void loop() {
  button.loop(); // MUST call the loop() function first

  if (button.isPressed()) {
    if (loopState == LOOP_STATE_STOPPED)
       loopState = LOOP_STATE_STARTED;
    else // if(loopState == LOOP_STATE_STARTED)
      loopState = LOOP_STATE_STOPPED;
  }
  if (loopState == LOOP_STATE_STARTED) {
    Serial.flush();
    Serial.println("The button is pressed");
    buzzer();
}
}

void buzzer() {
  // iterate over the notes of the melody:
  int size = sizeof(noteDurations) / sizeof(int);

  for (int thisNote = 0; thisNote < size; thisNote++) {
    // to calculate the note duration, take one second divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(buzzer_pin, melody[thisNote], noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(buzzer_pin);
  }
}

Cheers in advance :slight_smile:

I moved your topic to an appropriate forum category @geekydrewbie.

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

1 Like

Thanks @Delta_G. I will read up on non-blocking code. Thanks again for taking the time to point me in the right direction :slight_smile:

I'm unable to type much just now but it's the same thing alla time, as @Delta_G observres.

A old post of mine.

is my contribution to a thread entitled

     Using milli () delay instead of delay

all of which may be of interest, but at least you can search with some of the terms of art that crop around writing non-blocking code.

Be brave. This is one of those things that is hard until it is easy. But it is the secret to getting the most out of these wimpy little boards we play with.

Expect an Aha! moment.

HTH

a7

Thanks @alto777 :). At the moment I am not using any delay() commands, so just trying to understand why a button press isn't cancelling the loop I have in the code. I am sure I will get there :slight_smile:

Did you write the code you posted?

    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);

That's what determines the tempo, and inside a for loop as it is what makes your sketch go unresponsive for the duration.

There are simpler ways to handle your particular situation. It would just be a matter of checking the button in the for loop and exiting early if it got pressed.

a7

Morning @Delta_G. Sorry I should have said I had modified the code to remove that. I removed the Melody all together and just using tone (just to test the process I am trying to achieve). There is also a count to break the loop if it goes through 10 times (That works fine). At the moment pressing the button a second time doesn't cancel the loop.

#include <ezButton.h>

#define LOOP_STATE_STOPPED 0
#define LOOP_STATE_STARTED 1

ezButton button(7);  // create ezButton object that attach to pin 7;
int buzzer_pin = 3;
int loopState = LOOP_STATE_STOPPED;
int count = 0;

void setup() {
  Serial.begin(9600);
  button.setDebounceTime(50); // set debounce time to 100 milliseconds
}

void loop() {
  button.loop(); // MUST call the loop() function first

  if (button.isPressed()) {
    if (loopState == LOOP_STATE_STOPPED)
       loopState = LOOP_STATE_STARTED;
    else // if(loopState == LOOP_STATE_STARTED)
      loopState = LOOP_STATE_STOPPED;
  }
  if (loopState == LOOP_STATE_STARTED) {
   Serial.flush();
   Serial.println("Playing Tone");
   buzzer();
   count+=1;
   Serial.println(count);
   Serial.println(loopState);
   Serial.println("Button Count = " + String(button_count));
   if (count == 10) {
     loopState = LOOP_STATE_STOPPED;
     Serial.println(loopState);
     count = 0;}
  }
}

void buzzer() {
    unsigned long startTime = millis(); // grab current time
    unsigned long duration = 1000;
    //Serial.println("Start Time = " + String(startTime) ) ;

    while (millis () < startTime + duration) {
      //Serial.println("Buzzer Pin High");
      tone(buzzer_pin,1000);
    }
      //Serial.println("Buzzer Pin Low");
      noTone(buzzer_pin);
  }

Thanks @Delta_G, I will take another look at calling that differently. Thanks :slight_smile:

Morning @Delta_G , I have been looking at how I can achieve the tune to start playing, but to also allow the button to stop and I am struggling to get my head around how I can achieve that without blocking. I have looked at blink with out delay etc, but are there any examples you can suggest I look at in relation to what I am trying to do? Appreciate your time. Many Thanks

Thanks @Delta_G will take another look. Just came across this example too https://arduinogetstarted.com/faq/how-to-use-buzzer-without-blocking-other-code

Yes and 99% of all these tutorials are beaten with

the experts blindness for beginner difficulties

That is the reason why reading one of these tutorials requires

That all these tons of tutorials make it hard to understand has three reasons:

  1. NOT emphasizing the fundamental difference between delay() and non-blocking timing which is only BASED on millis(). It needs more than a simple call to function millis().

From a quick reading most newcomers think replace delay() with millis() and you are done
which is totally wrong.

  1. this: has been handed down for ages
    but still a very bad example this
    infamous inglorious blink without delay example of the arduino-IDE
    making non-blocking timing unnecessarily difficult to understand through badly chosen variable names, and scattering the variables around in the code.

  2. not using an everyday-example with easy to follow numbers that explains all details written out for most easiest understanding

The experienced users here will roll their eyes :roll_eyes: but I will repeat such postings until the Arduino-team will have replaced this BW-understand-example code with something much better and setup a tutorial that follows the three reasons I have written above

So here it is:

Additionally here is a short not yet finished tutorial outer high-level circular-repeating coding
which shows the fundamental difference to linear coding / inner low-level repeating coding.

best regards Stefan

Some. Others just be shaking their heads.

a7

1 Like

If you choose a DFplayer Mini or similar module to play your files, your blocking code may not be a problem. These modules, once instructed to play, continue playing with no further input from the Arduino. So your sketch can continue checking buttons etc.

I said earlier there are simpler ways to handle your particular situation.

The quoted lines above are the strat of the loop where the notes are stepped along.

If you were to check the button every time through this loop, and exit prematurely if you see it pressed, you would only have to be on the button for at most one note before the code noticed and broke out of the loop and returned, that might be satisfactory.

I'd supply more detail except it's raining here, and anyway I'm not sure how to weave it in there because you are using a library for the button handling, and that would have to be taken account of.

So the first press that launched the song would not be confused for a request to return early from the song playing function, and so any button activity that did mean return early would not also be seen again right away as an indictation that you want the song to start. Again.

Which is one of the reasons I prefer to do all button handling with code I wrote. But I am guessing you could get it to work with the library you using.

HTH

a7

Thanks @PaulRB I will take a look at that once I get the un-blocking code sorted in my head first :slight_smile:

Thanks @StefanL38, hopefully reading those other examples will help. Arduino and coding (beyond doing some PowerShell for work) is new to me and I find it hard to read something and understand it, so prefer to dive it and learn based on something I am trying to achieve.

To all the experts rolling their eyes and shaking their heads sorry :slight_smile:

I hope you don't see this as narcism:
This rolling eyes comment was exclusively about me because I often post similar postings about the "standard" bink without delay-example code.

It is in no way meant about you. Beeing a newcomer is normal. Everybody here once was a newcomer (at least at the age of 3 years)

You show own effort and you ask specific questions that is all it needs.

best regards Stefan

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.