Introduction to the ESP32 

Lesson 11

PWM output experiment using an LED

In the previous lesson you learned how to get the LED connected an ESP32 to blink. In this lecture, I'll show you how to make it fade using the PWM capability of the ESP32.

If you are not familiar with PWM, please checkout Arduino Step by Step Getting Started.

The Arduino, of course, can also output PWM. But as you'll see, the ESP32 has got several additional capabilities in its hardware that the Arduino Uno with the Atmega328 cannot even imagine.

You can watch the video, or, if you are the "reading" type, you can read the text below.

The wiring

For this experiment you can reuse the circuit from the previous lesson.

The circuit contains a single LED connected in series to a 230 Ω resistor. You can use any resistor value between 230 Ω and 500 Ω, and the LED will be bright.

Connected the anode of the LED to GPIO32 on the ESP32, and the cathode to the blue rail on the breadboard, which is connected to one of the GND pins of the ESP32 dev kit.

You can see the schematic below.

An LED is controlled by GPIO32, and is protected by a 230 Ω resistor connected to GND1.

I have connected my LED to GPIO32, though I could have used any of the other pins (with some exceptions that I'm going to discuss about later).

GPIO32, as most other GPIOs, are PWM-capable.

The sketch

Here, I think it is useful to compare the Arduino version of the fading LED sketch, with the ESP version. I have them side-by-side below:

Arduino version
int led = 9;           // the PWM pin the LED is attached to
int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by

// the setup routine runs once when you press reset:
void setup() {
  // declare pin 9 to be an output:
  pinMode(led, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
  // set the brightness of pin 9:
  analogWrite(led, brightness);

  // change the brightness for next time through the loop:
  brightness = brightness + fadeAmount;

  // reverse the direction of the fading at the ends of the fade:
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  }
  // wait for 30 milliseconds to see the dimming effect
  delay(30);
}
ESP32 version
const byte led_gpio = 32; // the PWM pin the LED is attached to
int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by

// the setup routine runs once when you press reset:
void setup() {
  ledcAttachPin(led_gpio, 0); // assign a led pins to a channel

  // Initialize channels
  // channels 0-15, resolution 1-16 bits, freq limits depend on resolution
  // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
  ledcSetup(0, 4000, 8); // 12 kHz PWM, 8-bit resolution
}

// the loop routine runs over and over again forever:
void loop() {
  ledcWrite(0, brightness); // set the brightness of the LED

  // change the brightness for next time through the loop:
  brightness = brightness + fadeAmount;

  // reverse the direction of the fading at the ends of the fade:
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  }
  // wait for 30 milliseconds to see the dimming effect
  delay(30);
}

In the Arduino, we use the analogWrite() function to "write" a value between 0 and 254 to the LED pin. Larger values will cause the LED to stay on for longer during a cycle, and thus the LED will be brighter. 

The analogWrite() function accepts two parameters:

  1. The number of the pin to which the LED is connected.
  2. The PWM value from 0 to 254.

Inside the loop function, each time it is called, the brightness value is increased or decreased by 5, depending on whether we are increasing or decreasing the LED brightness. 

Simple.

The ESP32 does not support the analogWrite() function. But is does support a much better one, the ledcWrite() function.

The ledcWrite() is very similar to analogWrite(). It also requires two parameters:

  1. The PWM channel that we want to "write" a value to.
  2. The PWM value we want to write to the selected channel.

This simple function shows off the power of the ESP32. Both the channel and the PWM values are configurable.

The PWM channel is not the same as a physical pin. The ESP32 contains 16 independent channels. Each of those can be assigned to any PWM-capable pin. This means that you can use any 16 GPIOs to generate PWM output.

The resolution of the PWM signal is also configurable. While in the Arduino, an PWM signal is set to 8 bit, in the ESP32 it can be whatever you choose, from 1 to 16 bits.

To assign a PWM channel to a pin, and configure the resolution of the signal, the ESP32-Arduino Core software provides two functions:

  • ledcAttachPin(gpio, channel)
  • ledcSetup(channel, frequency, resolution)

In ledcAttachPin(), pass the GPIO number and the channel number that you want to bind. In the example sketch, you can see:

ledcAttachPin(led_gpio, 0);

This binds PWM channel "0" to GPIO32.

We use ledcSetup(channel, frequency, resolution) to configure the PWM signal. In the example sketch, you can see:

ledcSetup(0, 4000, 8)

The first parameter is "0". This means that we are configuring PWM channel "0".

The second parameter is 4000, which means that we have chosen the PWM frequency to be 4KHz. The frequency range depends on the resolution you have chosen, but typical values for an 8-bit resolution are from 4KHz to 8KHz. If you are curious about the details, read this.

The third parameter is "8", meaning 8 bits. You can set the resolution to any value between 1 and 16 bits.

Use ledcAttachPin() and ledcSetup() function in setup() to... setup the PWM channel 0, and then you are ready to start creating PWM output.

Inside the ESP32 version of the sketch, have a look at the loop(). Apart from the substitution of analogWrite() with ledcWrite() , the code should look very familiar.

PWM signal visualization with the oscilloscope


The ESP32 PWM output is very configurable, and this help in creating very precise output. I used my oscilloscope to see the PWM signal the was generated by my ESP32. It looks like this:

My oscilloscope visualises the ESP32 8-bit, 4KHz PWM output.

Please watch the video at the top of this article for the details of my experimentation with my ESP32 and the oscilloscope.

This concludes this introductory series on the ESP32. There's a lot more to learn. 

If you are keen to make the most of your ESP32, consider enrolling to my full video course, "ESP32 For Busy People". Unless you have time to waste 🙂

"Introduction to the ESP32" series

Ready for some serious learning?

Enrol to 

ESP32 for Busy People.

This is our comprehensive ESP32 course for Arduino Makers.


It's packed with high-quality video, mini-projects, and everything you need to learn Arduino from the ground up.


Just click on the big red button to learn more.