Project 4.08 Metronome II
In this project, we sense taps on the piezo to set the beat of a metronome. This requires us to use the piezo as both an input, to record the taps, and as an output, to play back the tones. The piezo records four taps and then emits beeps at the average rate of the four taps. We use the red channel of the RGB LED to indicate when a tap has been recorded.
We set up SW1 as our reset. Upload this sketch and tap on the piezo four times. We print the tempo to the serial monitor, so open it once the sketch has been uploaded. It will play back the tempo until you hold down SW1. This resets the Arno and gets it ready for four more taps.
Concepts: analogRead, boolean variable type, functions, switching pinMode
Circuits:
We set up SW1 as our reset. Upload this sketch and tap on the piezo four times. We print the tempo to the serial monitor, so open it once the sketch has been uploaded. It will play back the tempo until you hold down SW1. This resets the Arno and gets it ready for four more taps.
Concepts: analogRead, boolean variable type, functions, switching pinMode
Circuits:
In the start of the program, we declare a long array to hold the millis() values of each tap:
long times[4];
In the setup() block we set up the piezo using our clearPiezo function:
clearPiezo();
Like project 4.07, we record the value from analogRead using the variable knock. If a strike is detected, we record the time in the array times and flash the redLED. Next, we increment count to record the number of strikes. We use count to move through the times array and to tell when four strikes have been recorded.
knock = analogRead(piezo);
if(knock > 40){
times[count] = millis();
digitalWrite(redLED,HIGH);
delay(50);
digitalWrite(redLED,LOW);
count ++;
Before exiting the if block, we make sure that the piezo pin has been brought back to near zero. We use a while loop and one or more calls to clearPiezo to reset the piezo pin:
while(knock > 20){
clearPiezo();
knock = analogRead(piezo);
}
}
The variable count tracks how many taps have been recorded. One we record 4 taps we switch to playback mode. At this point we switch the pinMode of the piezo to an output:
if(count==4){
pinMode(piezo,OUTPUT);
count = 0; //reset for next time
periods = 0;
Next we want to calculate the time lapse between taps:
//calculate periods
for(int j = 0; j<3; j++){
//measure the time between beats
periods = periods + times[j+1] - times[j];
}
periods = periods/3;
We output the results to the serial monitor. Metronomes usually are set in beats per minute so we convert the value from milliseconds to beats per minute for the user. We also don’t allow values of < 50 milliseconds (that would be 12000 beats per minute!):
if(periods < 50) periods = 51;
Serial.print("Beats per Minute = ");
Serial.println(60L*1000L/periods);
Notice that when we calculate the beats per minute in the last line above, we put an ‘L’ at the end of the numbers 60 and 1000. This tells the sketch to treat these numbers as long variable types. Without the ‘L’, the sketch might treat theses as int variables, which would ruin our calculation.
We now playback the beat until SW1 is pressed:
//now play back until SW1 pressed
while(digitalRead(SW1)==HIGH){
//create tone
digitalWrite(redLED,HIGH);
piezoTone(2500,50);
digitalWrite(redLED,LOW);
Since the tone lasts of 50 milliseconds, we subtract that from the delay between tones to keep on the beat:
delay(periods-50);
Once we break out of the while loop, we clear the piezo before starting the process over again:
//clear piezo while button is pressed
while(digitalRead(SW1)==LOW){
clearPiezo();
}
long times[4];
In the setup() block we set up the piezo using our clearPiezo function:
clearPiezo();
Like project 4.07, we record the value from analogRead using the variable knock. If a strike is detected, we record the time in the array times and flash the redLED. Next, we increment count to record the number of strikes. We use count to move through the times array and to tell when four strikes have been recorded.
knock = analogRead(piezo);
if(knock > 40){
times[count] = millis();
digitalWrite(redLED,HIGH);
delay(50);
digitalWrite(redLED,LOW);
count ++;
Before exiting the if block, we make sure that the piezo pin has been brought back to near zero. We use a while loop and one or more calls to clearPiezo to reset the piezo pin:
while(knock > 20){
clearPiezo();
knock = analogRead(piezo);
}
}
The variable count tracks how many taps have been recorded. One we record 4 taps we switch to playback mode. At this point we switch the pinMode of the piezo to an output:
if(count==4){
pinMode(piezo,OUTPUT);
count = 0; //reset for next time
periods = 0;
Next we want to calculate the time lapse between taps:
//calculate periods
for(int j = 0; j<3; j++){
//measure the time between beats
periods = periods + times[j+1] - times[j];
}
periods = periods/3;
We output the results to the serial monitor. Metronomes usually are set in beats per minute so we convert the value from milliseconds to beats per minute for the user. We also don’t allow values of < 50 milliseconds (that would be 12000 beats per minute!):
if(periods < 50) periods = 51;
Serial.print("Beats per Minute = ");
Serial.println(60L*1000L/periods);
Notice that when we calculate the beats per minute in the last line above, we put an ‘L’ at the end of the numbers 60 and 1000. This tells the sketch to treat these numbers as long variable types. Without the ‘L’, the sketch might treat theses as int variables, which would ruin our calculation.
We now playback the beat until SW1 is pressed:
//now play back until SW1 pressed
while(digitalRead(SW1)==HIGH){
//create tone
digitalWrite(redLED,HIGH);
piezoTone(2500,50);
digitalWrite(redLED,LOW);
Since the tone lasts of 50 milliseconds, we subtract that from the delay between tones to keep on the beat:
delay(periods-50);
Once we break out of the while loop, we clear the piezo before starting the process over again:
//clear piezo while button is pressed
while(digitalRead(SW1)==LOW){
clearPiezo();
}