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
- waitForButtonStart
- waitForDelayFinished
- startTone
- waitForToneToEnd
- 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