Pi IoT In Python Using GPIO Zero - On/Off Devices
Written by Harry Fairhead & Mike James   
Monday, 14 December 2020
Article Index
Pi IoT In Python Using GPIO Zero - On/Off Devices
Buzzer & Custom Device


The only other standard on/off device is the Buzzer and it goes on and off in the sense that it is driven by a single GPIO line, just like an LED, and makes a sound when the line is high and is silent when the line is low. It isn’t capable of making different tones, it is either on or off.
The exact type of buzzer is a piezoelectric buzzer which emits a single tone and can be very loud:


You don’t need a current-limiting resistor as piezo buzzers take very little current. All you have to do is to make sure that will operate at 3 volts. All you have to do to use one is connect the negative lead to ground and the positive lead to a GPIO line:



The Buzzer object has all of the properties and methods of the LED object. The on and off methods now make the buzzer sound but work in exactly the same way. The one small change is that blink is now renamed beep. It works in exactly the same way but now the buzzer is switched on and off.

from gpiozero import Buzzer
from signal import pause
buzz = Buzzer(4)
print("Program Complete")

You can see quite clearly that creating a Buzzer class is more about renaming things, so that your program can be more understandable, than introducing anything new.

A Custom On/Off Device

There are lots of on/off devices other than LED and Buzzer, so how do you arrange to work with them?

One solution is to treat everything as if it was an LED. For example, if you have an electric door lock you could write:


It would work, but it might be confusing in the future when you have forgotten the program and are forced to wonder what an LED is doing with a lock and what lock.on() means.

A better and simpler solution is to derive a custom on/off device. You can do this using inheritance from DigitalOutputDevice which provides nearly all of the methods that LED has – in particular it has on and off methods. In this case we can simply pass the constructor parameters to the constructor of DigitalOutputDevice:

class Lock(DigitalOutputDevice):
    def __init__(self,*args,**kwargs):

At this point Lock is really just a copy of DigitalOutputDevice and to customize it to be a Lock class we need to add appropriate methods. What we really need are two new methods, lock and unlock, and these can be implemented by calling the on and off methods of the super class:

def lock(self):
def unlock(self):

Now we have a Lock object that has lock and unlock methods that make sense for the device. However, we also still have on, off and blink which don’t make any sense.

The simplest solution is to override them with methods that raise an appropriate exception:

def on(self):
  raise AttributeError(
"'Lock' object has no attribute 'on'") def off(self): raise AttributeError(
"'Lock' object has no attribute 'off'") def blink(self): raise AttributeError(
"'Lock' object has no attribute 'blink'")

This stops the use of the method calls, but the inappropriate attributes are still accessible. That is:


results in an exception, but:


works even if you can’t actually call the method. This is probably good enough for most use cases, but you could take it one step further by customizing the constructor. After all, you don’t want to allow active_high to be set to False and have lock mean unlock and vice versa.

You can check for any keyword parameter using something like:

def __init__(self,*args,**kwargs):
   if 'active_high' in kwargs:
      raise TypeError("active_high not supported")

If you would like a more specific error, you could define your own exception class.

You can carry on tailoring the behavior of Lock to be more lock-like until you have the perfect class. It is also a good idea to put your code into a module so that you can import Lock.

The complete program is:

from gpiozero import DigitalOutputDevice
class Lock(DigitalOutputDevice):
  def __init__(self,*args,**kwargs):
      if 'active_high' in kwargs:
          raise TypeError("active_high not supported")
  def lock(self):
  def unlock(self):
  def on(self):
      raise AttributeError(
"'Lock' object has no attribute 'on'") def off(self): raise AttributeError(
"'Lock' object has no attribute 'off'") def blink(self): raise AttributeError(
"'Lock' object has no attribute 'blink'")

Using Lock is trivial:


You can use the same ideas to implement a custom class for any on/off device you care to use. What is interesting is that in practice customizing an existing class using inheritance is often as much about changing and restricting what the class can do as it is about extending it, which is what textbooks always emphasize.

This completes the software and it is worth remarking that simple locks of this sort are the same hardware problem as driving a solenoid. As these mostly work with 6 or 12 volts you will need to use a transistor driver and you will need to take account of the voltage and the current involved. See the next chapter for more details.

Not in this extract:

  • Phased On/Off



  • There are only two simple on/off devices, LED and Buzzer. They both inherit from DigitalOutputDevice.

  • The blink method turns the device on and off a given number of times and can be run in the background while the program gets on with something else or in the foreground when your program will wait.

  • The toggle method turns the device on if it is off and off if it is on.

  • The Buzzer class is intended to be used with a piezo buzzer, which is either on or off and hence either emits a sound or remains silent. It can only make one tone, which cannot be varied.

  • A piezo buzzer doesn’t need a driver as it takes very little current.

  • You can create a custom on/off device by inheriting from DigitalOutputDevice and providing the additional methods that are required. Restricting access to any existing inappropriate methods is a more difficult task.

  • You can switch multiple on/off devices at the same time or at coordinated times, but there is always a lag between switching devices - 140µs for a Pi Zero and 10µs for a Pi 4.


Raspberry Pi IoT In Python Using GPIO Zero
Second Edition

By Harry Fairhead & Mike James


Buy from Amazon.


  1. Why Pi for IoT?
  2. Getting Started With Python And GPIO Zero
  3. Introduction to the GPIO
  4. Python - Class and Object
  5. Simple On/Off Devices
      Extract 1: On/Off Devices *
  6. Pins And Pin Factories
      Extract 1: Pins 
  7. Some Electronics
  8. Simple Input
      Extract 1:Getting Input ***NEW!!
  9. Complex Input Devices
      Extract 1: Complex Input *
  10. Pulse Width Modulation
      Extract 1:  PWM*
  11. Controlling Motors And Servos
      Extract 1: DC Motors *
  12. Working With Compound Devices
      Extract 1: Compound Devices*
  13. The SPI Bus
  14. Custom SPI Devices
  15. Using The Lgpio Library
  16. Appendix Visual Studio Code Remote Python
    *Extracts from first edition - will be updated.


To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.


SQL Turns 50

The first release of SQL was in June 1974. Designed at IBM by Donald D. Chamberlin and Raymond F. Boyce, it was based on the relational model proposed by E.F. Codd. SQL became the most widely used dat [ ... ]

Visual Studio 17.11 Focuses On Quality Of Life

Microsoft has released Visual Studio 2022 v17.11 Preview 1, the first preview of the next update for Visual Studio 2022. The developers say this update focuses on quality of life improvements for deve [ ... ]

More News

C book



or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 14 December 2020 )