gpiozero
gpiozero is the library Raspberry Pi OS installs by default, and it is the one to reach for first. You describe the component you wired — an LED, a button, a motion sensor — and gpiozero handles pins, pulls, debouncing and PWM behind the scenes. The official documentation is at gpiozero.readthedocs.io.
PyRasp runs gpiozero unmodified. It ships the real gpiozero 2.0.1 wheel and points it at the simulator's own RPi.GPIO, so from gpiozero import ... behaves the same as on a Pi.
Blinking an LED
Wire a red LED to GPIO17 through a 330 Ω resistor:
from wiring import *
pi = Pi()
pi.GPIO17 >> Resistor(330) >> LED("red") >> pi.GNDThen blink it. blink() runs in a background thread, so the program keeps going while the LED flashes:
from gpiozero import LED
from time import sleep
led = LED(17)
led.blink(on_time=0.5, off_time=0.5)
while True:
sleep(1)Buttons and sensors
Button enables the Pi's internal pull-up by default, so wire the button between the GPIO pin and ground:
from gpiozero import Button
from signal import pause
button = Button(27, pull_up=True) # GPIO27, internal pull-up
button.when_pressed = lambda: print("pressed")
button.when_released = lambda: print("released")
pause() # waits for the callbacks
Sensors that are just digital inputs work the same way:
from gpiozero import MotionSensor
pir = MotionSensor(4) # PIR OUT on GPIO4
pir.when_motion = lambda: print("motion!")
pir.when_no_motion = lambda: print("still")
Analog sensors have no digital equivalent: read them through an ADC (below) and use the value. The LDR, soil probe, potentiometer and gas sensor in the shop all work that way.
from gpiozero import MCP3008
light = MCP3008(channel=0) # LDR on channel 0
print(light.value) # 0.0 (dark) … 1.0 (bright)
Distance and motion
DistanceSensor wires to an HC-SR04: trigger on one pin, echo (divided to 3.3 V) on another.
from gpiozero import DistanceSensor
sensor = DistanceSensor(echo=24, trigger=23, max_distance=4)
print(sensor.distance) # metres
sensor.wait_for_in_range(timeout=5)
Servos
Servo drives a hobby servo from a PWM pulse. The simulator's servo reads pulses from 500 µs (0°) to 2400 µs (180°), so pass those pulse widths to AngularServo:
from gpiozero import AngularServo, Servo
servo = AngularServo(17, min_angle=-90, max_angle=90,
min_pulse_width=0.5 / 1000, max_pulse_width=2.4 / 1000)
servo.angle = 45
servo.value = -0.5 # -1 … 1, same as angle -90 … 90
plain = Servo(27, min_pulse_width=0.5 / 1000, max_pulse_width=2.4 / 1000)
plain.min() # go to 0°
plain.max() # go to 180°
Power the servo from 5 V, not from a GPIO pin.
Buzzer
An active buzzer beeps when it is on; a passive one plays the frequency you feed it:
from gpiozero import Buzzer, TonalBuzzer
beep = Buzzer(22) # active buzzer
beep.beep(on_time=0.2, off_time=0.3, n=3)
tone = TonalBuzzer(27) # passive buzzer
tone.play("A4") # a real note, in Hz under the hood
tone.stop()
RGB LEDs
RGBLED treats a red, green and blue die as three PWM outputs. Give each die its own resistor:
from gpiozero import RGBLED
led = RGBLED(red=17, green=27, blue=22)
led.color = (1, 0.5, 0) # orange: (r, g, b), each 0 … 1
led.blue = 1
Reading analog values: MCP3008
The Pi has no analog inputs, so read a potentiometer (or LDR, or soil probe) through an MCP3008 ADC over SPI. gpiozero talks to it through the simulator's spidev:
from gpiozero import MCP3008, PWMLED
pot = MCP3008(channel=0)
led = PWMLED(17)
led.source = pot # the knob now dims the LED
print(pot.value, pot.voltage) # 0.0 … 1.0 and 0 … 3.3 V
Going deeper
GPIOZERO_PIN_FACTORY is set to rpigpio before your program starts, so every device uses the simulator's RPi.GPIO. You never need Device.pin_factory = ..., but you can still inspect it:
from gpiozero import Device
print(Device.pin_factory) # <gpiozero.pins.rpigpio.RPiGPIOFactory object ...>
Devices that work: everything built on digital inputs, digital outputs, PWM, edges and the SPI adapter — LED, PWMLED, RGBLED, Button, MotionSensor, DistanceSensor, Servo, AngularServo, Buzzer, TonalBuzzer, Motor, PhaseEnableMotor, RotaryEncoder, MCP3008 and the other MCP3xxx classes, LEDBoard, LEDBarGraph and TrafficLights, plus CPUTemperature, which reads the simulator's /sys/class/thermal/thermal_zone0/temp.
What is not available: PingServer and LoadAverage (they shell out or read files the simulator does not provide), DiskUsage (no real statvfs), LightSensor used as the RC-timing circuit (the shop's LDR is analog, so read it through an MCP3008), and any device that needs a pin factory other than rpigpio (pigpio, lgpio, native, remote GPIO). There is no MCP23017 I²C expander part in the shop, so only the built-in SPI ADC classes can be used.
pause() comes from the standard library's signal module and the simulator provides it: it blocks until you press Stop, letting callbacks do the work. time.sleep, debounce times and edge timestamps all run on simulated time (see the runtime page).
Simulator vs real hardware
- PWM is modelled, not toggled. On a real Pi, gpiozero's software PWM flips a pin thousands of times a second; here the simulation records a duty cycle and frequency and the part reacts to them. LEDs dim, servos turn and buzzers sound at exact duty cycles, with no CPU cost and no jitter.
- Edges carry their own timestamps.
DistanceSensortimes the echo withtime.monotonic()inside a pin callback; the simulator gives callbacks the clock reading of the edge, so distances come out right even though the simulation advances in steps. - Bounce handling differs slightly. gpiozero asks
RPi.GPIOfor a bounce time; the simulator debounces by waiting for the level to settle (trailing), while realRPi.GPIOignores edges for a fixed time after the first one. Bouncy buttons behave more cleanly here. - No warnings on pin reuse. Real gpiozero/RPi.GPIO prints warnings when a pin changes function under a device; the simulator keeps quiet (
setwarningsis accepted but nothing warns). CPUTemperaturereports the simulator's room temperature plus 22 °C — an idle Pi 4 in a case — not the load of your real machine.- MCP3008 reads include the chip's noise and non-linearity (0.5 LSB σ and 1 LSB INL by default), so values wobble like real ones; on a real Pi they would also wobble with power-supply noise.