Using Pulse Width Modulation to Control Hardware With Python
Pulse width modulation simulates analog signals with digital square waves, letting Python control LED brightness, motor speed, and servo position. This guide explains the concepts, jargon, frequencies, and practical code examples for Raspberry Pi and Arduino.
Pulse Width Modulation: The Simple Trick That Lets Python Talk to Hardware
If you've ever watched an LED slowly brighten, heard a motor smoothly accelerate, or seen a servo arm move to a precise position, you've witnessed pulse width modulation (PWM) in action. But what's happening inside that tiny chip or microcontroller?
At PythonSkillset, we often get asked: "How do I control something more than just turning an LED on or off?" The answer is PWM, and it's surprisingly simple once you understand the basics.
What PWM Actually Is
Pulse width modulation isn't complex math or secret hardware magic. It's a method of simulating an analog signal using a digital square wave. Instead of varying the voltage continuously, you rapidly switch a signal between high and low states. The key is how long it stays high versus low.
Think of it like a light switch. If you flip it on and off really fast—say 500 times per second—the light doesn't actually flash. Your eyes see an average brightness. Flip it on for longer each cycle, the light looks brighter. Flip it on for shorter bursts, it dims.
That's it. PWM is just a fancy way of saying "vary the duty cycle." The duty cycle is the percentage of time the signal is high during one complete cycle.
Breaking Down the Jargon
Let's make this concrete. When working with a Raspberry Pi or Arduino using Python, you set up a PWM pin with two parameters:
Frequency: How many complete on-off cycles happen per second. Measured in hertz (Hz). For LEDs, 100-500 Hz works fine. For motors, you want higher—usually 1000-2000 Hz—to avoid audible whining.
Duty cycle: The percentage of each cycle that the signal stays on. 0% means always off. 100% means always on. 50% means half the time on, half off.
Here's where most tutorials get confusing. They start throwing formulas at you. Let's skip that and use a real example from PythonSkillset's workshop.
A Simple Python Example
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
# Create PWM instance at 100 Hz
pwm = GPIO.PWM(18, 100)
# Start at 50% duty cycle
pwm.start(50)
time.sleep(2)
# Increase brightness
pwm.ChangeDutyCycle(75)
time.sleep(2)
# Full brightness
pwm.ChangeDutyCycle(100)
time.sleep(2)
# Clean up
pwm.stop()
GPIO.cleanup()
See what happened? We didn't change the voltage. We didn't use a potentiometer. We just told the pin to be on more often. The LED appears brighter because the average power delivered to it increased.
Where PWM Really Shines
LED dimming is the obvious use case, but PWM gets more interesting in three common applications:
Servo Motors
Servos expect a very specific PWM signal. They read the pulse width, not the duty cycle percentage. A typical servo works with 50 Hz frequency (20 millisecond period). A 1 millisecond pulse sends the arm to 0 degrees. A 1.5 ms pulse centers it. A 2 ms pulse swings it to 180 degrees.
# For a standard servo
servo_pwm = GPIO.PWM(servo_pin, 50)
servo_pwm.start(0)
# 2.5% duty cycle = 0.5 ms pulse (approx 0 degrees)
servo_pwm.ChangeDutyCycle(2.5)
time.sleep(1)
# 7.5% duty cycle = 1.5 ms pulse (center position)
servo_pwm.ChangeDutyCycle(7.5)
time.sleep(1)
# 12.5% duty cycle = 2.5 ms pulse (approx 180 degrees)
servo_pwm.ChangeDutyCycle(12.5)
time.sleep(1)
DC Motor Speed Control
Hobby motors are beautifully simple. Higher PWM duty cycle means faster speed. But there's a catch: motors have inertia, and low duty cycles might not provide enough torque to start moving. That's why some motor controllers use a "soft start" where the duty cycle ramps up gradually.
Audio Generation
This one surprises beginners. By controlling PWM frequency and duty cycle precisely, you can generate simple tones through a speaker or buzzer. A 440 Hz square wave sounds like an A note. Change the duty cycle to 50% and you get the loudest output. This is how cheap musical greeting cards work.
The Hardware Details You Should Know
Most microcontrollers and single-board computers have dedicated PWM hardware. The Raspberry Pi has two PWM channels. Some Arduino boards have six or more. At PythonSkillset, we use software PWM when we need more channels than hardware provides, but it's less accurate and uses CPU cycles.
The actual PWM generation happens in a timer module inside the chip. A counter runs continuously, resetting at the end of each period. A comparator compares the counter value to your duty cycle setting. When the counter is below the duty cycle value, the output pin goes high. When it exceeds the duty cycle, the pin goes low.
Common Pitfalls
The biggest mistake I see in PythonSkillset's community is using the wrong frequency. For LED dimming, too low a frequency (under 50 Hz) causes visible flicker. Too high (over 2000 Hz for most LEDs) wastes power as the LED switches faster than it needs to.
For motors, low frequencies cause audible whining and reduce efficiency. The motor coil acts as an inductor, smoothing out the current. Higher frequencies produce smoother operation but generate more switching losses in the driver transistor.
Another gotcha: ground loops. If your PWM signal and power supply share a ground path poorly, you'll get erratic behavior. Always use a common ground between your microcontroller and the controlled device.
Why Understanding PWM Matters
When you move beyond blink sketches and start building actual products—smart lights, robot arms, custom audio devices—PWM becomes your primary tool for interacting with the analog world from a digital system. It's cheap, efficient, and supported by nearly every microcontroller on the market.
The next time you see a dimmable LED bulb, know that inside is a tiny chip running PWM at thousands of cycles per second, letting a digital signal pretend to be an analog voltage. And with Python and a few lines of code, you can do the same thing on your desk tonight.
Start with an LED and a resistor. Work up to a servo. Before long, you'll be watching motors spin at precisely controlled speeds, all thanks to that simple square wave that learned to tell time.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.