Skip to content
Tutorial
6 min read
October 17, 2026

Mastering PWM on Arduino: Dimming LEDs and Controlling Motors

Understand what PWM actually is on Arduino, dim an LED smoothly, and safely control a DC motor's speed with a driver chip -- plus the mistakes (like wiring a motor straight to a pin) that damage boards.

Mastering PWM on Arduino: Dimming LEDs and Controlling Motors

Arduino doesn't have a true analog output on most of its pins -- there's no built-in circuit that produces a genuinely variable voltage between 0V and 5V. What it has instead is PWM (Pulse Width Modulation): a digital pin switching between fully on and fully off so fast that an LED, a motor, or your eye can't tell the difference from a real analog voltage. Understanding why that trick works, and where it breaks down, is most of what you need to dim an LED smoothly or control a motor's speed reliably.

Image pending: Oscilloscope-style waveform showing PWM duty cycle at 25%, 50%, and 75%

What PWM Actually Is

A PWM signal is a square wave with a fixed frequency but a variable duty cycle -- the percentage of each cycle spent HIGH versus LOW. A 25% duty cycle spends a quarter of each cycle at 5V and the rest at 0V; a 75% duty cycle flips that ratio. Average that out over time and a 75% duty cycle behaves like 3.75V to an LED or a filter capacitor, even though the pin is only ever fully on or fully off.

On a classic Arduino Uno/Nano (ATmega328P), six pins support hardware PWM, marked with a ~ on the silkscreen next to the pin number:

PinTimerDefault PWM Frequency
3, 11Timer2~490 Hz
5, 6Timer0~980 Hz
9, 10Timer1~490 Hz

Pins 5 and 6 run at roughly double the frequency of the others because Timer0 is also what millis() and delay() are built on, and its configuration is tuned as a compromise between PWM smoothness and timekeeping accuracy. analogWrite(pin, value) takes a duty cycle from 0 (always off) to 255 (always on) and only works on these six pins -- calling it on a non-PWM pin silently does nothing useful.

Dimming an LED

This is the simplest real use of PWM, and it's a good way to see the effect directly rather than just reading about it.

const int LED_PIN = 9;  // must be a ~PWM pin

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  // Fade up
  for (int b = 0; b <= 255; b++) {
    analogWrite(LED_PIN, b);
    delay(8);
  }
  // Fade down
  for (int b = 255; b >= 0; b--) {
    analogWrite(LED_PIN, b);
    delay(8);
  }
}

At low frequencies (or a slow enough eye), you'd see individual flashes. At ~490-980 Hz, the flicker is well above what a human eye perceives, so what you actually see is a smooth brightness ramp. A camera sensor, notably, can still pick up flicker at these frequencies in video -- which is why LED PWM frequency matters more for camera-facing projects than for direct viewing.

Image pending: Breadboard with an LED and current-limiting resistor wired to Arduino pin 9

Controlling a DC Motor with PWM

This is where most beginners hit a wall, because the instinct is to wire a motor directly to a PWM pin the same way as an LED. Don't -- an Arduino pin can supply roughly 20-40 mA safely, while even a small DC motor can draw several hundred mA at startup, and a stalled motor draws far more. Wiring one directly to a GPIO pin will, at best, brown out the board, and at worst destroy the pin outright.

The fix is a motor driver sitting between the Arduino and the motor -- it uses the low-current PWM signal to switch a separate, higher-current supply on and off:

DriverMotorsNotes
Single transistor/MOSFET1 (one direction only)Cheapest option; needs a flyback diode across the motor
L293D2Common DIP chip, socket-friendly, moderate current limit (~600mA/channel)
L298N module2Higher current capacity, includes onboard heatsink, very common on breakout boards
// L298N: IN1/IN2 set direction, ENA takes the PWM speed signal
const int ENA = 9;
const int IN1 = 7;
const int IN2 = 8;

void setup() {
  pinMode(ENA, OUTPUT);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);  // spins one direction
}

void loop() {
  for (int speed = 0; speed <= 255; speed += 5) {
    analogWrite(ENA, speed);
    delay(50);
  }
}

Notice the PWM pin (ENA) only ever sets speed -- direction comes from the two plain digital pins, which tell the driver's internal H-bridge which way to route current through the motor.

Image pending: L298N motor driver module wired between an Arduino and a small DC motor

Changing the PWM Frequency

The default frequencies work fine for LEDs and most small motors, but a few projects genuinely need a different one -- a common example is quiet motor control, since some default PWM frequencies fall inside the audible range and produce an audible whine from the motor's windings. Raising the frequency above roughly 20 kHz (out of human hearing range) eliminates that whine.

Changing it means writing directly to the timer's prescaler register, which requires care:

// Changes the PWM frequency on pins 9 and 10 (Timer1)
TCCR1B = (TCCR1B & 0b11111000) | 0x01;  // sets prescaler to 1 -> ~31kHz PWM

This is genuinely advanced territory -- touching Timer0's registers (pins 5 and 6) will break millis() and delay() project-wide, since they share that timer. Stick to Timer1 (pins 9, 10) or Timer2 (pins 3, 11) for frequency changes unless you're prepared to also rebuild your own timekeeping.

Common Mistakes

Wiring a motor directly to a GPIO pin. The single most common way to damage a board on a first motor project. Always use a driver.

Using analogWrite() on a non-~ pin. It compiles without error and simply does nothing useful -- always double-check the pin has the tilde marking before wiring up a PWM project.

Forgetting a flyback diode on inductive loads. Motors and relays generate a voltage spike when switched off as their magnetic field collapses, which can damage a driver transistor over time. A diode wired backwards across the load absorbs that spike; most motor driver modules include this already, but a bare transistor circuit needs one added manually.

Confusing duty cycle with frequency. analogWrite() only changes duty cycle; the frequency stays fixed unless you touch timer registers directly. Slowing a fade by increasing delay() inside the loop doesn't change the PWM frequency itself.

Editing Timer0 registers without realizing it powers millis(). A frequency change on pins 5 or 6 that seems to work will often quietly break every timing-dependent library in the sketch.

Where to Go Next

PWM is one of the first tools that turns a blinking-LED project into something that feels physical -- a fan that spins up gradually, a lamp that fades instead of snapping on. From here, Getting Started with Arduino Uno R3 covers the board fundamentals this guide assumes, and Arduino Uno Power Options is worth reading before adding a motor, since motors are exactly the kind of load that exposes a marginal power supply.

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.