Pulse Width Modulation (PWM) is a digital technique that simulates analog control by rapidly switching power on and off at a specific duty cycle. It enables precise control of motor speed, LED brightness, and servo position using simple digital microcontroller pins without analog hardware.
Key Concepts
PWM (Pulse Width Modulation): A technique for controlling average power by rapidly switching a signal between HIGH and LOW; duty cycle = (time HIGH) / (period) x 100%; a 50% duty cycle delivers 50% of the supply voltage as an average
Duty Cycle: The percentage of one PWM period during which the signal is HIGH; 0% = always off, 100% = always on; controls LED brightness, motor speed, heater temperature, and servo position (within a specific pulse width range)
PWM Frequency: The number of complete PWM cycles per second; motor control: use 20 kHz+ to avoid audible noise; servo control: 50 Hz standard (20 ms period); LED dimming: any frequency above 200 Hz to prevent visible flicker
PWM Resolution: The number of discrete duty cycle steps; ESP32 LEDC peripheral: 1-16 bit configurable; Arduino Uno: 8-bit (256 steps); higher resolution provides finer control but requires more timer bits
analogWrite() / ledcWrite(): Arduino function (analogWrite) and ESP32-specific function (ledcWrite) for setting PWM duty cycle; Arduino Uno/Nano output 8-bit 490 Hz PWM on most pins; ESP32 uses the LEDC peripheral requiring channel configuration before use
Servo Pulse Width: Servo motors use PWM with a 20 ms period (50 Hz); pulse width controls position: 1 ms = 0 degrees (minimum), 1.5 ms = 90 degrees (center), 2 ms = 180 degrees (maximum); use the Servo.h library which handles this automatically
Dead Time: A brief delay inserted between turning one switch off and its complementary switch on in an H-bridge or inverter; prevents shoot-through (both high and low side switches on simultaneously), which would create a short circuit across the power supply
Soft Start / Ramp Control: Gradually increasing PWM duty cycle from 0 to operating value at startup rather than switching immediately to full power; reduces inrush current, mechanical shock, and EMI during motor startup
Learning Objectives
After completing this chapter, you will be able to:
Calculate PWM duty cycles for various actuator applications
Configure ESP32 PWM channels for different frequencies and resolutions
Use the interactive PWM calculator for design decisions
Analyze PWM timing parameters for servo position control
Optimize PWM frequency for different actuator types
For Beginners: PWM Control
Imagine flicking a light switch on and off very rapidly – so fast that the light appears to be dimmed rather than blinking. That is essentially what PWM (Pulse Width Modulation) does. By controlling how long the power stays on versus off in each cycle, you can smoothly adjust a motor’s speed or an LED’s brightness using just a simple digital on/off signal.
How It Works: PWM Creates “Fake” Analog Voltage
Imagine you’re pushing a child on a swing. You can’t push continuously (analog), but you can push in pulses (digital). The faster and longer you push each time, the higher the swing goes.
PWM works the same way:
Microcontroller limitation: GPIO pins output only 0V (OFF) or 3.3V/5V (ON) - nothing in between
The PWM trick: Switch ON and OFF thousands of times per second
Motor/LED response: Physical inertia smooths the pulses - the device “sees” the average voltage
Example: 50% duty cycle = ON for 0.5ms, OFF for 0.5ms, repeat at 1kHz = motor sees ~2.5V average from a 5V supply
The key insight: By varying the ON time (duty cycle), you create any “analog” voltage from 0% (always OFF) to 100% (always ON).
8.1 PWM Fundamentals
PWM (Pulse Width Modulation) is a digital technique that simulates analog control by rapidly switching power ON and OFF:
Duty cycle = Percentage of time the signal is HIGH (ON)
Frequency = How many times per second the signal repeats
Average power = Supply voltage x Duty cycle percentage
Why use PWM? Digital microcontrollers can’t produce true analog voltages (like 2.5V from a 5V supply). Instead, they rapidly switch between 0V and 5V. A motor or LED responds to the average voltage over time.
Putting Numbers to It
At 5kHz PWM with 50% duty cycle, each period is \(T = 1/5000 = 200\) μs. The signal is HIGH for \(t_{on} = 0.5 \times 200 = 100\) μs and LOW for 100 μs. A motor with mechanical time constant \(\tau_m \approx 50\) ms cannot respond to individual 200 μs pulses (\(\tau_m / T = 250\) cycles per time constant), so it sees only the average voltage: \(V_{avg} = 0.5 \times 5 = 2.5\) V. This is why PWM “fakes” analog – the actuator’s inertia performs the averaging.
Concept Check: PWM Fundamentals
8.2 Interactive PWM Calculator
Show code
viewof targetSpeed = Inputs.range([0,100], {step:1,value:50,label:"Target Output (%)"})viewof pwmResolution = Inputs.select( [8,10,12], {value:8,label:"PWM Resolution",format: x => x ===8?"8-bit (0-255)": x ===10?"10-bit (0-1023)":"12-bit (0-4095)"})viewof pwmFrequency = Inputs.select( [490,1000,5000,20000], {value:5000,label:"PWM Frequency",format: x => x ===490?"490 Hz (Arduino)": x ===1000?"1 kHz": x ===5000?"5 kHz (ESP32 default)":"20 kHz (Silent)"})
// Convert angle to pulse widthint angleToPulseWidth(int angle){return map(angle,0,180,1000,2000);// 1000-2000 microseconds}// Using ESP32Servo library (handles timing automatically)#include <ESP32Servo.h>Servo myServo;myServo.attach(pin,1000,2000);// min/max pulse widthmyServo.write(90);// Move to 90 degrees
8.5 ESP32 PWM Configuration
// ESP32 has 16 PWM channels (0-15)// Each channel can have different frequency and resolution// Motor control - high frequency for silent operationconstint motorChannel =0;constint motorFreq =20000;// 20 kHz (above human hearing)constint motorResolution =8;// 0-255// LED control - lower frequency acceptableconstint ledChannel =1;constint ledFreq =5000;// 5 kHzconstint ledResolution =10;// 0-1023 (higher resolution for smooth fades)// Servo control - must be 50 Hzconstint servoChannel =2;constint servoFreq =50;// 50 Hz (20ms period)constint servoResolution =16;// High resolution for precise anglesvoid setup(){// Motor PWM ledcSetup(motorChannel, motorFreq, motorResolution); ledcAttachPin(MOTOR_PIN, motorChannel);// LED PWM ledcSetup(ledChannel, ledFreq, ledResolution); ledcAttachPin(LED_PIN, ledChannel);// Servo PWM (use ESP32Servo library instead for easier control) ledcSetup(servoChannel, servoFreq, servoResolution); ledcAttachPin(SERVO_PIN, servoChannel);}
8.6 PWM Speed to Value Conversion
// Convert percentage speed to PWM valueint speedToPWM(int speedPercent,int resolution){int maxValue =(1<< resolution)-1;// 2^resolution - 1return map(speedPercent,0,100,0, maxValue);}// Examples:// 75% speed at 8-bit resolution:int pwm8 = speedToPWM(75,8);// = 191 (out of 255)// 75% speed at 10-bit resolution:int pwm10 = speedToPWM(75,10);// = 767 (out of 1023)// 75% speed at 12-bit resolution:int pwm12 = speedToPWM(75,12);// = 3071 (out of 4095)
8.7 Why Duty Cycle Matters for IoT
Energy Efficiency: LED at 50% duty cycle uses approximately 50% power, extending battery life
Precise Control: Achieve 256 speed levels (8-bit) or 1024 levels (10-bit) from a simple digital pin
No Analog Hardware: Digital MCUs can control analog-like behavior without DAC chips
Thermal Management: Lower duty cycles reduce heat in motors and drivers
Key insight: The actuator “sees” the average voltage, smoothed by its physical response time. A 1kHz PWM signal switching between 0V and 5V at 50% duty looks like steady 2.5V to a motor!
8.8 Worked Example: Smart Greenhouse Ventilation Fan
A greenhouse uses a 12V DC fan (rated 0.5A at full speed) controlled by an ESP32 via a MOSFET driver. The system adjusts fan speed based on temperature.
Step 1: Power at each operating point
Temperature
Fan Speed
Duty Cycle
Average Current
Power Consumed
Below 22C
Off
0%
0 A
0 W
22-25C
Low
30%
0.15 A
1.8 W
25-30C
Medium
60%
0.30 A
3.6 W
30-35C
High
85%
0.425 A
5.1 W
Above 35C
Full
100%
0.50 A
6.0 W
Power = Voltage x Average Current = 12V x (Duty Cycle x 0.5A)
Step 2: Daily energy consumption
Assume a typical summer day: 6 hours off (night), 8 hours at 30%, 6 hours at 60%, 4 hours at 85%.
Energy = (6 x 0) + (8 x 1.8) + (6 x 3.6) + (4 x 5.1) = 0 + 14.4 + 21.6 + 20.4 = 56.4 Wh/day
Compare to running the fan at full speed 24 hours: 6.0W x 24h = 144 Wh/day. PWM-based variable speed saves 61% of the daily energy.
Step 3: Why 20 kHz for this fan?
At lower PWM frequencies, the fan motor produces audible hum. A DC motor’s coils vibrate at the PWM frequency, and humans hear 20 Hz to 20 kHz. Choosing 20 kHz pushes the hum above hearing range, making the fan appear to run silently at any speed. The trade-off: higher PWM frequencies increase switching losses in the MOSFET driver (typically adding 0.1-0.3W at 20 kHz versus 1 kHz), but the acoustic benefit outweighs the small efficiency loss in an occupied greenhouse.
8.9 Choosing PWM Frequency: Decision Factors
The PWM frequency is not arbitrary – it must match the actuator’s physical characteristics and the application’s requirements. Choosing wrong creates audible noise, poor control, or wasted energy.
Actuator
Recommended Frequency
Why This Range
What Goes Wrong Outside It
LED
200-1000 Hz
Human eye flicker fusion threshold is ~70 Hz; 200+ Hz eliminates all visible flicker. Higher than 1 kHz wastes switching energy with no visual benefit.
Servo decoder IC expects 20ms period. Position is encoded as pulse width (1-2ms) within this period.
Any other frequency: servo misinterprets position or jitters. Most servos tolerate 40-60 Hz but 50 Hz is the standard.
Heating element
0.5-2 Hz
Thermal mass smooths temperature. Fast switching is unnecessary because the element’s temperature changes over seconds, not milliseconds.
Above 10 Hz: relay contacts wear prematurely (if relay-controlled). No benefit – heaters cannot respond faster than their thermal time constant.
Solenoid valve
100-500 Hz
Must be fast enough that the valve plunger does not physically open/close each cycle (it “floats” at average position).
Below 50 Hz: valve chatters (audible clicking, mechanical wear). Above 1 kHz: increased eddy current losses in the solenoid core.
8.10 Quick Reference: PWM Formulas
Duty Cycle (%) = (ON time / Period) x 100
Duty Cycle (%) = (PWM value / Max value) x 100
Examples (8-bit PWM, 0-255):
- PWM = 0 = 0% duty = 0V average (OFF)
- PWM = 64 = 25% duty = 1.25V @ 5V supply
- PWM = 128 = 50% duty = 2.5V @ 5V supply
- PWM = 191 = 75% duty = 3.75V @ 5V supply
- PWM = 255 = 100% duty = 5V average (FULL ON)
Average Voltage = Supply Voltage x (PWM Value / Max Value)
For Kids: Meet the Sensor Squad!
“I have a superpower,” announced Max the Microcontroller. “I can make things go fast or slow, bright or dim, even though I can only say ON or OFF!”
“That doesn’t make sense,” said Sammy the Sensor. “How can you make something half-bright if you can only turn it fully on or fully off?”
Max grinned. “Watch this!” He started flicking Lila the LED on and off – really, really fast. “Lila, how do you feel?”
“I feel… kind of medium bright!” Lila said, glowing at about half her usual brightness. “But I can tell you’re flickering me!”
“That’s because I’m switching you on and off 5,000 times per second,” Max explained. “Your eyes and most actuators can’t see the individual flickers – they just see the average. If I keep you ON for 25% of the time, you glow at quarter brightness. 75% of the time? Three-quarter brightness!”
“So it’s like a really fast strobe light that tricks everyone into seeing smooth dimming?” asked Bella the Battery.
“Exactly! It’s called PWM – Pulse Width Modulation. And the best part is, I only use as much of Bella’s energy as needed. At 50% duty cycle, we use about half the power!”
“I like that,” said Bella, smiling. “More power saved means I last longer!”
Connection: PWM Control meets PID Feedback Loops
PWM sets the output to an actuator, but how do you decide what duty cycle to use? That is where PID (Proportional-Integral-Derivative) controllers come in. A PID controller continuously reads a sensor (e.g., temperature), compares it to the setpoint, and adjusts the PWM duty cycle to minimize the error. For example, a smart thermostat reads the room temperature, calculates the PID output, and adjusts the heater’s PWM duty cycle accordingly. PWM without feedback is open-loop control; PWM with PID is closed-loop control. See DC Motor Control for PID implementation examples with motors.
Key Takeaway
PWM is the universal technique for controlling actuator output (speed, brightness, position) using digital microcontroller pins. The key parameters are duty cycle (controls power output), frequency (must match the actuator type – 50 Hz for servos, 1-20 kHz for motors, 500+ Hz for LEDs), and resolution (determines how many discrete control levels are available). The average voltage delivered to an actuator equals the supply voltage multiplied by the duty cycle percentage.
8.11 Knowledge Check
Question 1: An ESP32 is using 8-bit PWM resolution to control an LED. What PWM value should you write to achieve approximately 75% brightness?
75
128
191
255
Answer: C) 191. With 8-bit resolution, the maximum value is 255. To achieve 75% duty cycle: 0.75 x 255 = 191.25, rounded to 191. This produces an average voltage of 75% of the supply voltage, resulting in approximately 75% brightness.
Question 2: Why must servo motors use exactly 50 Hz PWM frequency rather than the 5 kHz or 20 kHz used for DC motors?
Servos cannot physically respond to faster frequencies
The servo’s internal control circuit interprets pulse width within a 20ms period to determine angle position
Higher frequencies would damage the servo motor
50 Hz saves more power than higher frequencies
Answer: B) The servo’s internal control circuit interprets pulse width within a 20ms period to determine angle position. Servo motors decode position from the pulse width (1ms = 0 degrees, 1.5ms = 90 degrees, 2ms = 180 degrees) within each 20ms period (50 Hz). Using a different frequency changes the period, causing the servo to misinterpret the commanded position or not respond at all.
Question 3: What is the main advantage of using 12-bit PWM resolution (0-4095) instead of 8-bit (0-255) for LED dimming?
12-bit PWM uses less power
12-bit resolution provides 4096 brightness levels for smoother, more gradual fading
12-bit PWM runs at a higher frequency
12-bit resolution is required for RGB LEDs
Answer: B) 12-bit resolution provides 4096 brightness levels for smoother, more gradual fading. Higher PWM resolution means more discrete steps between off and full brightness. With 8-bit (256 levels), transitions can appear slightly stepped. With 12-bit (4096 levels), fading appears seamless to the human eye, especially at low brightness levels where steps are most noticeable.
1. Using analogWrite() on ESP32 Without LEDC Configuration
The Arduino analogWrite() function does not work on ESP32. The ESP32 uses the LEDC (LED Control) peripheral which requires channel setup (ledcSetup(channel, freq, resolution)) and pin attachment (ledcAttachPin(pin, channel)) before ledcWrite() will function. Attempting analogWrite() on ESP32 silently produces no PWM output.
2. Wrong PWM Frequency for Servo Control
Standard RC servos require 50 Hz PWM (20 ms period). Using higher frequencies (e.g., 1 kHz from analogWrite()) causes the servo to jitter, overheat, or receive incorrect position commands. Always configure the PWM channel to exactly 50 Hz when using PWM to control servo motors — or better, use the Arduino Servo.h library which handles frequency automatically.
3. PWM Frequency Below 20 kHz for DC Motors Causing Audible Whine
DC motor coils vibrate mechanically at the PWM switching frequency. PWM at 1 kHz or 5 kHz produces clearly audible tones from the motor. Set motor PWM frequency above 20 kHz (beyond human hearing range) to eliminate audible noise. This requires direct timer configuration on Arduino; the ESP32 LEDC peripheral supports frequencies up to 40 MHz.
4. Ignoring PWM Output Voltage Level vs. Driver Input Requirements
ESP32 GPIO outputs 3.3 V HIGH; many H-bridge driver ICs (L298N) have input thresholds designed for 5 V logic and may not reliably switch at 3.3 V. Verify the driver IC’s input HIGH voltage threshold against your microcontroller’s output voltage; some drivers require a logic level shifter or accept 3.3 V inputs directly (TB6612FNG, DRV8833).
8.15 What’s Next
If you want to…
Read this
Learn about servo motor control which uses specific PWM pulse widths