
Update 3rd December 2024: I’ve discovered that the code I had listed here no longer worked with the Espressif ESP32 V3 core that was released a while back. I’ve updated the post so that the example sketch works with v3.
A reader alerted me to the Tone function for Arduino. When I was making my WordPress hit counter I wanted to add in an audio alert. I preferred something other than a constant tone but thought I would have a go using the tone function. It turns out that the tone function is not implemented with the ESP32.
After a bit of searching, I found a couple of useful pages:
- Lesson 5: Playing tones
- ESP32 Arduino: Controlling a buzzer with PWM
- LED Control (LEDC) Espressif documentation
They explain a workaround using PWM functionality. It’s quite simple and only requires a few lines of code. To avoid having a constant tone, I tried a few combinations of loops and changing the frequency for each loop. I ended up with one, that while fairly simple, I am happy with. The code is below and the comments should be enough to see what is going on. The pages above have a lot more detail and more options.
One part that I am not too sure about is connecting this to a speaker. In the linked articles they use a buzzer module that I think is just a piezo module. Using a regular speaker instead by itself is probably a bad idea. It could result in a higher current flow than the ESP32 and maybe the speaker can handle and I’ve also read that the ESP32 doesn’t like to drive inductive loads. let me know if you have a good and simple solution for this. In the WordPress stats counter I used a 130 ohm speaker from a phone with a 330 ohm resistor and 220uF capacitor all in series. It works and the volume is about right but I could be taking a risk. Another option is to drive a small amplifier. I’ve tried an LM386 and that works ok, but I prefer not using an amplifier if there is a simpler solution.
Here is a recording of the sketch below so you can hear the sound of each part.
This sketch will produce the above sounds. I have not found a way to have it not make a sound once the ledcAttach command is used, so when I want the sound to end I detach the pin ledcDetach. There may be a more elegant way to do this.
// Audio alert example using PWM
// Connections: Output connected between pin 26 and ground
#define LEDC_PIN 26 // Define output pin for speaker
#define LEDC_RESOLUTION 10 // Set resolution to 10 bits
void setup() {
}
void loop() {
// 100Hz tone for 1 second
ledcAttach(LEDC_PIN, 50, LEDC_RESOLUTION); // Attach pin before starting tone
ledcWriteTone(LEDC_PIN, 100);
delay(1000);
ledcDetach(LEDC_PIN); // Detatch pin to stop the tone
delay(2000); // Delay before starting the next example
// 1000Hz tone for 1 second
ledcAttach(LEDC_PIN, 50, LEDC_RESOLUTION); // Attach pin before starting tone
ledcWriteTone(LEDC_PIN, 1000);
delay(1000);
ledcDetach(LEDC_PIN); // Detatch pin to stop the tone
delay(2000); // Delay before starting the next example
// Short rising tone (100 to 2000Hz for 200ms)
ledcAttach(LEDC_PIN, 50, LEDC_RESOLUTION); // Attach pin before starting tone
for (int i=1; i<20; i++) {
ledcWriteTone(LEDC_PIN, i * 100);
delay(10);
}
ledcDetach(LEDC_PIN); // Detatch pin to stop the tone
delay(2000); // Delay before starting the next example
// Sound alert - Short rise and fall sound effect
ledcAttach(LEDC_PIN, 50, LEDC_RESOLUTION); // Attach pin before starting tone
for (int i=1; i<20; i++) {
ledcWriteTone(LEDC_PIN, i * 100);
delay(20);
}
for (int i=10; i>0; i--) {
ledcWriteTone(LEDC_PIN, i * 100);
delay(10);
}
ledcDetach(LEDC_PIN); // Detatch pin to stop the tone
delay(2000);
// Slow fall (500 to 50 Hz 1800 ms)
ledcAttach(LEDC_PIN, 50, LEDC_RESOLUTION); // Attach pin before starting tone
for (int i=500; i>50; i--) {
ledcWriteTone(LEDC_PIN, i);
delay(4);
}
ledcDetach(LEDC_PIN); // Detatch pin to stop the tone
delay(2000); // Delay before starting loop again
}