Arduino PWM and analogWrite() Explained (Beginner-Friendly Guide)
Controlling the brightness of an LED or the speed of a motor with Arduino is one of the most exciting parts of any beginner project. This is made possible through PWM (Pulse Width Modulation) — a technique that lets Arduino create “analog-like” signals using digital pins.
In this guide, you’ll learn how PWM works in Arduino, how to use the analogWrite()
function, and how to control devices like LEDs and DC motors easily. We’ll also explore how to change PWM frequency, fix common issues, and get smoother control over your projects.
By the end, you’ll understand exactly what happens behind analogWrite()
, how to use it properly, and how to tweak it for better performance.

What is PWM in Arduino?
PWM stands for Pulse Width Modulation, a technique used by Arduino to simulate analog output using digital signals.
Since the Arduino board can’t produce true analog voltage (it only outputs HIGH or LOW), PWM allows it to rapidly switch the digital pin ON and OFF to create an effect similar to varying voltage levels.
This makes it possible to control the brightness of LEDs, adjust motor speed, and even generate audio signals using just digital pins.
Why We Need PWM
Many electronic devices need more than just ON or OFF control.
For example:
- An LED shouldn’t just glow or stay off, you may want to fade it smoothly.
- A motor might need to spin at different speeds, not just full speed or stop.
PWM helps achieve this by controlling how long a signal stays ON versus OFF within a fixed time. The average voltage delivered to the device depends on this ratio, allowing smooth control without requiring an actual analog output.
Understanding How PWM Works
Think of PWM as turning a digital pin ON and OFF very quickly, so quickly that the connected device (like an LED or motor) doesn’t notice the flickering.
If the pin is ON for a longer time and OFF for a shorter time in each cycle, the device receives more power. Whereas, If it’s ON for a shorter time and OFF for longer, it receives less power.
This switching happens thousands of times per second, creating an illusion of a steady analog voltage.
PWM Square Wave (Varying Duty Cycle)
Analog Voltage (0-5V)
In Arduino, the analogWrite()
function handles this switching automatically. You just need to specify a value between 0 and 255, and Arduino adjusts the ON/OFF ratio (duty cycle) for you.
Duty Cycle and Frequency Explained
The duty cycle defines how long the signal stays ON during each PWM cycle.
It’s usually expressed as a percentage:
- 0% duty cycle → Always OFF (0V)
- 50% duty cycle → Half ON, half OFF (approx. 2.5V average on a 5V Arduino)
- 100% duty cycle → Always ON (5V)
The frequency is how often these ON/OFF cycles repeat per second, measured in Hertz (Hz). Arduino’s default PWM frequency is typically 490 Hz for most pins and 980 Hz for a few others (like pins 5 and 6 on the Uno).
Together, duty cycle and frequency determine how smooth or stable the output appears — higher frequency means smoother control, especially for LEDs and motors.
Understanding analogWrite() in Arduino
The analogWrite()
function in Arduino is used to generate PWM signals on digital pins.
Although the name suggests it writes an analog value, it actually outputs a digital square wave with a variable duty cycle. This clever trick allows you to simulate analog voltage without a true DAC (Digital-to-Analog Converter).
With analogWrite()
, you can easily control the brightness of LEDs, speed of motors, and many other devices that respond to variable voltage levels, all using simple code.
How analogWrite() Generates PWM Signals
When you use the function
analogWrite(pin, value);
Arduino turns that value
(ranging from 0 to 255) into a PWM signal:
- 0 → Always OFF (0% duty cycle)
- 127 → Half ON, half OFF (≈50% duty cycle)
- 255 → Always ON (100% duty cycle)
Internally, the Arduino’s timers generate these PWM signals automatically.
Each timer is connected to specific pins, and it rapidly toggles the pin HIGH and LOW according to the duty cycle you set.
You don’t need to configure timers manually for basic tasks, analogWrite()
does all the work for you.
PWM Pins on Different Arduino Boards
Not every pin on the Arduino supports PWM. Each board has a few dedicated pins that can generate PWM signals through hardware timers.
Here’s a quick reference:
Arduino Board | PWM Pins |
---|---|
Uno / Nano / Pro Mini | 3, 5, 6, 9, 10, 11 |
Mega 2560 | 2–13, 44–46 |
Leonardo / Micro | 3, 5, 6, 9, 10, 11, 13 |
Arduino Due | All digital pins (true DAC available on pins A0, A1) |
ESP32 (Arduino Core) | Almost all pins support PWM (via LEDC module) |
On most boards, PWM pins are marked with a “~” symbol next to the pin number.
Default PWM Frequency and Resolution
The PWM frequency defines how fast the pin switches ON and OFF.
By default:
- Arduino Uno, Nano, Mega → ~490 Hz on most pins, ~980 Hz on pins 5 and 6
- Arduino Leonardo / Micro → ~490 Hz for all PWM pins
- Arduino Due / ESP32 → Frequency can be easily customized
The resolution of PWM on most AVR-based Arduinos (like Uno or Nano) is 8-bit, meaning analogWrite()
accepts values from 0–255.
That gives you 256 levels of control between fully OFF and fully ON.
On newer boards (like ESP32 or Due), you can set higher resolutions (up to 12 or even 16 bits) for much smoother control over output signals.
Using analogWrite() to Control Devices
The analogWrite()
function is one of the most practical tools for controlling real-world devices with Arduino.
By adjusting the PWM signal’s duty cycle, you can smoothly control how much power goes to components like LEDs, motors, buzzers, and more.
Let’s look at how it works in two common applications — LED brightness control and motor speed control.
Controlling LED Brightness with PWM
An LED is a great way to visualize how PWM works. When you change the duty cycle of the PWM signal, the LED appears dimmer or brighter:
- A low duty cycle means the LED stays OFF most of the time → dimmer light
- A high duty cycle means it stays ON longer → brighter light
Here’s a simple example:
int ledPin = 9; // PWM pin connected to LED
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
for (int brightness = 0; brightness <= 255; brightness++) {
analogWrite(ledPin, brightness); // Increase brightness
delay(10);
}
for (int brightness = 255; brightness >= 0; brightness--) {
analogWrite(ledPin, brightness); // Decrease brightness
delay(10);
}
}
How the Code works:
- The ledPin (Pin 9) is configured in the output mode. We will connect the LED to this pin.
- Inside the infinite loop, we will vary the PWM Duty cycle from 0 to 255 every 10ms. Once the PWM Duty reaches 255, we will decrease it back to 0, again every 10ms.
The LED fades in and out smoothly as the PWM duty cycle changes from 0 to 255 and back. This is a classic example of using PWM to simulate analog control.
Result of varying LED Brightness with PWM
The gif below shows the simulation for this project on the TinkerCAD simulator.
You can see the LED brightness increases when the duty cycle of the PWM signal increases. And it decreases with the Duty cycle as well.
The scope shows the duty cycle of the PWM signal and you can compare the LED brightness with respect to it.
The voltmeter shows the voltage measured on the Pin 9. You can see it varies from 0V to 5V as the Duty Cycle varies.
Adjusting Motor Speed with analogWrite()
DC motors respond directly to the average voltage they receive. With PWM, you can control that average voltage by adjusting how long the pin stays ON in each cycle.
Here’s how you can control motor speed using analogWrite()
:
int motorPin = 5; // Connect to motor driver input
void setup() {
pinMode(motorPin, OUTPUT);
}
void loop() {
analogWrite(motorPin, 128); // 50% duty cycle → half speed
delay(2000);
analogWrite(motorPin, 255); // 100% duty cycle → full speed
delay(2000);
analogWrite(motorPin, 0); // 0% duty cycle → stop motor
delay(2000);
}
How the Code works:
- The motorPin (Pin 5) is configured in the output mode.
- Inside the infinite loop, we will vary the PWM Duty cycle every 2 seconds. Here I am changing the duty cycle from 50% to 100% and then back to 0.
Result of Adjusting motor speed with PWM
The gif below shows the simulation for this project on the TinkerCAD simulator.
You can see above, the motor RPM varies with PWM duty cycle. The motor is powered with 12V from an external DC supply. You can also see the current consumption when by the motor increases with increase in PWM duty cycle.
Tip:
Always use a motor driver (like L298N or L293D) or a MOSFET between the Arduino and the motor. The Arduino pins can’t directly supply enough current for most DC motors.
Changing PWM Frequency and Resolution
By default, Arduino sets the PWM frequency and resolution automatically based on the board and pin you use. For most projects like LED dimming or small motor control, the default settings work perfectly.
However, for more advanced applications, such as controlling servos, generating audio signals, or reducing flicker, you might need to change the PWM frequency or resolution manually.
Let’s explore how you can do that.
How to Modify PWM Frequency
PWM frequency determines how fast the signal switches ON and OFF per second. On most Arduino boards (like the Uno or Nano), the default frequency is around 490 Hz for most pins and 980 Hz for pins 5 and 6.
If your LED flickers or your motor makes noise, increasing the PWM frequency can help. You can modify it by changing the prescaler of the timer that controls the pin.
Here’s a simple example to increase the frequency on pins 5 and 6 (Timer0) of Arduino Uno:
void setup() {
pinMode(5, OUTPUT);
// Set Timer0 prescaler to 1 (default is 64)
TCCR0B = (TCCR0B & 0b11111000) | 0x01;
}
Note:
Changing timer settings will affect other functions that depend on that timer (like delay()
, millis()
, or tone()
).
Always check which timer your pin uses before modifying it.
If you just need to reduce flicker or change the tone of a motor, minor adjustments to the prescaler are usually enough.
Using Timer Registers for Advanced Control
For advanced users, directly controlling the timer registers gives full flexibility over PWM. Each Arduino timer (Timer0, Timer1, Timer2, etc.) has a set of control registers that define:
- PWM mode (Fast or Phase-Correct)
- Prescaler value (controls frequency)
- Compare value (controls duty cycle)
- Output pin mapping
Example: configuring Timer1 (16-bit timer) for custom PWM on pin 9:
void setup() {
pinMode(9, OUTPUT);
// Set Fast PWM mode using ICR1 as TOP
TCCR1A = (1 << COM1A1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
ICR1 = 40000; // Sets frequency
OCR1A = 20000; // Sets duty cycle (50%)
}
Explanation:
ICR1
defines the PWM period (and thus frequency).OCR1A
sets how long the signal stays HIGH (duty cycle).
This level of control lets you fine-tune the output for precision applications like servos, signal generation, or dimmable power control.
Library Options for Easier PWM Control
If manually tweaking timer registers feels too complex, don’t worry — there are Arduino libraries that simplify the process. They allow you to set custom frequency and resolution easily, without dealing with low-level timer code.
Here are a few popular ones:
- PWM Library:
Works on many Arduino boards, lets you set custom frequencies for each pin. - TimerOne Library:
Provides easy control over the 16-bit Timer1 for custom PWM output.
Example using ESP32’s LEDC library:
int ledPin = 5;
int channel = 0;
int freq = 5000;
int resolution = 8;
void setup() {
ledcSetup(channel, freq, resolution);
ledcAttachPin(ledPin, channel);
}
void loop() {
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(channel, duty);
delay(10);
}
}
Why use libraries?
They’re safer, easier, and compatible across boards, perfect if you don’t want to mess with hardware registers.
Common Issues and Troubleshooting
When working with PWM and analogWrite()
, you might sometimes face problems like flickering LEDs, unstable motor speeds, or pins not responding at all.
Most of these issues are easy to fix once you understand what’s happening behind the scenes.
Let’s go through some common problems and their solutions.
analogWrite() Not Working on Certain Pins
If analogWrite()
doesn’t seem to work on a specific pin, the most likely reason is that the pin doesn’t support PWM output. Only certain pins on each Arduino board are connected to hardware timers capable of generating PWM signals.
Fix:
- Double-check your board’s PWM-capable pins (look for the
~
symbol on the board or in the pinout diagram). - Move your connection to a valid PWM pin.
- On boards like the ESP32, you can configure almost any pin for PWM using the
ledcAttachPin()
function.
If you still don’t see output, ensure the pin isn’t being used by another library or peripheral that overrides the timer.
Flickering LEDs or Unstable Motor Speed
If your LED flickers or your motor buzzes, it’s usually caused by the PWM frequency being too low. At low frequencies (like 490 Hz), the rapid ON/OFF switching can become visible or audible.
Fix:
- Increase PWM frequency by changing the timer prescaler or using a library that supports higher frequency.
- For LEDs, use frequencies above 1 kHz to remove visible flicker.
- For DC motors, frequencies between 5 kHz and 20 kHz give smoother motion and quieter operation.
On ESP32, for example, you can set the frequency up to 40 kHz easily with the LEDC API.
Fixing PWM Conflicts with Other Functions
Some timers are shared between different Arduino functions. For example:
- Timer0 is used for
millis()
,delay()
, andmicros()
. - Timer1 and Timer2 might be used by libraries like Servo, tone(), or motor drivers.
If you modify timer settings manually, you may notice timing functions or other features stop working correctly.
Fix:
- Avoid changing the settings of Timer0 unless absolutely necessary.
- Use Timer1 or Timer2 for custom PWM instead.
- If a library (like Servo or tone) is already using a timer, use another available timer to prevent conflicts.
When using multiple PWM outputs, it’s a good idea to plan which pins and timers your code or libraries will use in advance.
Other Common Issues
- Low output voltage: Check your wiring and ensure the ground (GND) is common between Arduino and external components.
- Noisy motors: Add a diode across the motor terminals and a capacitor between the motor leads to reduce noise.
- Inconsistent results: Avoid using the same pin for both digital I/O and PWM in different parts of the code.
Conclusion
In this tutorial, we explored everything you need to know about PWM (Pulse Width Modulation) and the analogWrite()
function in Arduino. You learned how PWM works, why it’s used to simulate analog signals, and how analogWrite()
generates those signals automatically. We also covered how to control LED brightness, adjust motor speed, change PWM frequency and resolution, and troubleshoot common issues related to PWM output.
Understanding PWM is one of the most valuable skills for any Arduino user. It gives you precise control over power delivery, making your projects smoother, more responsive, and energy-efficient. Whether you’re dimming LEDs, running motors, or experimenting with advanced signal control, mastering PWM with analogWrite()
opens the door to more creative and professional Arduino applications.
Browse More Arduino Tutorials
Arduino UART Tutorial: Step-by-Step Guide to Serial Communication
Arduino ADC and analogRead() Explained – Read Analog Voltage Easily
Complete Arduino I2C Tutorial with Examples: Wire Library & Projects
Mastering Arduino’s delayMicroseconds() Function: A Practical Guide
Arduino PWM FAQs
No — analogWrite()
doesn’t produce a true analog voltage. It creates a PWM (Pulse Width Modulated) signal that switches the pin ON and OFF very quickly. The average voltage of this signal acts like an analog output for most devices such as LEDs and motors.
LED flicker happens when the PWM frequency is too low (around 490 Hz on most boards).
To fix it, increase the PWM frequency by adjusting the timer prescaler or by using a PWM control library.
Frequencies above 1 kHz are generally flicker-free for the human eye.
No. Only specific pins support PWM output.
Look for the “~” symbol next to the pin numbers on your Arduino board (e.g., 3, 5, 6, 9, 10, 11 on Uno).
On ESP32, however, almost any GPIO pin can be used for PWM with the ledcAttachPin()
function.
The duty cycle controls how long the signal stays ON during each PWM cycle — it affects brightness or speed.
The frequency defines how many ON/OFF cycles occur per second — it affects how smooth or stable the signal appears.
Avoid changing settings on Timer0, because it controls timing functions like delay()
and millis()
.
Instead, modify Timer1 or Timer2, or use a dedicated library (like TimerOne or PWM Library) that manages timers safely.
Search This Site
Subscribe
