PWM Tutorial for Arduino, ESP8266 and ESP32
In this tutorial we focus on Pulse Width Modulation (PWM) for the Arduino, ESP8266 and ESP32 microcontroller.
PWM is an important part to control different devices with your microcontroller.
For example you can control the brightness of an LED or the speed of a motor by changing the PWM.
Table of Contents
How does PWM work?
The digital inputs / outputs on your microcontroller have a constant voltage of 3.3V (for ESP8266 and ESP32 boards) or 5V (for Arduino boards). But in some cases you want to control the voltage to a specific value between 0V and the maximum voltage.
In case of PWM, a signal is pulsing between HIGH (3.3V or 5V) and LOW (0V). How often the signal is changing between HIGH and LOW is defined by the PWM frequency. The PWM frequency on Arduino pins are 976 cycles per seconds (Herz), for the ESP8266 up to 1 kHz and for the ESP32 up to 40 MHz.
To generate a PWM signal you use the function analogWrite(pin, value). This function create a square wave PWM signal. You can control the shape of the PWM signal with the duty cycle of (value/255). A 0% to 100% duty cycle corresponds to a value between 0 and 255.
The following table shows broadly the relation between the duty cycle and the average output voltage if the maximum voltage is 5V for Arduino microcontroller and 3.3V for ESP8266 and ESP32 microcontroller.
Duty Cycle | Output voltage (Arduino) | Output voltage (ESP8266 and ESP32) | analogWrite(X) |
0% | 0V | 0V | 0 = 0%*255 |
0.25% | 1.25V | 0.825V | 63.75 = 25%*191.25 |
0.5% | 2.5V | 1.65V | 127.5 = 50%*255 |
0.75% | 3.75V | 2.475V | 191.25 = 75%*255 |
100% | 5V | 3.3V | 255 = 100%*255 |
I used my oscilloscope to measure the duty cycles of the table above for the Arduino output voltage. Therefore I connected digital pin 11 of the Arduino Uno to my PicoScope and set up a measurement for the duty cycle. You can do the exactly same measurement for the ESP8266 or the ESP32
The code for the quick measurement is the following. For each measurement I changed the value for the analogWrite function.
Duty Cycle 0%
Note that the 0% duty cycle is measured as 100% because there is no square wave at 0% and also at 100%. Therefor be careful how the duty cycle is measured. In my case I have to see if the voltage is 0 at 100% duty cycle.
Duty Cycle 25%
Duty Cycle 50%
Duty Cycle 75%
Duty Cycle 100%
Create PWM Examples
The following table gives you an overview of all components and parts that I used for this tutorial. I get commissions for purchases made through links in this table.
Arduino Uno | Amazon | Banggood | AliExpress | |
OR | ESP8266 NodeMCU | Amazon | Banggood | AliExpress |
OR | ESP32 NodeMCU | Amazon | Banggood | AliExpress |
AND | Resistor and LED in Package | Amazon | Banggood | AliExpress |
AND | DC Motor | Amazon | Banggood | AliExpress |
AND | L298N Motor Driver Module | Amazon | Banggood | AliExpress |
Change the brightness of an LED by PWM
In the following example we want to change the brightness of an LED by changing the duty cycle of the regarding PWM signal.
The following fritzing sketches show the circuit done with different Arduino, ESP8266 and ESP32 microcontroller boards. We have to make sure that the LED is connected to a microcontroller pin that is PWM able.
I recommend to get the Microcontroller Datasheet eBook where you can find the pinouts of the different microcontroller boards, that include the information which pins are able to use PWM. You get the eBook for free if you join the DIYI0T newsletter.
The program code is pretty straight forward. We have to define the pins and some variables for the time and the height to increment the brightness of the LED. If you want to know how to change the color for multicolor LEDs, using different PWM signals, you find this in my LED tutorial.
int LEDpin = 11; // for Arduino microcontroller
//int LEDpin = D4; // for ESP8266 microcontroller
//int LEDpin = 4; // for ESP32 microcontroller
int bright = 0; // initial value of LED brightness
int incremt = 5; // incremental change in PWM frequency
int time = 100; // time period the PWM frequency is changing
void setup()
{
pinMode(LEDpin, OUTPUT); // define the LEDpin as output pin
}
void loop()
{
analogWrite(LEDpin, bright); // set LED brightness as PWM signal
delay(time); // wait for a time period
bright = bright + incremt; // increment LED brightness
// if the brightness is out of range, reduce brightness
if (bright <=0 || bright >=255) incremt = - incremt;
}
In the first part of the Arduino code, we define the pin that connects the LED to the microcontroller. Because this script is for Arduino, ESP32 and ESP8266 microcontrollers, you have to comment out two of the first three lines that defines the pin.
Also we have to define three additional variables:
- bright: initial value of the LED brightness and therefore 0 to shut down the LED.
- increment: the incremental change in the PWM frequency. Each increment the LED increases and decreases the brightness.
- time: the time period in milliseconds for each PWM cycle.
In the setup function, we fine the pin, that we defines as LED pin at the beginning of the script, as output pin to use the pin with PWM.
We start the loop function, with the analogWrite(pin, value) function we set the analog value (PWM wave) for the brightness to the LED pin. After the delay of 0.1 seconds, the brightness in incremented. If the brightness reaches the minimum (0) or maximum (255) value, the increment is changed from a positive value to a negative value.
The following video shows the change of the brightness of the LED due to the PWM function.
Control the speed of an DC motor by PWM
The second example shows how we can change the speed of an DC motor with the help of a PWM signal. We use the L298N motor driver module to connect the Arduino, ESP8266 or ESP32 to the DC motor. Also it is recommend to power the DC motor with an external power supply. Therefore I use 4 AA batteries, each with 1.5V so in total 6V.
I want to focus on the PWM signal in this example. Therefor I do not further describe the DC motor example. But if you are interested in DC motors, I will write an extra article about DC motors, that explains everything in detail.
For the program code we want to increase the motor speed each second. At the start, the voltage provided through the PWM signal is to low to start the motor. This is why it takes some intervals in the for loop until the motor is turning.
// Arduino connection to L298N
int enA = 10; // PWM for Motor A
int in1 = 9; // Control Rotation of Motor A
int in2 = 8; // Control Rotation of Motor A
// ESP8266 connection to L298N
//int enA = D4; // PWM for Motor A
//int in1 = D5; // Control Rotation of Motor A
//int in2 = D6; // Control Rotation of Motor A
// ESP32 connection to L298N
//int enA = 4; // PWM for Motor A
//int in1 = 0; // Control Rotation of Motor A
//int in2 = 2; // Control Rotation of Motor A
int motor_speed = 0;
void setup()
{
// set all the motor control pins to outputs
pinMode(enA, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
}
void loop()
{
// run the motor between 0 and 250 in increments of 10
digitalWrite(in1, LOW); // Input1 LOW = move forward
digitalWrite(in2, HIGH); // Input2 HIGH = move forward
for(motor_speed = 0; motor_speed < 250; motor_speed += 10)
{
analogWrite(enA, motor_speed); // PWM output
delay(1000);
}
}
In the first part of the script we define the pins that connect the microcontroller to the L298N module, that is connected to the DC motor. In total we have to define 3 pins:
- enA: transfers the PWM signal to the L298N module and must be a pin that is able to produce a PWM signal.
- int1 and int2: control the rotation direction of the motor. The following table shows how to control int1 and int2 to stop the motor, move forward and move backwards.
int1 | |||
LOW | HIGH | ||
int2 | LOW | Motor stops | Motor moves backwards |
HIGH | Motor moves forward | Motor stops |
Because this script is for Arduino, EPS8266 and ESP32 microcontroller boards, you have to select only the lines for your microcontrolle and comment the other lines. The current script is commented in a way, that is for Arduino boards.
To increase the motor speed, the speed has to be saved to a variable that we call motor_speed and has an initial value of 0.
In the setup function we define all digital pins, that connects the board to the L298N as outputs.
We start the loop function by setting int1 LOW and int2 HIGH. Therefore the DC motor rotates in the forward direction. In the for loop we increase the motor speed each second by 10 between 0 and 250. The motor speed in written as analog output (PWM signal) to the enA connection.
The following video shows the Arduino script in action for the Arduino Uno as example.
Conclusion
I hope you enjoined this article about the PWM signal. The PWM signal is a very handy tool which is used a lot in practical examples. It is recommended to know how PWM is working. Therefore if you have any further questions, use the comment section below to ask.
Leave A Comment