GPIO Zero: a friendly Python API for physical computing

Physical computing is one of the most engaging classroom activities, and it’s at the heart of most projects we see in the community. From flashing lights to IoT smart homes, the Pi’s GPIO pins make programming objects in the real world accessible to everybody.

Some three years ago, Ben Croston created a Python library called RPi.GPIO, which he used as part of his beer brewing process. This allowed people to control GPIO pins from their Python programs, and became a hit both in education and in personal projects. We use it in many of our free learning resources.

However, recently I’ve been thinking of ways to make this code seem more accessible. I created some simple and obvious interfaces for a few of the components I had lying around on my desk – namely the brilliant CamJam EduKits. I added interfaces for LED, Button and Buzzer, and started to look at some more interesting components – sensors, motors and even a few simple add-on boards. I got some great help from Dave Jones, author of the excellent picamera library, who added some really clever aspects to the library. I decided to call it GPIO Zero as it shares the same philosophy as PyGame Zero, which requires minimal boilerplate code to get started.


This is how you flash an LED using GPIO Zero:

from gpiozero import LED
from time import sleep

led = LED(17)

while True:

(Also see the built-in blink method)

As well as controlling individual components in obvious ways, you can also connect multiple components together.


Here’s an example of controlling an LED with a push button:

from gpiozero import LED, Button
from signal import pause

led = LED(17)
button = Button(2)

button.when_pressed = led.on
button.when_released =


We’ve thought really hard to try to get the naming right, and hope people old and young will find the library intuitive once shown a few simple examples. The API has been designed with education in mind and I’ve been demoing it to teachers to get feedback and they love it! Another thing is the idea of minimal configuration – so to use a button you don’t have to think about pull-ups and pull-downs – all you need is the pin number it’s connected to. Of course you can specify this – but the default assumes the common pull-up circuit. For example:

button_1 = Button(4)  # connected to GPIO pin 4, pull-up

button_2 = Button(5, pull_up=False)  # connected to GPIO pin 5, pull-down

Normally, if you want to detect the button being pressed you have to think about the edge falling if it’s pulled up, or rising if it’s pulled down. With GPIO Zero, the edge is configured when you create the Button object, so things like when_pressed, when_released, wait_for_press, wait_for_release just work as expected. While understanding edges is important in electronics, I don’t think it should be essential for anyone who wants to create a simple interactive project.

Here’s a list of devices which currently supported:

  • LED (also PWM LED allowing change of brightness)
  • Buzzer
  • Motor
  • Button
  • Motion Sensor
  • Light Sensor
  • Analogue-to-Digital converters MCP3004 and MCP3008
  • Robot

Also collections of components like LEDBoard (for any collection of LEDs), FishDish, Traffic HAT, generic traffic lights – and there are plenty more to come.

There’s a great feature Dave added which allows the value of output devices (like LEDs and motors) to be set to whatever the current value of an input device is, automatically, without having to poll in a loop. The following example allows the RGB values of an LED to be determined by three potentiometers for colour mixing:

from gpiozero import RGBLED, MCP3008
from signal import pause

led = RGBLED(red=2, green=3, blue=4)
red_pot = MCP3008(channel=0)
green_pot = MCP3008(channel=1)
blue_pot = MCP3008(channel=2) = red_pot.values = green_pot.values = blue_pot.values


Other wacky ways to set the brightness of an LED: from a Google spreadsheet – or according to the number of instances of the word “pies” on the BBC News homepage!

Alex Eames gave it a test drive and made a video of a security light project using a relay – coded in just 16 lines of code.

Yasmin Bey created a robot controlled by a Wii remote:

Version 1.0 is out now so the API will not change – but we will continue to add components and additional features. GPIO Zero is now pre-installed in the new Raspbian Jessie image available on the downloads page. You can also install it now by entering the following commands into a terminal:

sudo apt-get update

sudo apt-get install python3-gpiozero python-gpiozero

Remember – since the release of Raspbian Jessie, you no longer need to run GPIO programs with sudo – so you can just run these programs directly from IDLE or the Python shell. GPIO Zero supports both Python 2 and Python 3. Python 3 is recommended!

Let me know your suggestions for additional components and interfaces in the comments below – and use the hashtag #gpiozero to share your project code and photos!

A huge thanks goes to Ben Croston, whose excellent RPi.GPIO library sits at the foundation of everything in GPIO Zero, and to Dave Jones whose contributions have made this new library quite special.

See the GPIO Zero documentation and recipes and check out the Getting Started with GPIO Zero resource – more coming soon.


Thomas avatar


that looks fantastic, thanks a lot. But shouldn’t it be

led = LED(17)

in the first code example?


Ben Nuttall avatar

Thanks – fixed. I threw the wiring diagrams in last.

Dougie avatar

This is excellent, one less reason for the pi user to need sudo.

Next challenge is re-writing Gordon’s WiringPi to use the same techniques so that WiringPi programs don’t need to run with sudo to access GPIO pins.

AndrewS avatar

That’s already been done Dougie, see

Dougie avatar

Thanks, Andrew. I’ve got wiringPi V2.29 but hadn’t seen there was some set-up needed to use /dev/gpiomem.

Megagorilla avatar

What do
button.when_pressed = led.on
button.when_released =
do? Do they bind an action for when the button is pressed/released later on? Or do they turn the led on/off then that line of code is running?

Ben Nuttall avatar

Yes, they bind a function to the action of the button being pressed and released, i.e. when the button is pressed, turn the LED on.

You can also provide custom functions, e.g:

def hello():
button.when_pressed = hello
Aydan avatar

I like the simple interface but I miss a generic input/output class. Not every input is a button and not every output is a led. I’d suggest to rename the current implementation of LED and BUTTON to OUTPUT and INPUT and then assign LED=OUTPUT and BUTTON=INPUT


Ben Nuttall avatar

We also provide generic classes for InputDevice, OutputDevice, PWMOutputDevice and more.

Ian Bardsley avatar

I like where this is heading. Look forward to seeing how it develops. Keep up the good work

WallyWare avatar

One less barrier to interfacing to the real world with the Raspberry Pi!

James avatar

I have been running Jessie for a while. I have done sudo apt-get update and upgrade but I am getting an ImportError No module named gpiozero.

I have encountered issues in the past where I have initially installed sw using pip and I have later installed through apt-get. So I prefer the later.

Do I just need to be patient and wait for it to be installable using sudo apt-get install gpizero or will everyone need to use pip?


HAL13 avatar

Yes, as per article: “GPIO Zero is now pre-installed in the new Raspbian Jessie image available on the downloads page. It will also appear in the apt repo shortly.”

Jon Witts avatar

I am running into exactly the same issue James. I am running the Raspbian Jessie image but cannot import the gpiozero library into Python. Also if I attempt the apt-get install commands I get package not found.

Did you ever resolve your issue?


Ben Nuttall avatar

GPIO Zero is now in the apt repo. Ensure you update your packages first “sudo apt-get update” then install with “sudo apt-get install python3-gpiozero python-gpiozero”

Jon Witts avatar

Thanks Ben; all working now!

Schelto avatar

Thanks for all the good work Ben. I’m new to Linux and Python. I’ve followed your install instructions to the letter (I think), but every time I hit the “sudo apt-get install python3-gpiozero python-gpiozero” command, I get the “E: Unable to locate package python3-gpiozero” error. This has been going on for several days.

Any idea whats the issue?

P.S. I am running the RPI from my desktop through VNC

NoSheds avatar

Like “Schelto” said on 17th Dec 2015 at 8:06 pm

“every time I hit the “sudo apt-get install python3-gpiozero python-gpiozero” command, I get the “E: Unable to locate package python3-gpiozero” error.”

I have done update, upgrade and dist-upgrade so I’m completely up-to-date. However, I’m running wheezy according to lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 7.8 (wheezy)
Release: 7.8
Codename: wheezy

I don’t want to update this pi to Jessie, as I it’s configured for a specific job and I don’t want to start it from scratch with a fresh install, and I got the impression that updating from Wheezy to Jessie was a bit flaky. Any ideas how to install gpiozero in this set up?

Francis Kim avatar

Another reason to learn Python! :)

charudatt avatar

Thanks for including the ADC, please include LCD and IO expander chips. That would give a big punch to the whole project.

Dave Jones avatar

Working on it, but IO expanders in particular is going to take a while. I’ve now got a very good idea in my head of exactly how the guts of the library need to change to support IO expanders (and how to maintain backward compatibility for all existing use cases); just need to find the time to implement it (and all the other myriad things on the todo list like a proper test suite!).

Can’t promise anything on the LCD side – I don’t think we’ve got any specific tickets regarding those yet, but if you’ve got something in mind do go open a ticket on the repo ( and we’ll certainly consider it!

Maurice avatar

Any chance of doing for the MCP23017 what you did for the MCP3008

Dave Jones avatar

That’s a rather different beast to the MCP3004/8 but we are intending to support port expanders at some point (see my reply above). If you’d like support for this one in particular, do feel free to open a ticket on the GitHub repo so we don’t forget anything!

Gary Camp avatar

Could you use something a little less obscure than MCP3008 in the naming. What the heck is that? We are beginners here and I liked the comment about making the terms more indicative of the function. Then this…

red_pot = MCP3008(channel=0)

Otherwise, going in a good direction. Is there a Basic available for Pi? I am old school (very old!) I will search when I get a chance.

AndrewS avatar

One of the very first google search results:

I’d guess that GPIO Zero will gain support for more ADC chips over time.

Dave Jones avatar

The problem with using a more generic name like “AnalogInterface” is that it could apply to an awful lot of chips with slightly different interfaces which we need to account for in the implementation.

As Ben mentioned earlier in the comments, we do have generic classes and in this case MCP3008 derives from AnalogInputDevice which is an abstract class representing an SPI connected ADC (we should probably rename the base class to indicate that it’s SPI specific but anyway…).

The fact remains, in this case being specific is necessary as the class is only intended to talk to an SPI connected, 8-channel, 10-bit ADC (i.e. an MCP3008). Does this mean that ultimately we’ll wind up with lots of ever so slightly different classes for a variety of ADCs? Sure, but then that’s a very good use-case for class inheritance.

SiriusHardware avatar

I love BASIC too. If you don’t mind getting to grips with a different operating system then try flashing a spare card with RISCOS, which comes with classic BBC BASIC integrated.

Or, have a look at FUZE BASIC, from here:-

I can’t say whether either of these have been (or need to be) updated specifically to work with the Zero, though – no doubt someone will jump in and let us know.

Ralph Corderoy avatar

Sorry, but hiding the control flow seems like a bad idea. What’s the point of “ = red_pot.values” without understanding loops and conditional statements so one can move on beyond the simple “wiring in software” to match the hardware wiring? It might need a few less lines to have a button control an LED than its predecessor, but those lines weren’t hard to learn and taught something useful. GPIO Zero’s source is advanced Python too, e.g. metaclasses, so that’s more of an impediment to one’s exploration.

Ben Nuttall avatar

Features like led.blink() and source/values provide a shorthand for common uses, and allow the user to do more than they could without.

I’d start with a while loop turning the LED on and off, and that’s fine for many people, but once you get to the point you want to do two things at once, you’re stuck in a while loop. What if you want to flash an LED and update an LCD display at the same time? We provide a simple solution to allow multitasking without letting timing slip.

The same applies with source/values. You can easily use a while loop to continuously update an LED’s brightness according to a potentiometer’s value. However, you’re again stuck in a while loop and can’t do anything else. Take a look at the GitHub issue that motivated us to add that feature! Issue #76

See my slides from a recent talk here: Physical computing with GPIO Zero

Toby avatar

How do you create a python module?

Ben Nuttall avatar

I gave a presentation on this at the Cambridge Raspberry Jam – and funnily enough, that’s what inspired me to create this library. See my slides here:

Toby avatar

Could you create a raspberry pi controlled BMO using gpiozero?

Ben Nuttall avatar

Could do! It’s been done with a Pi before but I’d love to see a GPIO Zero one!

Toby avatar

Cool. I love adventure time. My favourite character is BMO.

JamesH avatar

Hi Ben, I’m new to Linux & Pi and I get the same issue as Schelto above:

pi@j-pi-1 ~ $ sudo apt-get install python3-gpiozero python-gpiozero
Reading package lists… Done
Building dependency tree
Reading state information… Done
E: Unable to locate package python3-gpiozero
E: Unable to locate package python-gpiozero

Apologies if this is a very noobie issue!

Ben Nuttall avatar

I guess you’re using Raspbian Wheezy – it’s not been added to that repo yet. You can download the deb and install it manually:

wget -O gpz.deb
sudo dpkg -i gpz.db
JamesH avatar

PS I should have mentioned that prior to running the install python3-gpiozero command I did also run:
sudo apt-get update

Singh avatar

great work,
two queries,

– Can I use a camera here as a torch instead of the LED to flash, record ..etc
– Can I integrate a NOIR camera module here?


Ben Nuttall avatar

Yes – there’s a Python module for the camera which plugs in to GPIO Zero very nicely. For example:

from gpiozero import Button
from picamera import PiCamera
from signal import pause

def capture():
    global i
    i += 1
    cam.capture('/home/pi/image%s.png' % i)

btn = Button(2)
cam = PiCamera()

btn.when_pressed = capture


And yes, the Pi NoIR works the same.

Anthony avatar

I use windows for my Python scripting, so how would I install this on a windows system?

Anthony avatar

I am fairly new to all of this, so please bare with me. I am using your module to switch a relay on and off, basically by using the “LED” method. What strikes me as funny, is the fact that when I set the led to “ON”, it switches the relay off….and vice versa. Any particular reason why it would do that?

Cyril Spaceman avatar

Your relay is connected between the output pin and 3.3v when it should be connected between output and Gnd (or vice versa).

Alternatively, you are using the normally closed output terminals on your relay, rather that the normally open pair.

Ben Nuttall avatar

Yes, that’s an active low circuit, whereas an LED is active high. The best way to do this is use the OutputDevice class with the active_high parameter set to False:

from gpiozero import OutputDevice

relay = OutputDevice(14, active_high=False)



wilmer gaona avatar

It’s so brilliant. I have been trying to program two interrupts with two different buttons, but the bounce_time parameter is not accepted in float.
What would be my error?

Here is the output:
File “/usr/lib/python3/dist-packages/gpiozero/pins/”, line 199, in _set_when_changed
TypeError: integer argument expected, got float

Ben Nuttall avatar

We’ve identified this as a bug. See

Zeph avatar

I’m wondering how well this works as an intro for beginners. In particular, how well does it work to help them “form a model” in their heads, so they can intelligently move forward. For example:

led.value = pot.value
led.source = pot.values

These look very similar, but they are doing unrelated things. Reduced to beginner concepts, the first one fetches and assigns on value, while the second “causes led to keep getting its values from pot until told otherwise”. Under the surface, it’s even more complex, with a thread updating led from pot.

My concern is that beginners might start out having fun, but not have much success in forming an internal model, such that they fairly soon “hit a wall” where the next level of understanding requires advanced Python skills. (Perhaps after the instructor has moved on, so they don’t hear about it).

But that’s just a guess. How well is this system working for teaching beginners, in your experience?

Zeph avatar

PS: The first problem – different things looking alike – might be easy enough to solve, if your design wasn’t frozen. You can’t change the syntax, but you could have a naming convention which distinguishes the second usage (led.source = pot.values).

led.call_for_values = pot.provide_values

(not the best choice, just the first example off the top of my head) The call_for_value means that it should be assigned something which provides a series of values, which .provides_values would do.

The second problem – deep python needed to understand that statement – isn’t easy to resolve, but perhaps could be skipped past, as they don’t HAVE to understand how it’s done, just when to use one or the other. So they won’t try:

led.call_for_values = pot.value

and wonder why it doesn’t work.

Maria avatar

With the button, pressed means the pin is low, because it switches to ground. That is a bit counter-intuitive because pressed, or “on”, would normally mean high. Just like led.on() means high, and not low.

Are you planning on making a more generic input “device”? I would very much like to be able to do something like

if pin(12)==True:
# do something

for digital signals that are not buttons.

Sweyn avatar

I came across the button bounce_time bug mentioned above, so I updated to the latest version (1.2.0), and it still doesn’t work properly, albeit without the error message.

from gpiozero import Button, Buzzer
from signal import pause

button = Button(25)
buzzer = Buzzer(22)
button.when_pressed = buzzer.on
button.when_released =

This works fine and as expected. But if you change the line to:

button = Button(25, bounce_time = 0.3)

Then when you press the button, the buzzer just goes on sounding forever.

If you then change ‘buzzer.on’ to ‘lambda: print(“button pressed”)’, it only works once, and seems to get stuck.

I can’t use the Button without a bounce_time, because then the callback usually gets called multiple times.

charudatt avatar

Has development on this stopped.

I am looking forward to seeing 16×2 LCD being integrated into this , but I see that further development has stopped.

Comments are closed