Help needed: 2 buttons to turn passive buzzer on and off

Hi all,

I am a design student who just started using Arduino as part of a subject at my university and have a problem with an assignment.

My goal: to make an alarm that turns on with button #1 and starts playing a melody along with some LEDs that light up with each note. The alarm can then be turned off with button #2, stopping the melody from playing and the LEDs from lighting up.

My problem: when I press button #2 to turn the alarm off, nothing happens. I then have observed in the serial monitor that when I press button #1 and the melody starts playing, the serial monitor stops reading and doesn't receive any other input, meaning that when I press button #2 while the melody is still playing, it doesn't register that I pressed it.

I'm sorry if my explanation is not very good, I am totally new to this, so if there are questions please ask. I am totally stuck and don't know how to continue or what is really causing this problem. Any suggestions and help is muuuuch appreciated! :slight_smile: :slightly_smiling_face: :pray:


const int buttonPin1 = 12;     // the number of the pushbutton pin
const int buttonPin2 = 11; 
const int LED1 = 2;  //red
const int LED2 = 3;  //green
const int LED3 = 4;  //yellow

// variables will change:
int buttonState1 = 0;         // variable for reading the pushbutton status
int buttonState2 = 0; 

#include "pitches.h"

// 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() {

    // initialize the pushbutton pin as an input:
  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  
  Serial.begin(9600);
}



void loop() {
  // read the state of the pushbutton value:
  buttonState1 = digitalRead(buttonPin1);
  buttonState2 = digitalRead(buttonPin2);
Serial.println(buttonState1);
Serial.println(buttonState2);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState1 == HIGH) {
    delay(1000);                // here you can set how long the timer should take before the music plays

    
    // play music:
    
    playMusic();

  }

}




// define playMusic
 void playMusic(){


 for (int thisNote = 0; thisNote < 8; 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(13, melody[thisNote], noteDuration);

 
 // switch case to light up one LED with each note of the melody:
 switch(thisNote) {
      case 0:
      digitalWrite(LED1, HIGH);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

      case 1:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

      case 2:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, HIGH);
      break;
      
      case 3:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

      case 4:
      digitalWrite(LED1, HIGH);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

      case 5:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

      case 6:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

      case 7:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, HIGH);
      break;
     
    }

    // 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:
    if (buttonState2 == HIGH)  {
 
      
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      
    break;  
    }

   } 

  }

looks like there ~1 sec delay for each note being generated. this requires that button 2 be pressed after the delay. tone() may also add an additional delay. is this your observation?

it's also conventional to wire the buttons between the pin and ground, configure the pin to use internal pull-up resistor, INPUT_PULLUP and check for the pin being LOW

ATTENTION Pins are changed

#define buttonPin1 = 2;//      ATTENTION Pins are changed
#define buttonPin2 = 3;
#define LED1 = 4;  //red
#define LED2 = 5;  //green
#define LED3 = 6;  //yellow

volatile bool buttonState2 = false;

#include "pitches.h"

// 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.:
byte noteDurations[] = {
  4, 8, 8, 4, 4, 4, 4, 4
};

void setup() {

  // initialize the pushbutton pin as an input:
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(buttonPin2), ISR, FALLING )
}
void ISR() {
  buttonState2 = true;
  noTone(13);
}

void loop() {
  bool buttonState1 = digitalRead(buttonPin1) == LOW;
  Serial.println(buttonState1);
  if (buttonState1) {
    delay(10);
    playMusic();
  }
}

void playMusic() {
  for (byte thisNote = 0; thisNote < 8; thisNote++) {
    switch (thisNote) {
      case 0:
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        break;
      case 1:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, LOW);
        break;
      case 2:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, HIGH);
        break;
      case 3:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, LOW);
        break;
      case 4:
        digitalWrite(LED1, HIGH);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        break;
      case 5:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        break;
      case 6:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, LOW);
        break;
      case 7:
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, HIGH);
        break;
    }
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(13, melody[thisNote], noteDuration);
    Serial.println(buttonState2);
    if (buttonState2)  {
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      buttonState2 = false;
      break;
    } else {
      unsigned int pauseBetweenNotes = (noteDuration * 13) / 10;
      delay(pauseBetweenNotes);
    }
  }
}
noTone(13)

Intentional?

will not work?
I mean will it stop the sound if it calls in interrupt routine?

It doesn't register because you only read the button in loop. Once playMusic is invoked, it plays the whole thing. You will need to read the button in there too.

to make your code react at any time to the press of button 2 to stop the playing
the complete code must be written with non-blocking timing.

non-blocking means that

void loop()

is the only thing that is "looping"

void loop() {
  // read the state of the pushbutton value:
  buttonState1 = digitalRead(buttonPin1);
  buttonState2 = digitalRead(buttonPin2);
  //jump in   AND QUICKLY   jump out to read the buttons again

your device has 5 steps that run down in sequence

  1. waitForButtonStart
  2. waitForDelayFinished
  3. startTone
  4. waitForToneToEnd
  5. pauseAfterNote (and go on with step 3 with next note)

if all notes are played go back to step 1
or if button 2 is pressed go back to step 1

This functionality can be coded in two ways:

way 1: using a lot of flag-variables that are locked against each other using a lot of if-conditions

way 2: using a state-machine with only a few if-conditions and the switch-case-break statement.

The break enables to mutually exclusive execute only that part of the code that is requiered at the moment. This is what eliminates a lot of if-conditions (like nescessary in way 1)

You are using the switch-case-break-statement already in your code for switching the leds on/off

The principle how non-blocking timing works is explained here

So here is the code. It compiles but I haven't really tested it. There still might be some minor bugs in the code

const int buttonPin1 = 12;     // the number of the pushbutton pin
const int buttonPin2 = 11;
const int LED1 = 2;  //red
const int LED2 = 3;  //green
const int LED3 = 4;  //yellow

// states for the state-machine prefix "sm_" indicates constant for s)tate-m)achine
const byte sm_waitForButtonStart   = 1;
const byte sm_waitForDelayFinished = 2;
const byte sm_startTone            = 3;
const byte sm_waitForToneToEnd     = 4;
const byte sm_pauseAfterNote       = 5;

byte myState = sm_waitForButtonStart;

// variables will change:
int buttonState1 = 0;         // variable for reading the pushbutton status
int buttonState2 = 0;

#include "pitches.h"

// notes in the melody:
int melody[] = {
  // 0        1        2       3       4       5     6       7
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
  //1, 2, 3, 4, 5, 6, 7, 8 // used as dummy-notes for testing compiling
};

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

// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

unsigned long myDurationTimer;
unsigned long myDurationInterval;

unsigned long myWaitTimer;
unsigned long myWaitInterval = 1000;

unsigned long pausingTimer;
unsigned long pauseBetweenNotes;

byte noteNr; // variable that iterates through notes 0 to 7


void setup() {
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  Serial.begin(9600);
}


void loop() {
  // read the state of the pushbutton value:
  buttonState1 = digitalRead(buttonPin1);
  buttonState2 = digitalRead(buttonPin2);

  stateMachinePlayMusic(); // do one step and immidiately come back to read buttonstate again
  SwitchLEDs(noteNr);
}


void stateMachinePlayMusic() {

  if (buttonState2 == HIGH) {
    myState = sm_waitForButtonStart; // whenever button2 is pressed stop playing
    //Serial.println(buttonState1);
    //Serial.println(buttonState2);
  }

  switch (myState) {

    case sm_waitForButtonStart:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);

      if (buttonState1 == HIGH) {
        noteNr = 0; // set variable that iterates throughg the notes to first value
        myWaitTimer = millis(); // store actual timestamp in variable named "myWaitTimer"
        myState = sm_waitForDelayFinished;
      }
      break; // immidiately jump to end of switch


    case sm_waitForDelayFinished:
      if ( TimePeriodIsOver(myWaitTimer, myWaitInterval) ) {
        noteNr = 0;
        myState = sm_startTone;
      }
      break; // immidiately jump to end of switch


    case sm_startTone:
      myDurationTimer = millis(); // store actual timestamp in variable named "myDurationTimer"
      myDurationInterval = 1000 / noteDurations[noteNr]; // set variable named "myDurationInterval" to duration of first note
      tone(13, melody[noteNr], myDurationInterval);

      myState = sm_waitForToneToEnd;
      break; // immidiately jump to end of switch


    case sm_waitForToneToEnd:
      if (noteNr == 7) { // if melody is played completely change to
        myState = sm_waitForButtonStart;
        break; // immidiately jump to end of switch
      }

      if ( TimePeriodIsOver(myDurationTimer, myDurationInterval) ) {
        pausingTimer = millis();
        pauseBetweenNotes = myDurationInterval * 1.30;
        myState = sm_pauseAfterNote;
      }
      break; // immidiately jump to end of switch


    case sm_pauseAfterNote:
      if ( TimePeriodIsOver(pausingTimer, pauseBetweenNotes) ) {
        noteNr++; // increment variable named "noteNr" by one == play next note
        myState = sm_startTone;
      }
      break; // immidiately jump to end of switch
  } // end of switch-state
}


void SwitchLEDs(byte thisNote) {
  // switch case to light up one LED with each note of the melody:
  switch (thisNote) {
    case 0:
      digitalWrite(LED1, HIGH);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

    case 1:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

    case 2:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, HIGH);
      break;

    case 3:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

    case 4:
      digitalWrite(LED1, HIGH);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

    case 5:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);
      break;

    case 6:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
      digitalWrite(LED3, LOW);
      break;

    case 7:
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, HIGH);
      break;
  }
}

Be the change you want to see in the world
best regards Stefan

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