Playing a melody with a piëzo speaker on arduino UNO

Hi!

I've been trying to figure out how to make my Arduino UNO play a melody, but I want it to be programmable so that I can put in different notes for other melodies. There are a couple of variables that I would like to include in my code and these are tone length, tempo and of course the tone. Also, the button is there to start the melody, and I intend for the led to be blinking when the melody can be started by pressing the button, but I think I will be able to code these things later. Here is my code so far:

//Own Project 000 Melody player v.1

int button = 4;
int speaker = 5;
int led = 6;
int buttonState = 0;
// int tempo = 1;
int toneFrequency = 0;
int toneLength = 0;
int x = 0;
int waitTime = 0;


void h () {
  toneLength = 1;
}
void i () {
  toneLength = 2;
}
void j () {
  toneLength = 3;
}
void k () {
  toneLength = 4;
}


void c () {
  toneFrequency = 261;
}
void d () {
  toneFrequency = 294;
}
void e () {
  toneFrequency = 329;
}
void f () {
  toneFrequency = 349;
}
void g () {
  toneFrequency = 392;
}
void a () {
  toneFrequency = 440;
}
void b () {
  toneFrequency = 493;
}
void c2 () {
  toneFrequency = 523;
}

void playNote () {
  waitTime = 1000000 / (toneFrequency * 2);
  while (x < 1000000) {
    delayMicroseconds(1);
    x++;
  }
  while (x < 1000000) {
    digitalWrite(speaker, HIGH);
    delayMicroseconds(waitTime);
    digitalWrite(speaker, LOW);
    delayMicroseconds(waitTime);
  }
}


void setup() {
  pinMode(speaker, OUTPUT);
  pinMode(led, OUTPUT);
  pinMode(button, INPUT);
  startMicros = micros();
}

void loop() {
  h ();
  c ();
  playNote ();
  i ();
  f ();
  playNote ();
  i ();
  f ();
  playNote ();
  h ();
  g ();
  playNote ();
  h ();
  a ();
  playNote ();
  h ();
  b ();
  playNote ();
  h ();
  g ();
  playNote ();
  i ();
  a ();
  playNote ();
  h ();
  g ();
  playNote ();
  h ();
  a ();
  playNote ();
}

As you can see I haven't really figured out a way to work the tempo variable into my code (and thus it is behind // at the start of the code), but I did want to try it out to see if it would work for as far as I've gotten. When I uploaded the code to my Arduino nothing happened, even after resetting it with the reset button.

So basically I have this question:
Why does the code not work the way I want it to, and how do I make it work in order for it to play a tone, for example, a c-note, for say one second?

I would appreciate any help!

Thanks!

P.S. I am aware that there are better and more compact ways of playing a melody using an Arduino, but I wanted to challenge myself and come up with a way to do this with the very limited knowledge I have of Arduino coding at the moment.

There are several problems.
Main problems are in playNote:

void playNote () {
  waitTime = 1000000 / (toneFrequency * 2);
  while (x < 1000000) {
    delayMicroseconds(1);
    x++;
  }
  while (x < 1000000) {
    digitalWrite(speaker, HIGH);
    delayMicroseconds(waitTime);
    digitalWrite(speaker, LOW);
    delayMicroseconds(waitTime);
  }
}

x is an int, so it's range is -32768 to 32767 so it may never reach 1000000
but even if you make x an unsigned long, the first while loop is of little use:
x is counted up to 1000000 witch takes about one second - same effect as delay(1000).
But after that, the second part oft playNote, will never be executed, because x is already 1000000.
So you could add a line with x=0;
and the second while loop will be executed - but forever - because x will not count up in the second while loop... but you could hear some sound from the piezo at least.

You could change the sketch somehow like this, and you may hear some tones from the piezo:

int button = 4;
int speaker = 5;
int led = 6;
int buttonState = 0;
// int tempo = 1;
int toneFrequency = 0;
int toneLength = 0;
unsigned long x = 0;
int waitTime = 0;


void h () {
  toneLength = 1;
}
void i () {
  toneLength = 2;
}
void j () {
  toneLength = 3;
}
void k () {
  toneLength = 4;
}


void c () {
  toneFrequency = 261;
}
void d () {
  toneFrequency = 294;
}
void e () {
  toneFrequency = 329;
}
void f () {
  toneFrequency = 349;
}
void g () {
  toneFrequency = 392;
}
void a () {
  toneFrequency = 440;
}
void b () {
  toneFrequency = 493;
}
void c2 () {
  toneFrequency = 523;
}

void playNote () {
  waitTime = 1000000 / (toneFrequency * 2);
  while (x < 1000000) {
    delayMicroseconds(1);
    x++;
  }
  x = 0;
  while (x < 100) {
    digitalWrite(speaker, HIGH);
    delayMicroseconds(waitTime);
    digitalWrite(speaker, LOW);
    delayMicroseconds(waitTime);
    x++;
  }
  x = 0;
}


void setup() {
  pinMode(speaker, OUTPUT);
  pinMode(led, OUTPUT);
  pinMode(button, INPUT);
  // startMicros = micros();
}

void loop() {
  h ();
  c ();
  playNote ();
  i ();
  f ();
  playNote ();
  i ();
  f ();
  playNote ();
  h ();
  g ();
  playNote ();
  h ();
  a ();
  playNote ();
  h ();
  b ();
  playNote ();
  h ();
  g ();
  playNote ();
  i ();
  a ();
  playNote ();
  h ();
  g ();
  playNote ();
  h ();
  a ();
  playNote ();
}

But maybe it would be better to rethink and improve your concept. :slight_smile:

Why start a new thread when you had lots of answers here to an identical question?

uxomm:

void playNote () {

waitTime = 1000000 / (toneFrequency * 2);
 while (x < 1000000) {
   delayMicroseconds(1);
   x++;
 }
 x = 0;
 while (x < 100) {
   digitalWrite(speaker, HIGH);
   delayMicroseconds(waitTime);
   digitalWrite(speaker, LOW);
   delayMicroseconds(waitTime);
   x++;
 }
 x = 0;
}

I think it probably would be better to rethink my concept indeed but I'd really like to get this to work now since I've spent a lot of time on it already :smiley:

The problem I have is that if the toneFrequency is higher the waitTime will be shorter. This makes a loop of turning the speaker on and off last shorter, and thus for different tones, the length they play at will be different. I added the while loop that lasts one second in hopes of making sure the note would always be one second, but it turns out that the two while loops don't run at the same time. Basically, what I'm really looking for is for the speaker on/off sequence to last one second, but I don't know how to achieve this.

Perhaps you could help me with that?

Thanks beforehand!

groundFungus:
Why start a new thread when you had lots of answers here to an identical question?

I'm sorry, I didn't know I wasn't allowed to do that. I applied the knowledge I got from that thread too, but afterwards I found out I had another question. I thought I'd just post this new question instead so it would be clearer.

Sorry