Pi IoT In Python Using GPIO Zero - Compound Devices
Written by Harry Fairhead & Mike James   
Monday, 07 February 2022
Article Index
Pi IoT In Python Using GPIO Zero - Compound Devices
An Example
LEDBarGraph

Most devices are complex with more than one function. To cope with these we need to build compound devices. In this extract from a new book on using GPIO Zero on the Pi in Python we look at how to get started.

Raspberry Pi IoT In Python Using GPIO Zero
Second Edition

By Harry Fairhead & Mike James

GPIOZero2E360

Buy from Amazon.

Contents

  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.

 <ASIN:1871962870>

 

You might well start off with a single GPIO line flashing an LED, but slowly and surely you will graduate to more elaborate devices such as a robot that consists of a number of flashing LED, motors and so on. If what you have created is a development on an existing device then you might create a new custom class using inheritance and we have seen examples of this in earlier chapters. For example, when we wanted to implement a door lock class, the obvious way was to create a new class that inherited from the existing LED class. The reason is that both the LED and lock share a basic on/off behavior, but in the case of the lock it is better called “lock/unlock”.

Compare this to the problem of building a custom class to control three or more separate LEDs. In this case you don’t need to inherit the behavior of a single LED, you need multiple instances of the LED class. In this case what you need is composition, i.e. combining classes, rather than inheritance. GPIO Zero has a class that you can use to create composite devices and this chapter looks at how this works in enough detail for you to create your own.

Devices That Contain Devices

The CompositeDevice class is intended to be inherited by your custom classes to make it easy to include instances of other classes but first it makes sense to see how to do the job without help.

If you were implementing a composite device from scratch then the logical thing to do is to create a tuple, list or dictionary of the instances you need. For example if you wanted to create a custom TriLED class, you might write in the constructor something like:

self.LEDs=(LED(pin1),LED(pin2),LED(pin3))

After this you would write methods that are required using the tuple to access the LEDs.

For example, you might want an AllOn and AllOff method:

from gpiozero import LED
from time import sleep
class TriLED():
    def __init__(self, pin1=None,pin2=None,pin3=None):
        self.LEDs=(LED(pin1),LED(pin2),LED(pin3))
                          
    def AllOn(self):
        self.LEDs[0].on()
        self.LEDs[1].on()
        self.LEDs[2].on()
    def AllOff(self):
        self.LEDs[0].off()
        self.LEDs[1].off()
        self.LEDs[2].off()     

A program to make use of the new class would be something like:

leds=TriLED(4,17,27)
while True:
    leds.AllOn()
    sleep(0.001)
    leds.AllOff()
    sleep(0.001) 

Of course, in a real TriLED application you might have methods for set patterns – rotate left, rotate right and so on, but the general idea is the same.

The CompositeDevice Class

The CompositeDevice class is designed to help with this general pattern of using a tuple of devices. You add the devices in the constructor and you can add them as unnamed members of a tuple or as named attributes. The difference is that named attributes make it easier to access the sub-devices from the outside world. If you specify a device as a positional parameter, then it is added to a tuple. If you specify a device as a named parameter then attributes with the same name are added. The only slight mystery is how do you access the tuple of devices? The answer is that the CompositeDevice class itself behaves like a tuple and you can access its contents in the usual way. It doesn’t achieve this by inheritance. Instead it overrides the __getitem__ magic method which is called when you use indexing.

For example:

compdev=CompositeDevice(LED(pin1),LED(pin2))
compdev[1].on()

turns on the LED on pin2.

Named parameters are easier to understand:

compdev=CompositeDevice(led1=LED(pin1),led2=LED(pin2))
compdev.led2.on()

You should use the first approach for devices that are used internally and the second if you want to allow outside access to the component devices.

Of course, if you are creating a custom class for some device then a better idea is to inherit from CompositeDevice rather than create an instance so that your custom class inherits the same behavior.



Last Updated ( Monday, 07 February 2022 )