Raspberry Pi PWM Basics
Learn how Pulse Width Modulation (PWM) lets a purely digital GPIO pin fake an in-between, analog-like voltage by switching on and off very quickly, and control it with gpiozero's PWMLED and duty cycle.
The Problem: GPIO Pins Are Digital
A standard GPIO pin only really knows two states: fully on (about 3.3V) or fully off (0V). That's fine for an LED you just want to switch on or off, but plenty of things you'd want to control - an LED's brightness, a motor's speed, a servo's position - call for something in between full power and none at all. A plain digital pin has no native way to output, say, '40% of full brightness.'
What PWM Actually Does
Pulse Width Modulation solves this without ever producing a true in-between voltage. Instead, it switches the pin fully on and fully off very rapidly - hundreds or thousands of times per second - and varies how much of each cycle is spent on versus off. If the switching is fast enough, an LED can't visibly flicker at that speed, and a motor's inertia can't respond to individual pulses either; both effectively respond to the average power delivered over each cycle, which reads as a steady, intermediate level rather than a series of blinks.
Duty Cycle Defined
The fraction of each cycle that the signal spends HIGH is called the duty cycle, usually expressed as a percentage (or, in gpiozero, as a float from 0.0 to 1.0). A 25% duty cycle means the pin is on for one quarter of each cycle and off for the other three quarters, averaging out to roughly a quarter of full power. A 100% duty cycle is indistinguishable from a plain 'on' signal, and 0% is indistinguishable from 'off' - PWM's plain digital on/off states are really just the two extremes of the same duty cycle scale.
Setting brightness directly with PWMLED
from gpiozero import PWMLED
from time import sleep
led = PWMLED(17)
led.value = 0.0 # off
sleep(1)
led.value = 0.25 # dim - 25% duty cycle
sleep(1)
led.value = 1.0 # full brightness - 100% duty cycle
sleep(1)
led.off()Fading Smoothly with pulse()
Instead of jumping between fixed brightness levels, `PWMLED` also has a `.pulse()` method that continuously ramps the duty cycle up and down over time, producing a smooth fade in and out - a common effect for 'breathing' status LEDs. Under the hood it's still just PWM: gpiozero is rapidly adjusting the duty cycle itself, many times per second, to create the illusion of a continuously changing brightness.
A smooth breathing fade
from gpiozero import PWMLED
from signal import pause
led = PWMLED(17)
led.pulse(fade_in_time=2, fade_out_time=2, n=None, background=True)
# fades from off to full brightness and back over 2s each way, repeating forever (n=None)
pause()