Hardware Interrupt with Piezo Buzzer

I'm writing a program where I can play different melodies through a little Piezo buzzer and every time a button is pressed it plays the next song. The way my code is written right now, the current song has to finish playing before it will switch to the next song. My goal is to get it change songs immediately after the user clicks the button. What can I do to achieve this goal? For reference I am using an Arduino Nano Every for this project. It should also be noted I borrowed the code for the Piezo buzzer which had limited comments. My comments for that code is how I interpret it when I read it. There's a pretty good chance that I may have misunderstood something when reading it, however I can confirm that the actual code as written works. If you notice a misinterpretation of the code, please mention it in the comments.

#include "pitches.h"

#define button 2
#define buzzer 3

volatile int bCount = 0; // a counter for number of button presses

/* The first 3 arrays here are where the notes of the song are defined.
   It references the "pitches.h" file where all of the notes are defined by
   there corresponding frequencies.*/

int songA[] = {
     //notes of song go here
};

int songB[] = {
     //notes of song go here
};

int songC[] = {
     //notes of song go here
};

/* The following 3 arrays defines the length of each note. These values are
   equivalent to the written note duration in sheet music (e.g. quarter notes = 
   0.25, half notes = 0.5, etc.). It is important each of these arrays are the
   same size as the matching song arrays above.*/

float songA_duration = {
     //length of notes go here
};

float songB_duration = {
     //length of notes go here
};

float songC_duration = {
     //length of notes go here
};

void setup(){
     pinMode(buzzer,OUTPUT); //Defines the digital pin connected to the buzzer as an output
     pinMode(button,INPUT); //Defines the digital pin connected to the button as an input
     attachInterrupt(button,button_pressed,RISING); //Defines the button as a hardware interrupt. When the digital pin sees a rising signal it will activate the ISR button_pressed.
}

void loop(){
/* This part of the code decides which song is played by getting
   the current button count modulo 3 and adding 1. That number
   will then be placed in the playSong function and the corresponding
   song will play. */

     song = bCount % 3 + 1;
     playSong(song);
}

void button_pressed(){
// ISR. When button is pressed, increment counter.
     bCount++;
}

void playSong(int song){
/* This function will play a song based on the number it is given. It
   first determines the song length by dividing the size of the array
   for each song and dividing it by the size of the first element of the
   array. It will then loop through both the song array and the
   duration array. The corresponding notes will be played through
   the buzzer. Note: The constant (180/148) in the duration floating
   point number was created to adjust for different tempos. Ideally,
   the '148' value could be replaced with the tempo of each new song
   and that exact tempo would be played. However, the tempo is not
   greatly effected except for when the constant is equal to a whole
   number. */

     if(song == 1){
          int songLength = sizeof(songA)/sizeof(songA[0]);

          for(int thisNote = 0; thisNote < songLength; thisNote++){
               float duration = (180/148) * 1000 * songA_duration[thisNote];
               tone(buzzer,songA[thisNote],duration);
               float pause = duration * 1.3;
               delay(pause);
               noTone(buzzer);
          }
     } else if(song == 2) {
          int songLength = sizeof(songB)/sizeof(songB[0]);

          for(int thisNote = 0; thisNote < songLength; thisNote++){
               float duration = (180/148) * 1000 * songB_duration[thisNote];
               tone(buzzer,songB[thisNote],duration);
               float pause = duration * 1.3;
               delay(pause);
               noTone(buzzer);
          }
     } else if(song == 3){
          int songLength = sizeof(songC)/sizeof(songC[0]);

          for(int thisNote = 0; thisNote < songLength; thisNote++){
               float duration = (180/148) * 1000 * songC_duration[thisNote];
               tone(buzzer,songC[thisNote],duration);
               float pause = duration * 1.3;
               delay(pause);
               noTone(buzzer);
          }
     }
}

The trap you’ve fallen into is

for (…) {
  xxx
  delay(sometime);
}

You either have to find a way to test and escape the for() loop, or use a different loop method, and millis() timing.

I would add a check for the button press inside each 'for' loop. If the button is pressed, return;. That will end the playing at the end of the current note.

Note: in loop() you should wait until the button is released between detecting a button press and calling playSong(). If you don't, playSong() might see that the button is still pressed and return after only one note.

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