MicroPython Programming Tutorial: Getting Started with the ESP32 Thing

Contributors: Shawn Hymel
Favorited Favorite 8

Experiment 2: Pulse Width Modulation (PWM)

Pulse width modulation is the technique of switching a digital signal on and off very quickly to control a variety of electrical components. By varying the amount of time a signal is on vs. off, you can vary the amount of electrical power provided to a component. We can use this, for example, to dim an LED or control the speed of a motor. To learn more about PWM, see this tutorial.

In this example, we are going to create a simple LED animation that occurs as long as a button is held down.

Hardware Connections

Connect an LED and a button to the ESP32 as per the following diagram:

ESP32 circuit with LED and button Fritzing diagram

⚡ Note: It matters how you plug in your LED! Current can only flow in one direction through an LED, so pay careful attention to the leads. The short lead on the LED should be connected on the same row as the 330Ω resistor.

LED polarity diagram

Note: Buttons can be a little weird, if it's the first time you've used them. The pins across from each other are always connected, whereas the pins on the same side are only connected when you push the button.

How a push button is configured on the inside

Note: If you are using the full-size breadboard, the power rails are divided in the middle. This means that to get power to the whole power row, you will need to connect the two halves. See the picture below to see how to use jumper wires to connect the halves of the power rows.

Connect power rows on a full-size breadboard

Code: LED Animation

In a new file, enter the following code:

import machine
import sys
import utime

# Pin definitions
repl_button = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
repl_led = machine.Pin(5, machine.Pin.OUT)
button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
pwm_pin = machine.Pin(27, machine.Pin.OUT)

# Create a PWM object out of our pin object
pwm = machine.PWM(pwm_pin)

# Slowly fade LED brightness
while True:

    # If button 0 is pressed, turn on LED and drop to REPL
    if repl_button.value() == 0:
        print("Dropping to REPL")

    # Increase brightness of LED if button is held
    for i in range(1024):
        if button.value() == 0:

Save the code with a name such as pwm.py. Open a command terminal on your host computer and navigate to the directory where you have pwm.py stored. Push button 0 on your ESP32 to ensure that it is in REPL mode, and enter the following commands into your terminal (don't forget to change <PORT> to your individual port number/location):

cp pwm.py main.py
ampy --port <PORT> put main.py

After the code has been uploaded, hold down the button (the one on the breadboard that is connected to pin 14). You should see the LED slowly brighten, turn off, and then begin the brightening animation over again as long as the button is held down.

Using PWM to dim an LED attached to the ESP32 Thing

Code to Note

Much of the code should look familiar from the previous experiment, as we are creating a machine.Pin object for our LED and another one for our button. Note that we are keeping the "drop to REPL" snippet in, as we always want a way to exit out of our code's main loop.

The main difference is that we are creating machine.PWM object out of our machine.Pin object (the pin object that we've created with the pwm_pin variable). With the machine.PWM object, we can call the pwm.duty() method, which allows us to control the amount of on vs. off time on pin 27. A duty cycle of 0 means that the pin is always 0 V. A duty cycle of 511 (about half of 1023) means that pin 27 should rapidly switch between 0 V and 3.3 V equally (50%). Finally, a duty cycle of 1023 means that pin 27 should be always on (always at 3.3 V). Again, refer to the Pulse Width Modulation tutorial for more information on pwm and duty cycles.

By placing pwm.duty() inside a for loop, we can increase the brightness one stage at a time with a 2 ms delay between each stage. This ultimately gives the effect of a slowly increasing LED brightness.

Notice that we check for button 0 at the beginning of the for loop. That means that you need to hold down button 0 until the for loop (LED animation) has completed one iteration before the program realizes you've pressed the button (and should therefore exit to REPL). To help you know when you have successfully exited the program into REPL, we added repl_led.value(1) to turn on the onboard blue LED.