PWM
A GPIO can be fully on or fully off — nothing in between. Pulse-width modulation (PWM) fakes the in-between by switching the pin on and off quickly:
- the duty cycle is the fraction of each period the pin is high (0–100%);
- the frequency is how many periods fit in a second.
How a part reads PWM depends on the part: an LED averages it into brightness, a servo measures the width of one pulse, a passive buzzer hears the switching as a tone. One technique, three very different results.
Dimming an LED
At 50% duty an LED is on half the time. Above about 100 Hz — the default for gpiozero's PWMLED — your eye averages the flashes into a steady, dimmer light:
from wiring import *
pi = Pi()
pi.GPIO18 >> Resistor(330) >> LED("red") >> pi.GND
from gpiozero import PWMLED
from time import sleep
lamp = PWMLED(18)
while True:
for level in (0, 0.2, 0.4, 0.6, 0.8, 1.0):
lamp.value = level
sleep(0.3)
lamp.value is the duty cycle, from 0.0 to 1.0. The simulator computes the LED's brightness from the average current: the current it would pass at full on, multiplied by the duty.
Turning a servo
A servo ignores brightness-style duty and measures one thing: how long the pulse is. The pulses repeat every 20 ms (50 Hz), and the width sets the angle — about 0.5 ms for 0°, 2.4 ms for 180°. Wire the signal to a GPIO and power the servo from 5 V, never from a GPIO or 3V3: moving draws hundreds of milliamps.
from wiring import *
pi = Pi()
servo = Servo()
servo.v_plus >> pi.V5
servo.gnd >> pi.GND
servo.sig >> pi.GPIO18
from gpiozero import AngularServo
from time import sleep
servo = AngularServo(18, min_angle=0, max_angle=180, min_pulse_width=0.0005, max_pulse_width=0.0024)
while True:
for angle in (0, 90, 180):
servo.angle = angle
sleep(1)
gpiozero's defaults are 1–2 ms pulses, which sweep only the middle of the range; passing min_pulse_width and max_pulse_width uses the full travel. The servo page has its electrical numbers, and these are its three pins:
| Pin | What it is | Type |
|---|---|---|
| GND | Ground (brown) | ground |
| V+ | Supply 4.8–6 V (red) | power |
| SIG | Control pulse (orange) | io |
Playing a tune
A passive buzzer is a bare transducer: a steady high level makes one click, but PWM at an audio frequency drives the diaphragm and makes a note. That is the whole difference from an active buzzer, which contains its own oscillator and just needs on and off. A passive buzzer costs $1.50.
from wiring import *
pi = Pi()
pi.GPIO18 >> Resistor(100) >> PassiveBuzzer() >> pi.GND
from gpiozero import TonalBuzzer
from gpiozero.tones import Tone
from time import sleep
buzzer = TonalBuzzer(18)
for note in ("C5", "E5", "G5", "C6"):
buzzer.play(Tone(note))
sleep(0.3)
buzzer.stop()
The 100 Ω series resistor stops the buzzer's low-impedance coil from drawing too much from the pin.
Which pins can do PWM?
Every GPIO can do software PWM — the CPU switches the pin, which is what RPi.GPIO.PWM and gpiozero do by default. It is fine for LEDs and buzzers, but it jitters when the CPU is busy. Four pins can also do hardware PWM, timed by dedicated hardware and rock steady, which is what a servo wants:
| Channel | GPIOs (BCM) | Physical pins |
|---|---|---|
| PWM0 | GPIO12, GPIO18 | 32, 12 |
| PWM1 | GPIO13, GPIO19 | 33, 35 |
The simulator works the same on every pin: it stores the duty and frequency the program asks for and lets the parts interpret them, so software and hardware PWM behave alike.
Going deeper
- PWM in the simulator is duty and frequency, not edges. When your program sets a PWM output, the simulator records a duty (0–1) and a frequency on that net and solves the circuit as if the pin were steadily on. Parts interpret it: the LED averages its current, the servo converts
duty ÷ frequencyinto a pulse width, the buzzer to a pitch. It does not generate a 50 Hz square wave you could watch on hardware — but the logic analyser and the board's readouts show the recorded duty and frequency. - The servo's model. It reacts to pulses only when the frequency is 20–400 Hz and the duty is strictly between 0 and 1 — steady DC is not a pulse, so the horn holds. Pulse width maps linearly from the unit's Pulse for 0° (500 µs by default) to Pulse for 180° (2400 µs), with a 10 µs deadband so tiny changes do not twitch; it slews at up to 600 °/s, draws 250 mA while moving and 650 mA stalled, and warns when its 5 V supply falls below 4.3 V ("it needs 4.8–6 V and will be weak or jerky").
- The buzzer's model. It sounds when more than 2 mA flows and the frequency is in audio range (20 Hz–20 kHz), and it is loudest at its 2.7 kHz resonance — an octave away it is noticeably quieter. A duty of exactly 0 or 1 is silence.
- The LED's model. Brightness is the average current over the nominal 20 mA, capped at 1.5; the resistor maths is on the LED page. The overcurrent warning is based on the peak (steady-on) current, so halving the duty does not rescue an overdriven LED.
- Frequency choices. LEDs: 100 Hz–1 kHz (gpiozero's 100 Hz default is invisible). Buzzers: the note itself. Servos: exactly 50 Hz. Motors: a few hundred Hz to a few kHz — high enough to be smooth, low enough not to heat the driver.
- gpiozero's output device documentation covers
PWMLED,Servo,AngularServoandTonalBuzzer, includingpulse(),blink()andled.source.