The Problem: Digital Pins Are All or Nothing
A digital output pin on Arduino has exactly two states: 5V (HIGH) or 0V (LOW). On or off. There is no middle ground — no 2.5V, no 3V. It is a binary world.
But the real world is not binary. A motor should run at 50% speed for a careful turn, not full throttle. An LED should dim gracefully, not just blink. A servo motor needs to hold at exactly 90 degrees. How do you get variable output from a pin that only knows on and off?
Your ceiling fan has 5 speed settings. But the wall socket only gives constant 230V AC — it does not change voltage. The fan regulator works by chopping the power on and off very rapidly. At speed 1, it is mostly off. At speed 5, it is mostly on. Your eyes and the fan's motor cannot react fast enough to see the switching — they see only the average. That is exactly how PWM works.
What Is PWM?
PWM stands for Pulse Width Modulation. The key word is modulation — you are changing (modulating) the width of the ON pulse within each cycle to control how much power is effectively delivered.
Duty Cycle — The Core Concept
In each PWM cycle (which happens thousands of times per second), the pin is ON for some portion of the time and OFF for the rest. The percentage of time it is ON is called the duty cycle.
- 0% duty cycle — always OFF (0V average) — motor stopped, LED off
- 25% duty cycle — on for 1/4 of each cycle — motor at 25% speed
- 50% duty cycle — on half the time — motor at half speed, LED at half brightness
- 75% duty cycle — on for 3/4 of each cycle — motor at 75%
- 100% duty cycle — always ON (5V) — full speed, full brightness
The switching happens so fast (490–980 Hz on Arduino) that a motor or LED responds to the average voltage, not the individual pulses. The result is smooth, continuous-looking control.
PWM in Arduino Code
Arduino makes PWM simple with one function: analogWrite(pin, value). Despite the name, it does not output a true analog voltage — it outputs a PWM signal. The value goes from 0 (0% duty, always off) to 255 (100% duty, always on).
analogWrite() only works on PWM-capable pins. On Arduino Uno, these are pins 3, 5, 6, 9, 10, and 11 — marked with a tilde (~) symbol printed on the board. Using it on other pins will not work correctly.
// LED with 220 ohm resistor on pin 9 (PWM pin ~)
const int LED_PIN = 9;
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// Fade in from off to full brightness
for (int brightness = 0; brightness <= 255; brightness++) {
analogWrite(LED_PIN, brightness);
delay(8); // 8ms × 256 steps = ~2 seconds to fade in
}
// Fade out from full brightness to off
for (int brightness = 255; brightness >= 0; brightness--) {
analogWrite(LED_PIN, brightness);
delay(8);
}
// Loop repeats — LED fades in and out continuously
}
Controlling Motor Speed
This is where PWM becomes genuinely useful. DC motors draw more current than any microcontroller pin can supply — trying to drive a motor directly from an Arduino pin will likely damage the Arduino. You need a motor driver IC between the Arduino and the motor.
The most common motor driver for beginners is the L298N. It has an ENA pin (Enable A) and ENB pin (Enable B) — connect your PWM signal here to control speed. The IN1/IN2 pins control direction.
// L298N Motor Driver connections:
// ENA → Arduino pin 9 (PWM)
// IN1 → Arduino pin 7
// IN2 → Arduino pin 8
// Motor A terminals → your DC motor
// L298N VCC → 9V or 12V battery, GND to Arduino GND
// Potentiometer middle pin → A0
const int ENA = 9; // PWM speed control
const int IN1 = 7; // Direction control
const int IN2 = 8;
const int POT = A0;
void setup() {
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
Serial.begin(9600);
// Set motor direction: IN1 HIGH, IN2 LOW = forward
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
}
void loop() {
int potValue = analogRead(POT); // 0 to 1023
int speed = map(potValue, 0, 1023, 0, 255); // Map to PWM range
analogWrite(ENA, speed); // Set motor speed via PWM
Serial.print("Speed: ");
Serial.println(speed);
delay(50);
}
Servo Motors and PWM
Servo motors are different from DC motors. Instead of controlling speed, PWM controls their angle. A standard servo rotates between 0° and 180° based on the pulse width it receives: a 1ms pulse → 0°, a 1.5ms pulse → 90°, a 2ms pulse → 180°.
You do not have to calculate pulse widths yourself. The Arduino Servo library handles it with a simple angle value.
#include <Servo.h>
Servo myServo; // Create servo object
void setup() {
myServo.attach(9); // Servo signal wire → pin 9
// Servo power: red → 5V, brown/black → GND
}
void loop() {
// Sweep from 0 to 180 degrees
for (int angle = 0; angle <= 180; angle++) {
myServo.write(angle);
delay(15); // Give servo time to reach position
}
// Sweep back from 180 to 0 degrees
for (int angle = 180; angle >= 0; angle--) {
myServo.write(angle);
delay(15);
}
}
Add a potentiometer to A0. Change the loop to read the pot value and use map(potValue, 0, 1023, 0, 180) to set the servo angle. Turn the knob and watch the servo follow. This is the core of a manually controlled robotic arm joint — the same principle used in much larger industrial systems.
