Piezo Buzzer

How to generate tones and alerts with a piezo buzzer and ESP32

Buzzers are the simplest way to add audio feedback to your project -- think alarm beeps, button click sounds, notification tones, or even simple melodies. There are two types of buzzers, and they work quite differently. In this guide, you will learn the difference between active and passive buzzers, how to wire each one, and how to play everything from a simple beep to a recognizable melody.

This guide uses the ESP32-WROOM-32 DevKit. Pin labels and GPIO numbers may differ on your board -- always check your board's pinout diagram.

🔗Active vs. Passive Buzzers

FeatureActive BuzzerPassive Buzzer
Internal oscillatorYes -- built inNo -- needs external signal
To make soundJust apply DC voltage (HIGH/LOW)Send a PWM signal at desired frequency
Frequency controlFixed tone (one pitch only)Variable -- you choose the frequency
MelodiesNo (only on/off beeping)Yes -- play different notes
How to identifyHas a sealed top, often with a + markOpen PCB on the back, or exposed elements

Tip: If you are unsure which type you have, connect it briefly to 3.3V and GND. An active buzzer will immediately produce a continuous tone. A passive buzzer will either make no sound or produce a faint click.

🔗Key Specs

ParameterValue
Operating voltage$3.3$ to $5\,\text{V}$ (varies by model)
Current draw$\approx 25$ to $30\,\text{mA}$
Frequency (active)Fixed, typically $\approx 2\,\text{kHz}$
Frequency (passive)Depends on your PWM signal ($200$ to $5000\,\text{Hz}$ typical)

🔗What You'll Need

ComponentQtyNotesBuy
ESP32 dev board1AliExpress | Amazon.de .co.uk .com
Active buzzer1Or passive buzzer for melodiesAliExpress | Amazon.de .co.uk .com
Breadboard1AliExpress | Amazon.de .co.uk .com
Jumper wires2AliExpress | Amazon.de .co.uk .com

Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.

🔗Wiring

The wiring is the same for both active and passive buzzers.

Buzzer PinESP32 PinNotes
+ (longer leg)GPIO 26Signal pin
- (shorter leg)GND

Note: Some buzzer modules have three pins (S, VCC, GND) on a small PCB. In that case, connect S to GPIO 26, VCC to 3.3V, and GND to GND.

Most small buzzers draw around $25\,\text{mA}$, which is within the ESP32 GPIO's capability. For louder buzzers that draw more current, consider driving them through a transistor (NPN such as 2N2222 with a $1\,\text{k}\Omega$ base resistor).

🔗Code Example: Active Buzzer -- Beep Pattern

An active buzzer only needs a HIGH signal to sound. This sketch creates an alarm-like beeping pattern.

#define BUZZER_PIN 26

void setup() {
    pinMode(BUZZER_PIN, OUTPUT);
}

void beep(int onTime, int offTime) {
    digitalWrite(BUZZER_PIN, HIGH);
    delay(onTime);
    digitalWrite(BUZZER_PIN, LOW);
    delay(offTime);
}

void loop() {
    // Three short beeps
    for (int i = 0; i < 3; i++) {
        beep(100, 100);
    }
    delay(500);

    // One long beep
    beep(500, 500);

    delay(1000);
}

🔗Code Example: Passive Buzzer -- Play a Scale

A passive buzzer needs a PWM signal to produce sound. The frequency of the signal determines the pitch. The ESP32 provides the ledcWriteTone() function, which is perfect for this.

#define BUZZER_PIN 26

// Note frequencies in Hz
#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494
#define NOTE_C5  523

int scale[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,
               NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5};

void setup() {
    ledcAttach(BUZZER_PIN, 2000, 8);  // Initial frequency does not matter
}

void playTone(int frequency, int duration) {
    ledcWriteTone(BUZZER_PIN, frequency);
    delay(duration);
    ledcWriteTone(BUZZER_PIN, 0);  // Silence
    delay(50);                     // Short gap between notes
}

void loop() {
    // Play ascending scale
    for (int i = 0; i < 8; i++) {
        playTone(scale[i], 300);
    }

    delay(1000);

    // Play descending scale
    for (int i = 7; i >= 0; i--) {
        playTone(scale[i], 300);
    }

    delay(2000);
}

🔗Code Example: Passive Buzzer -- Simple Melody

This sketch plays a recognizable melody by defining arrays of notes and durations.

#define BUZZER_PIN 26

// Note definitions (frequency in Hz)
#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494
#define NOTE_C5  523
#define REST     0

// "Ode to Joy" (simplified)
int melody[] = {
    NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4,
    NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4,
    NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4,
    NOTE_E4, NOTE_D4, NOTE_D4, REST
};

// Note durations (in milliseconds)
int durations[] = {
    400, 400, 400, 400,
    400, 400, 400, 400,
    400, 400, 400, 400,
    600, 200, 800, 400
};

int noteCount = sizeof(melody) / sizeof(melody[0]);

void setup() {
    ledcAttach(BUZZER_PIN, 2000, 8);
}

void playTone(int frequency, int duration) {
    if (frequency == REST) {
        ledcWriteTone(BUZZER_PIN, 0);
    } else {
        ledcWriteTone(BUZZER_PIN, frequency);
    }
    delay(duration);
    ledcWriteTone(BUZZER_PIN, 0);
    delay(30);  // Brief pause between notes
}

void loop() {
    for (int i = 0; i < noteCount; i++) {
        playTone(melody[i], durations[i]);
    }

    delay(3000);  // Wait before repeating
}

🔗How It Works

🔗Active Buzzers

An active buzzer contains an internal oscillator circuit. When you apply a DC voltage (set the GPIO HIGH), the oscillator drives a piezoelectric element at a fixed frequency, producing a tone. The pitch is determined by the internal circuit and cannot be changed -- you can only control whether the buzzer is on or off.

🔗Passive Buzzers

A passive buzzer is essentially a bare piezoelectric element with no internal oscillator. It needs an external AC signal (a square wave) to produce sound. The frequency of your signal determines the pitch:

$$f = \text{frequency in Hz} \Rightarrow \text{perceived pitch}$$

Human hearing ranges from approximately $20\,\text{Hz}$ to $20{,}000\,\text{Hz}$. The most audible and pleasant tones for buzzers are in the $200$ to $5000\,\text{Hz}$ range.

The ESP32's ledcWriteTone(pin, frequency) function generates a square wave at the specified frequency on the given pin, making it straightforward to play notes.

🔗Troubleshooting

ProblemPossible CauseSolution
No sound at allWrong buzzer pin or wiring reversedCheck GPIO number in code; verify polarity
Active buzzer makes no sound with ledcWriteTone()Active buzzers need DC, not PWM at arbitrary frequencyUse digitalWrite() for active buzzers
Passive buzzer clicks but no toneUsing digitalWrite() instead of PWMUse ledcWriteTone() for passive buzzers
Sound is very quietGPIO current too low for the buzzerDrive through a transistor (2N2222 + $1\,\text{k}\Omega$ base resistor)
Tone sounds off-pitchWrong frequency valuesDouble-check note frequency definitions
Buzzing or distortionPWM resolution or frequency mismatchTry different resolution values in ledcAttach()

🔗Next Steps

  • Use a buzzer as an alarm triggered by a sensor (for example, a motion detector or gas sensor)
  • Build a simple electronic piano with buttons mapped to different notes
  • Create a notification system that plays different tones for different events
  • Combine with a relay module for both audible and visual/electrical alerts