Arduino PWM and analogWrite() Explained: Complete Guide with Examples
analogWrite() is one of the first Arduino functions most developers learn — and one of the least understood. It doesn’t actually output an analog voltage. What it does instead is far more interesting: it uses PWM (Pulse Width Modulation) to rapidly switch a digital pin ON and OFF, creating the illusion of a varying voltage that LEDs, motors, and other devices respond to.
In this tutorial, you’ll learn exactly how PWM works on Arduino, how analogWrite() generates those signals using hardware timers, how to control LED brightness and motor speed with working code examples, and how to change the default PWM frequency and resolution when the defaults aren’t good enough. We also cover the most common problems — flickering LEDs, timer conflicts, and pins that don’t respond — and how to fix them.
This is part of the Arduino Core Tutorials series. If you’re new to Arduino I/O, start with the following:

What is Arduino PWM and How Does It Work?
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.
How PWM Works: Duty Cycle & Frequency
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.
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.
Arduino analogWrite() Function Explained
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.
Controlling Devices with Arduino PWM
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.
Changing Arduino 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;
}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 PWM 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:
ICR1defines the PWM period (and thus frequency).OCR1Asets 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.
Arduino Libraries for PWM Frequency 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);
}
}Arduino PWM Troubleshooting & Common Issues
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 Timer 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.
Arduino PWM and analogWrite(): FAQs
Arduino Uno uses approximately 490 Hz on most PWM pins (3, 9, 10, 11) and 980 Hz on pins 5 and 6. These frequencies are set by the default timer prescaler values and can be changed by modifying the timer control registers, though doing so may affect delay() and millis() if Timer0 is involved.
Technically yes, but it's not recommended. Calling digitalWrite() on a PWM pin will override the PWM output and set it to a fixed HIGH or LOW state. If you need to switch between PWM and digital control on the same pin, call analogWrite(pin, 0) or analogWrite(pin, 255) first to cleanly stop the PWM output before switching to digitalWrite().
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.
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.
It does not matter for GPIO, but it must be called before mongoose_init(). If Mongoose tries to allocate memory before the heap is initialized, the heap_instance pointer will be NULL and all allocations will fail. Placing the call at the very top of main() before any other initialization is the safest approach.
Conclusion
PWM is one of the most practical tools in your Arduino toolkit. Once you understand that analogWrite() is really just controlling the ON/OFF ratio of a digital pin — and that the hardware timer handles the switching automatically — the behavior becomes completely predictable. You know exactly why a low duty cycle dims an LED, why a high frequency removes motor noise, and why changing Timer0 breaks delay().
From here, the natural next step depends on what you're building. If you need to read analog signals back into the Arduino, analogRead() and ADC covers the other direction of analog I/O. If you want to control the exact moment a PWM action happens — for example, syncing motor changes to a button press without delay-based blocking — external interrupts is the right follow-up. And if you're working with ESP32 instead of Uno, the LEDC-based PWM shown in the library section of this article gives you far more frequency and resolution flexibility than AVR timers allow.
Browse More Arduino Core Tutorials
Arduino Nano Pinout – Complete Guide with Diagram
Arduino digitalWrite() and digitalRead(): Complete Guide with Examples
Arduino UART Tutorial: Serial Communication, Send, Receive & LED Control
Arduino ADC and analogRead() Explained: Complete Guide with Examples
Arduino I2C Tutorial: Wire Library, Master/Slave, Scanner & Troubleshooting
Arun is an embedded systems engineer with 10+ years of experience in STM32, ESP32, and AVR microcontrollers. He created ControllersTech to share practical tutorials on embedded software, HAL drivers, RTOS, and hardware design — grounded in real industrial automation experience.
Recommended Tools
Essential dev tools
Categories
Browse by platform



