PyRaspBeta

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:

circuit.py
from wiring import *

pi = Pi()
pi.GPIO17 >> Resistor(330) >> LED("red") >> pi.GND

Then blink it. blink() runs in a background thread, so the program keeps going while the LED flashes:

main.py
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. DistanceSensor times the echo with time.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.GPIO for a bounce time; the simulator debounces by waiting for the level to settle (trailing), while real RPi.GPIO ignores 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 (setwarnings is accepted but nothing warns).
  • CPUTemperature reports 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.