An Arduino melody with tone(): a song on a piezo
Beginner
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
A passive piezo and two lines of code are enough to play a tune on an Arduino. tone() makes a note, noTone() stops it, and a list of notes and lengths turns that into a song. This page builds one from scratch: where the numbers for the notes come from, how to keep time, and why each note needs a short silence after it.
The circuit above is an Uno playing the opening of Beethoven’s “Ode to Joy” on a piezo on pin 8, with a lamp on pin 13 that lights for every note. It runs in your browser on the sketch below. The serial monitor prints each pitch as it plays, and the simulated piezo measures the frequency it is really given, so you can check every note.
What you need
- An Arduino Uno
- A passive piezo buzzer (not an active one)
- One LED and a 220 ohm resistor
- A breadboard and jumper wires
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| Piezo, one leg | Pin 8 | Either leg; a piezo has no polarity |
| Piezo, other leg | GND | |
| LED anode, through 220 ohm | Pin 13 | The lamp that lights on each note |
| LED cathode | GND |
A note is a frequency
Every musical note is a vibration at a set number of times a second. The A above middle C is 440 hertz, and each step up the piano is about 6 percent higher, so an octave up doubles it. Middle C is 262 Hz, the E above it is 330 and the G is 392. tone(8, 330) makes pin 8 switch on and off 330 times a second, and the piezo turns that into an E.
tone() starts the note and returns at once. The note keeps playing in the background, from one of the Uno’s hardware timers, until you call noTone() or start another note. So the sketch decides how long a note lasts with its own delay().
The timer can only divide the Uno’s 16 MHz clock in whole steps, so most notes come out a hair off. Run the circuit and the piezo measures 330.7 Hz for the 330 Hz E. Nobody can hear that difference.
The sketch, step by step
The tune is two lists of the same length. NOTES holds the pitch of each note in hertz, and BEATS holds how long each one lasts. The lengths are counted in half beats, so 2 is one beat, 3 is a beat and a half and 4 is two beats. That keeps them whole numbers.
loop() walks both lists together. For each note it prints the pitch, lights the lamp, starts the tone, waits for the note’s length minus 30 milliseconds, then stops the tone and waits out those 30 milliseconds in silence. After the last note it says "again in two seconds" and starts over.
TEMPO_MS is the length of half a beat, 200 milliseconds here, so one beat is 400 ms: 150 beats a minute. Make it smaller and the whole tune speeds up, with no other change.
const int PIEZO = 8;
const int LAMP = 13;
const unsigned int C4 = 262;
const unsigned int D4 = 294;
const unsigned int E4 = 330;
const unsigned int F4 = 349;
const unsigned int G4 = 392;
const unsigned int NOTES[] = {
E4, E4, F4, G4, G4, F4, E4, D4, C4, C4, D4, E4, E4, D4, D4,
};
// Length in beats, times two, so a half beat is 1.
const unsigned char BEATS[] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 4,
};
const int COUNT = sizeof(BEATS);
const unsigned long TEMPO_MS = 200; // half a beat
const unsigned long GAP_MS = 30; // the silence between notes
void loop() {
for (int i = 0; i < COUNT; i++) {
unsigned long length = TEMPO_MS * BEATS[i];
Serial.print("note ");
Serial.print(NOTES[i]);
Serial.println(" Hz");
digitalWrite(LAMP, HIGH);
tone(PIEZO, NOTES[i]);
delay(length - GAP_MS);
noTone(PIEZO);
digitalWrite(LAMP, LOW);
delay(GAP_MS);
}
Serial.println("again in two seconds");
delay(2000);
}Why each note needs a gap
The tune starts with two E’s in a row. Play them back to back with no gap and you hear one long E, because the pin never stops switching. The 30 milliseconds of silence after every note is what makes two notes of the same pitch sound like two. It is also why the lamp blinks off between them.
Arduino’s own toneMelody example does the same thing another way: it calls tone(pin, note, duration) and then waits about 30 percent longer than the note. Either way works. What matters is that the silence is there.
Write your own song
You do not have to look up frequencies. #include "pitches.h" gives you names for every note from NOTE_B0 (31 Hz) to NOTE_DS8 (4978 Hz), the same numbers the Arduino IDE’s example uses, so NOTE_A4 is 440. Mokxi has the file built in, so the include works without adding a tab.
Then write the tune as two lists: the notes, and the lengths. Use 0 for a rest, and play a rest with noTone() and a delay instead of a tone. Copy a melody from sheet music one note at a time, check the count of both lists is the same, and press Run.
In the editor, try changing TEMPO_MS to 150 for a faster tune, or GAP_MS to 0 to hear the two E’s run together. Add 12 notes to each list and the loop plays them, because COUNT is worked out from the list.
Common mistakes
Using an active buzzer. It has its own oscillator and plays one pitch whatever you send it. A tune needs a passive piezo or a small speaker.
No gap between notes, so repeated notes blur into one.
Lists of different lengths. If BEATS is shorter than NOTES, the loop reads past its end and plays nonsense lengths. Count them, or let the compiler count one of them as this sketch does.
PWM stopping on pins 3 and 11. On an Uno, tone() uses timer 2, the same timer that drives PWM on those two pins, so analogWrite() on them stops working while a note plays. Use pins 5, 6, 9 or 10 for anything you fade.
Trying to play two notes at once. tone() works on one pin at a time. Start a note on a second pin and the first one stops.
A loose ground. With the piezo’s second leg pulled out, the piezo here goes silent, as a real one does.
Questions
How do I play a song on an Arduino buzzer?
Use a passive piezo, put the song’s notes in one array and their lengths in another, and loop through them calling tone(pin, note), delay(length), then noTone(pin) and a short delay. The sketch on this page does exactly that.
What is the difference between tone(pin, freq) and tone(pin, freq, duration)?
Both return at once. With a duration, the note stops by itself after that many milliseconds, but the sketch does not wait for it, so you still need a delay before the next note.
Where do I get pitches.h?
In the Arduino IDE it is a tab in the toneMelody example (File, Examples, 02.Digital). In Mokxi it is built in: #include "pitches.h" works on its own.
Can tone() make a sound on any pin?
Yes, any digital pin on an Uno. It uses timer 2 whichever pin you pick, which is why PWM on pins 3 and 11 pauses while it plays.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.