Arduino metronome isn't working

Hi, I'm very new to Arduino (and coding in general, honestly). I'm trying to make a metronome that receives user input, but when I upload my code onto the Arduino board (which is connected to a breadboard with a speaker), a shrill and continuous beep sound is emitted from the speaker. I don't get any error messages, but the program isn't even close to doing what I want it to do. Can someone please tell me what I'm doing wrong? My code is below. Thanks so much.

  • I don't have a touch shield or anything, so I use the Serial Monitor to input numbers for the metronome.
int pin = 8;
int bpg; // number of beats on page
int bpm; // tempo of the piece
float answer = bpg * 60000.00 / bpm; // number of milliseconds it takes to play the page of music 
float tpbeat = answer / bpg; // number of milliseconds between each beat
float t = 0.00;
                    
void setup() {
  Serial.begin(9600);
  tone (pin, 30, 50);
}


void loop() {

Serial.println("Enter number of beats on page:"); 
      while (Serial.available() == 0){
      };
      bpg = Serial.parseInt();

Serial.println("Enter tempo:");
      while (Serial.available()==0){
      };
      bpm = Serial.parseInt();

float answer = Serial.parseFloat();
float tpbeat = Serial.parseFloat();


for (float t = 0.00; t <= answer; t += tpbeat) {
     tone (pin, 30, 50);
     delay (tpbeat);
     } // speaker is supposed to beep every 'tpbeat' milliseconds, until 'tppg' milliseconds pass
}

I think the first thing to do is to just test the tone function.

Use the simplest arduino code you can get, and just test the tone function itself..... to ensure it's giving the correct tone. Eg, tone(pin, 30, 50); means 30 Hz for 50 millisecs.

I would try tone(pin, 30, 5000); to give 5 seconds of sound, just for testing.

It appears you put the computation lines outside of the 'working' loop of your program. So it just appears that your program isn't periodically doing the computations.

So the lines such as ....

float answer = bpg * 60000.00 / bpm; // number of milliseconds it takes to play the page of music
float tpbeat = answer / bpg; // number of milliseconds between each beat

..... should ALSO go somewhere before....

float answer = Serial.parseFloat();
float tpbeat = Serial.parseFloat();

..... this means.... you can probably keep most of your code as-is, but get rid of these two lines below....

float answer = Serial.parseFloat();
float tpbeat = Serial.parseFloat();

..... we remove the above, because your program is supposed to calculate values of 'answer' and 'tpbeat' after you've supplied the program with values of 'bpg' and 'bpm'. While a line of code such as:
answer = Serial.parseFloat(); basically grabs incoming serial information and converts it into a number and assigns that number to 'answer', which isn't what you wanted to do, right?

... and you should give some initial value for 'bpg' eg.... bpg = 10; or whatever.
and for 'bpm' as well. So ...at the very top of your code, put in some initial values for these.

Then make one part of your code look like this maybe.....

Serial.println("Enter tempo:");
      while (Serial.available()==0){
      };
      bpm = Serial.parseInt();

answer = bpg * 60000.00 / bpm; // number of milliseconds it takes to play the page of music
tpbeat = answer / bpg; // number of milliseconds between each beat

And finally..... just for testing purposes, don't be afraid to temporarily add some serial print statements for you to see whether or not the software is giving you sensible values when it is running.... such as... in your loop, add lines like...

Serial.println(answer);

Okay, the tone function is fine by itself. I put the computation lines at the spot you suggested, and assigned #s to the ints. And I added Serial.println to see the numbers I put in. This is what happened:

Enter number of beats on page:
125 // number I put in
Enter tempo:
0 // automatically put in for me?

right after I put in 125, the speaker beeps continuously like it did before. :((((

frustrasians:
Okay, the tone function is fine by itself.

Yes, it is, thousands of us have tested it successfully. The question is, does it work in your setup?
Hardcode a pattern and test it.

lg, couka

Try this....

int pin = 8;
int bpg = 125; // number of beats on page
int bpm = 60; // tempo of the piece
int beep_duration = 50; //microseconds
float answer = bpg * 60000.00 / bpm; // number of milliseconds it takes to play the page of music
float tpbeat = answer / bpg; // number of milliseconds between each beat
float t = 0.00;
                   
void setup() {
  Serial.begin(9600);
  tone (pin, 30, 50);
}


void loop() {

if (Serial.available() == 0)
{

Serial.println(bpg);
Serial.println(bpm);
Serial.println(answer);
Serial.println(tpbeat);


     tone (pin, 30, beep_duration);
     delay(tpbeat - beep_duration);  //the subtraction is for taking into account that the 'beep' takes a certain amount of time. And, in order to have the next 'beep' start at exactly 'tpbeat' microseconds after the BEGINNING of the first beep, the gap between the END of the first beep and the start of the second beep needs to be 'tpbeat - beep_duration'


      // speaker is supposed to beep every 'tpbeat' milliseconds, until 'tppg' milliseconds pass
}
else 
{
  while (Serial.available()) //keep reading receive buffer until there's nothing left in it.... flush it out before proceeding.
  {
    Serial.read();  
  }
  
  Serial.println("Enter number of beats on page:");
      while (Serial.available() == 0){
      };
      bpg = Serial.parseInt();

Serial.println("Enter tempo:");
      while (Serial.available()==0){
      };
      bpm = Serial.parseInt();
      
      float answer = bpg * 60000.00 / bpm; // number of milliseconds it takes to play the page of music
      float tpbeat = answer / bpg; // number of milliseconds between each beat
}
}

When it runs, watch the serial monitor..... and, whenever you want to enter new input values, type a single character, then hit enter. Doing this will get you into the value entry mode.

Later, comment out the Serial.println lines that I put in..... since the more commands that the software needs to execute, the less accurate the timing will be (due to the commands taking up some time). If it's not significant, then you may leave those Serial.println lines as-is.

Enter number of beats on page:
125 // number I put in
Enter tempo:
0 // automatically put in for me?

Set your serial monitor to No Line Ending. There is a pull down menu for selection at the bottom right of the monitor window.

Thank you all so much! @cattledog, I did what you said AND IT WORKS THANK YOU ^^

@southpark I tried your code but didn't use it; however I learned about the valuable delay function from your comments. Thank you!

I'm sort of ecstatic guys

Absolutely most welcome frustrasians. Great to hear it's working now.