ESP32 Arduino: Controlling a buzzer with PWM

The objective of this post is to explain how to control a buzzer with the ESP32, using its PWM functionalities. In particular, we are going to use ESP32’s LED PWM functions.

Introduction

The objective of this post is to explain how to control a buzzer with the ESP32, using its PWM functionalities. In particular, we are going to use ESP32’s LED PWM functions, covered in detail in this previous post.

Basically, using the LED PWM of the ESP32, we will be able to control both the duty cycle and the frequency of our output signal. Note however that we don’t need to use PWM to control a buzzer, since we just only need to vary its frequency and we can maintain a constant duty cycle.

Nevertheless, since the tone function is not yet implement on the Arduino libraries for the ESP32, using the PWM frequency control functionalities is a possible work around.

Although, as said before, we don’t need to change the duty cycle to control a buzzer and the best duty cycle is of 50% [1][2], we will vary it just to check what it happens. The expected effect is that a change on the duty cycle will affect the volume of the buzzer [1].

We are going to also vary the frequency, which will affect the type of sound produced.

Electronic schematic

For this tutorial I’m going to use a cheap buzzer module that already has all the hardware needed to directly control it from a microcontroller GPIO (more precisely, a transistor driver). This way, we are not drawing the current from the ESP32 GPIO pin.

Check the schematic bellow in figure 1.

Electric diagram of connection between ESP32 and buzzer module.
Figure 1 – Connection diagram between the ESP32 and a buzzer.

The code

We will start by declaring some global variables needed to control the PWM functionality of the ESP32. We will set the initial frequency (although we are going to change it later in the code), the channel of the PWM and the resolution of the duty cycle specification.

We will use channel 0 and a resolution of 8 bits for the duty cycle. For the frequency we are going to put 2000 Hz just to do the initial setup.

int freq = 2000;
int channel = 0;
int resolution = 8;

Now, in the setup function, we are going to setup the PWM with the ledcSetup function, which receives as input the PWM channel, the frequency and the duty cycle resolution.

Next, we will attach the PWM channel to an actual GPIO of the ESP32, by calling the ledcAttachPin function. This receives as input the GPIO and the channel. I’m going to use GPIO 12 of the ESP32. Please take in consideration that some ESP32 pins don’t directly map to the pins of the ESP32 boards.

We will also open start a serial communication, so we can check output some information about the execution. Check the full setup function bellow.

void setup() {
 
  Serial.begin(115200);
  ledcSetup(channel, freq, resolution);
  ledcAttachPin(12, channel);
 
}

Now we are going to move to the main loop function, where we will experiment with our buzzer by changing the frequency and duty cycle configurations.

The first value we will vary is the duty cycle. So at the beginning of the loop we will fix the frequency. Although we configured it in the setup function, we want it to reset to the original value at the beginning of each iteration of the main loop.

So, to set the frequency again, we call the ledcWriteTone function, passing as inputs the PWM channel and the frequency to set. We will set it to 2000 Hz, as the initial configuration.

ledcWriteTone(channel, 2000);

Next we will do a for loop starting with a PWM of zero and increasing it by 10 at each iteration. We can vary the duty cycle between 0 and 255, since we specified a resolution of 8 bits.

We will introduce a small delay of 1 second in each iteration, so we can listen to the effects of changing it. We will also print the current duty cycle value.

To set the duty cycle, we just need to call the ledcWrite function, passing as input both the channel and the duty cycle value to set. Check the full loop bellow.

for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle=dutyCycle+10){
 
    Serial.println(dutyCycle);
 
    ledcWrite(channel, dutyCycle);
    delay(1000);
}

Next we are going to vary the frequency. Thus, we will start by fixing the duty cycle on 125 (approximately 50%). Next, we will do a for cycle similar to the previous one but this time starting with a frequency of 0 Hz and increase it by 250 until 10000 Hz. We will also print the current value again to the serial port.

You can check bellow the full source code, which already includes this previously mentioned loop where the frequency is incremented by calling the ledcWriteTone function.

int freq = 2000;
int channel = 0;
int resolution = 8;
 
void setup() {
 
  Serial.begin(115200);
  ledcSetup(channel, freq, resolution);
  ledcAttachPin(12, channel);
 
}
 
void loop() {
 
  ledcWriteTone(channel, 2000);
 
  for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle=dutyCycle+10){
 
    Serial.println(dutyCycle);
 
    ledcWrite(channel, dutyCycle);
    delay(1000);
  }
 
  ledcWrite(channel, 125);
 
  for (int freq = 255; freq < 10000; freq = freq + 250){
 
     Serial.println(freq);
 
     ledcWriteTone(channel, freq);
     delay(1000);
  }
 
}

Testing the code

To test the code, just upload it to the ESP32 using the Arduino IDE. Then open the serial port to check the values of frequency an duty cycle changing.

With the ESP32 connected to the buzzer, you should first ear a changing in the volume produced by the buzzer, caused by changing the duty cycle. Then, when changing the frequency, you will notice that the sound produced will also change.

You can check in the video bellow the result of running this code on a ESP32 board.

Related Posts

Related content

References

[1] http://justanotherlanguage.org/content/tutorial_pwm2

[2] http://www.microchip.com/forums/m155649.aspx

9 Replies to “ESP32 Arduino: Controlling a buzzer with PWM”

  1. I try this example and it is working ,thank you.
    One question, why can’t I stop my buzzer with ledcWrite(channel,0)?, I still hear sound from buzzer. I try to find answer everywhere but still no clue.

    1. Hi!

      You’re welcome, and thanks for the feedback 🙂

      Since I’ve written this a while ago, I don’t recall what was the exact behavior of the signal when it was set to 0 and if it was causing similar issues on my device.

      But is there any possibility of that sound being caused by some kind of electrical noise?

      Are you using other peripherals or just the buzzer? Are the wires between your ESP32 and the buzzer long?

      Best regards,
      Nuno Santos

      1. Hi! I use buzzer module which low level trigger. Later from when I asked you, I found out that -1 is the number to stop the sound, I also try to recheck by ledcRead from the pin which just wrote with -1, but the return number is ridiculously around 200,000 or something, but it works fine now. I can pause the sound without any problem, maybe a bit worry about damage my components.

        Sound of buzzer when duty = 0, is the same pitch when digitalWrite(pin, HIGH), I mean full annoying but sounding with ledcWriteTone correctly. A short interval of that squeak appearing everytime when command run into set duty to 0. But I cannot stop sound with bruteforce digitalWrite(pin, LOW) or even ledcDetachPin too. So I roughly assumed that timer which running in the background never be stop even duty = 0.

        1. Hi,

          Nice to hear that you found a workaround 🙂

          Nonetheless, my suggestion would also be to ask around the ESP32 forum to check if there is a better solution, to make sure you don’t damage the components and you have the desired behavior.
          https://esp32.com/

          Surely someone there already had the same situation to solve 🙂

          Note that these APIs work with unsigned numbers, so -1 is equal to a very big number, rather than to a negative value!

          This is most likely why you are receiving such big number in the ledcRead function call.

          I’m not sure what is the ESP32 behavior when this happens and how the signal is affected.

          Let us know if someone there could help 🙂

          Best regards,
          Nuno Santos

  2. I try this example and it is working ,thank you.
    One question, why can’t I stop my buzzer with ledcWrite(channel,0)?, I still hear sound from buzzer. I try to find answer everywhere but still no clue.

    1. Hi!
      You’re welcome, and thanks for the feedback 🙂
      Since I’ve written this a while ago, I don’t recall what was the exact behavior of the signal when it was set to 0 and if it was causing similar issues on my device.
      But is there any possibility of that sound being caused by some kind of electrical noise?
      Are you using other peripherals or just the buzzer? Are the wires between your ESP32 and the buzzer long?
      Best regards,
      Nuno Santos

      1. Hi! I use buzzer module which low level trigger. Later from when I asked you, I found out that -1 is the number to stop the sound, I also try to recheck by ledcRead from the pin which just wrote with -1, but the return number is ridiculously around 200,000 or something, but it works fine now. I can pause the sound without any problem, maybe a bit worry about damage my components.
        Sound of buzzer when duty = 0, is the same pitch when digitalWrite(pin, HIGH), I mean full annoying but sounding with ledcWriteTone correctly. A short interval of that squeak appearing everytime when command run into set duty to 0. But I cannot stop sound with bruteforce digitalWrite(pin, LOW) or even ledcDetachPin too. So I roughly assumed that timer which running in the background never be stop even duty = 0.

        1. Hi,
          Nice to hear that you found a workaround 🙂
          Nonetheless, my suggestion would also be to ask around the ESP32 forum to check if there is a better solution, to make sure you don’t damage the components and you have the desired behavior.
          https://esp32.com/
          Surely someone there already had the same situation to solve 🙂
          Note that these APIs work with unsigned numbers, so -1 is equal to a very big number, rather than to a negative value!
          This is most likely why you are receiving such big number in the ledcRead function call.
          I’m not sure what is the ESP32 behavior when this happens and how the signal is affected.
          Let us know if someone there could help 🙂
          Best regards,
          Nuno Santos

    1. Hi!

      I’ve been having some troubles with the new code syntax highlight plugin, which is not rendering properly all the code from some posts.

      Where you see & lt; it should be a < instead. If you do find and replace on the code, it should work 🙂 Sorry for the trouble, I'll try to fix this as soon as I can. Best regards, Nuno Santos

    1. Hi!
      I’ve been having some troubles with the new code syntax highlight plugin, which is not rendering properly all the code from some posts.
      Where you see & lt; it should be a < instead. If you do find and replace on the code, it should work 🙂 Sorry for the trouble, I'll try to fix this as soon as I can. Best regards, Nuno Santos

Leave a Reply