Skip to main content

2.4 Buzzer Beep

The goal of this example is to make some noise. A passive buzzer is a tiny speaker: feed it a square wave and it vibrates at whatever frequency you send it, so a single buzzer can play any note you like.

In this example the NULA MINI plays a short eight-note scale the moment it powers up.

In this documentation you will learn:

  • How to connect a passive buzzer to the board.
  • How tone() turns a frequency into a sound.
  • How noTone() silences the buzzer again.
  • How to keep a melody in an array and step through it with a loop.

Hardware required:

  • 1x Soldered NULA MINI board
  • 1x Breadboard
  • 1x Passive buzzer
  • 3x Jumper wires
  • 1x USB-C cable
ℹ️
Unlike the LED examples, this one needs no resistor. The buzzer sits straight between IO5 and ground. There is nothing to protect it from, because the pin only ever swings between 0 V and 3.3 V.
⚠️
Make sure you are holding the passive buzzer and not an active one. A passive buzzer produces whatever frequency you send it, which is exactly what this example relies on. An active buzzer contains its own oscillator and can only beep at one fixed pitch, so the melody would come out as a single flat tone.

Putting the components together

Follow the five steps below. Each photo is taken from the same position, so you can compare it with the previous one and see exactly what changed.

ℹ️
This example only reaches two pins on the board: IO5 in row 28 and GND in row 30, both on the f–j side. The board body covers columns f–i, so j28 and j30 are the holes you can actually get a wire into.

1. Insert the NULA MINI board on the breadboard

ℹ️
This step assumes you know how a breadboard is wired inside and what its power rails are. For an introduction, see Breadboard Fundamentals documentation page.

Push the board into one end of the breadboard so that its two rows of pins sit on either side of the centre channel, with the chip facing down.

NULA MINI board seated on the breadboard
Step 1: the board seated on the breadboard

2. Place the buzzer and bring ground out to the rail

The buzzer goes on the f–j side, well clear of the board, with its two legs in row 14 and row 15.

Look at the top of the buzzer's case before you push it in. Next to the HWDZ moulding there is a small + inside a circle, and the leg on that side of the case is the positive one. That leg goes into row 15.

Then run a jumper from j30 (GND) across to the blue rail. That rail is the ground line for the rest of the example.

Passive buzzer in rows 14 and 15, with a jumper from j30 to the negative rail
Step 2: the buzzer bridging rows 14 and 15, and GND wired to the blue − rail
⚠️
The two legs sit in neighbouring rows, so take a moment to check that they really did land in two different rows. If both legs end up in the same row the buzzer is short-circuited and stays silent no matter what the code does.

3. Connect the positive leg to IO5

One jumper from j15 (the row holding the buzzer's + leg) down to j28 (IO5). This is the wire that will carry the square wave.

Jumper from j15 to j28, connecting the buzzer's positive leg to IO5
Step 3: the buzzer's + leg wired to IO5 in row 28

4. Connect the other leg to ground

One more jumper, from j14 (the buzzer's other leg) across to the blue rail, which is already tied to GND.

Jumper from j14 to the negative rail, completing the circuit
Step 4: the buzzer's other leg wired to the blue − rail, completing the circuit

The loop is now closed: IO5 → buzzer → GND. Whatever IO5 does, the buzzer feels.

5. Connect the board to your computer

The finished buzzer circuit powered over USB-C
Step 5: the finished circuit, powered over USB-C
ℹ️
Rows 14 and 15 are only the rows the photos happen to use. Any two free rows work, as long as the buzzer's legs sit in two different rows, IO5 reaches the + row, and the other row reaches ground.

How a passive buzzer makes a note

Sound is nothing more than air being pushed back and forth. Inside the buzzer is a thin disc that moves whenever the voltage across it changes, and moving it quickly enough makes a note you can hear.

That is all tone() does. It switches IO5 between 3.3 V and 0 V over and over, making a square wave, and the number of switches per second is the frequency, measured in hertz. Your ear reads that frequency as pitch:

NoteFrequency (Hz)
C4262
D4294
E4330
F4349
G4392
A4440
B4494
C5523
ℹ️
A higher frequency gives a higher pitch, a lower frequency a deeper one. The eight values above are one octave of a C major scale, which is exactly the melody this sketch plays.

The useful thing about tone() is that it keeps running in the background. Once a note has started, the board is free to carry on with the rest of the code, which is why the sketch uses delay() to decide how long to wait before starting the next one.


How the melody is stored

A melody is really two lists that travel together: which note, and how long it lasts. The sketch keeps them in two arrays of the same length:

  • melody[] holds the frequency of each note in hertz.
  • noteDuration[] holds the length of each note, written the way sheet music writes it: 4 for a quarter note, 2 for a half note.

A for loop then walks through both arrays one index at a time, plays the note with tone() and waits out its duration. When the loop finishes, noTone() makes sure the buzzer is left silent.


Code

/*
This is a variable to which we assign the number of the pin that we connected the buzzer to.
The NULA board has a pin naming logic as follows: IO5, where 5 is the number that we give to the variable.
*/
const int BUZZER_PIN = 5;

/*
We will use two arrays - one for note frequencies (in Hertz) and one for note durations.
These define a short melody that the buzzer will play.
*/
int melody[] = { 262, 294, 330, 349, 392, 440, 494, 523 }; // C4 to C5
int noteDuration[] = { 4, 4, 4, 4, 4, 4, 4, 2 }; // Quarter notes (last one is half note)

/*
Here we work out how many notes the melody has, instead of counting them by hand. sizeof() tells us how much memory
something takes up, so the size of the whole array divided by the size of one entry gives us the number of entries.
Written this way the number stays correct even after you add a note of your own.
*/
const int NOTE_COUNT = sizeof(melody) / sizeof(melody[0]);

void setup() {

/*
pinMode() is a function that configures the specified pin to behave either as an input or in this case as an output.
As our pin needs to drive the buzzer, we will put the pin in OUTPUT mode.
*/
pinMode(BUZZER_PIN, OUTPUT);

/*
Serial.begin() establishes serial communication between your board and your computer via a USB cable. We use it here
to let you know when the melody starts and when it is done.
*/
Serial.begin(115200);
Serial.println("Playing melody...");

/*
A for loop repeats a block of code a set number of times, counting with a variable of its own. Here "i" starts at 0
and grows by one each pass until it reaches NOTE_COUNT, so the block below runs once for every note in the melody.
*/
for (int i = 0; i < NOTE_COUNT; i++) {

/*
Musical note lengths are written as fractions: a quarter note is a quarter of a whole note. Here we turn that
fraction into milliseconds by dividing one second by the number in the array, so a 4 becomes 250 ms and a 2
becomes 500 ms.
*/
int duration = 1000 / noteDuration[i];

/*
tone() is a function that makes the buzzer produce a sound. It generates a square wave of the given frequency,
which our ears hear as the pitch of the note, and the third value tells it how long to keep playing.
*/
tone(BUZZER_PIN, melody[i], duration);

/*
delay() is a function that starts a pause in the code. We wait slightly longer than the note itself, which leaves a
short silence between notes so they do not run into each other. Feel free to experiment with the 1.3.
*/
delay(duration * 1.3);
}

/*
noTone() stops whatever sound the buzzer was making. The last note already ended on its own, but calling this makes
sure the buzzer is left silent no matter what.
*/
noTone(BUZZER_PIN);
Serial.println("Melody finished!");
}

void loop() {
// Nothing happens here - melody plays once in setup()
}

What you should see

Upload the sketch and the buzzer plays a rising scale of eight notes, about two seconds in all. Then it goes quiet.

Everything happens in setup() and loop() is empty, so the melody plays once per power-up. To hear it again, tap the RST button on the board.

Open Tools → Serial Monitor and set the baud rate to 115200, and you can watch the same thing in text:

Serial Monitor showing Playing melody and Melody finished
The Serial Monitor at 115200 baud, before and after the melody
ℹ️
If both messages appear but you hear nothing, the code is fine and the wiring is not. Check that the buzzer's legs are in two different rows, and that the jumper from IO5 lands in the same row as one of them.

Try it yourself: change one of the numbers in melody[] and upload again. Because NOTE_COUNT is worked out from the array itself, you can also add notes to both arrays and the loop will pick them up with no other change.


Full example

Check out the full example code on the link below:

2.4_Buzzer_Beep.ino

Example that shows how to use the tone() function to play a simple melody on a passive buzzer connected to IO5.