Prototyping a Raspberry Pi robot idea with two emulators

While preparing for a workshop last week, my colleague Marc and I started brainstorming ideas. One of the ideas I came up with was to use the mini joystick on a Sense HAT (a sensor board add-on for the Raspberry Pi) to remotely control a robot using GPIO Zero’s remote pins feature. I soon started writing the code for it on my laptop. Then I realised I could actually prototype the whole idea without touching a Raspberry Pi, using a combination of the Sense HAT’s desktop emulator developed by Dave Jones, and GPIO Zero’s mockpin interface (erm… also developed by Dave Jones).

The first proof-of-concept demo was really simple: it was just a case of connecting the joystick events with robot actions, i.e. joystick up => robot forward, and so on:

from gpiozero import Robot
from sense_emu import SenseHat
from time import sleep

sense = SenseHat()
robot = Robot(left=(2, 3), right=(4, 5), pwm=False)

sense.stick.direction_up = robot.forward
sense.stick.direction_down = robot.backward
sense.stick.direction_left = robot.left
sense.stick.direction_right = robot.right
sense.stick.direction_middle = robot.stop

while True:
    print(tuple(robot.value))
    sleep(1)

Note: I’ve turned PWM off (used for variable motor speed) here because the motors are only being driven at full speed in this case, and using PWM with mockpin would add a few lines.

With gpiozero and sense_emu installed with pip, running this script with:

GPIOZERO_PIN_FACTORY=mock python3 sense_robot_mock.py

This opens up the Sense HAT emulator window and continuously prints the left and right motor speed values (1 is forward, -1 is backward, 0 is stopped). When you click the up/down/left/right joystick buttons, the motor values change accordingly, and when you click the middle button, it stops: (0, 0).

Now, to run this on a Raspberry Pi, I just made a few small adjustments:

from gpiozero import Robot
from gpiozero.pins.pigpio import PiGPIOFactory
from sense_hat import SenseHat
from signal import pause

factory = PiGPIOFactory(host='192.168.1.5')

sense = SenseHat()
robot = Robot(left=(2, 3), right=(4, 5), pin_factory=factory)

sense.stick.direction_up = robot.forward
sense.stick.direction_down = robot.backward
sense.stick.direction_left = robot.left
sense.stick.direction_right = robot.right
sense.stick.direction_middle = robot.stop

pause()

Because remote pins only works for controlling devices available in GPIO Zero, the Sense HAT interaction has to be done locally and the GPIO stuff has to be remote on another Pi. So I create a pin factory referring to the other Pi’s IP address, and create the robot using that pin factory. This time, of course, I’m using sense_hat not sense_emu but (by design) the library APIs are identical. And now I’m seeing the robot move around for real, so I use pause() just to keep the script running instead of printing the values in a loop.

While this implementation is fine, it doesn’t account for releasing the joystick (so when you press it up, it goes forward but when you release it, it doesn’t stop until you middle press the joystick which is ok but not ideal).

So I considered an alternative implementation. Like Python itself, GPIO Zero and the Sense HAT library provide a multiple programming paradigms, allowing users to choose between procedural and event-driven styles according to their needs. The previous example is event-driven, and while I’d consider procedural the less advanced option, in this case it gives us an advantage:

from gpiozero import Robot
from sense_emu import SenseHat

sense = SenseHat()
robot = Robot(left=(2, 3), right=(4, 5), pwm=False)

while True:
    event = sense.stick.wait_for_event()
    if event.action in ('pressed', 'held'):
        if event.direction == 'up':
            robot.forward()
        elif event.direction == 'down':
            robot.backward()
        elif event.direction == 'left':
            robot.left()
        elif event.direction == 'right':
            robot.right()
    else:
        robot.stop()
    print(tuple(robot.value))

Here we constantly check the joystick state is ‘pressed’ or ‘held’, then check the direction, and drive the robot accordingly. This leaves any ‘released’ state to stop the robot. Again, I print the value to see what’s going on.

And to run on the Pi itself:

from gpiozero import Robot
from gpiozero.pins.pigpio import PiGPIOFactory
from sense_hat import SenseHat

factory = PiGPIOFactory(host='192.168.1.5')

sense = SenseHat()
robot = Robot(left=(2, 3), right=(4, 5), pin_factory=factory)

while True:
    event = sense.stick.wait_for_event()
    if event.action in ('pressed', 'held'):
        if event.direction == 'up':
            robot.forward()
        elif event.direction == 'down':
            robot.backward()
        elif event.direction == 'left':
            robot.left()
        elif event.direction == 'right':
            robot.right()
    else:
        robot.stop()

Easy!

What’s new in GPIO Zero v1.4?

It’s been a while since the last GPIO Zero release, so it’s with great pleasure I announce v1.4 is here. Upgrade now on your Raspberry Pi:

sudo apt update
sudo apt install python-gpiozero python3-gpiozero

Or on your PC:

pip install gpiozero

Why on your PC?

  • Run Python code on your PC to remotely control a Raspberry Pi
  • Use a handy command-line tool to see the Raspberry Pi’s pinout
  • Use the mock pin interface to test your code

So what’s new?

In summary:

Read on for details!

Pin factories and remote GPIO

Up to v1.0, everything was built directly on top of the RPi.GPIO library. Then in v1.1, my co-author Dave created a pin class abstraction which allowed us to implement pin interaction in any given library, so people could select a pin library to be used, and we added support for RPIO and Dave even wrote a pure Python one called NativePin. In v1.2 we added support for the pigpio library, which allows remote connections. This meant you could use GPIO Zero to control one Raspberry Pi’s pins from another, or even install GPIO Zero on a PC and control a Pi’s pins remotely.

History

Remote GPIO Support in v1.2 was sketchy. It improved in v1.3, but in v1.4 we have come up with a much neater mechanism for selecting and re-selecting pin libraries, and we’ve stabilised the API. We’ve also introduced documentation for configuring remote GPIO on Windows, Mac, Linux and Raspberry Pi, and added a section for recipes specifically showcasing remote GPIO use cases.

New pin factory syntax

Previously, there were two methods of selecting a pin library to use: by setting environment variables, or by using Pin objects in the code. Both of these methods have changed:

  • Environment variables no longer require you specify the pin class name, just the library name in lowercase:
    • Old: GPIOZERO_PIN_FACTORY=PiGPIOPin
    • New: GPIOZERO_PIN_FACTORY=pigpio
  • Pin factories are now used rather than pin objects:
    • Old:
      from gpiozero import LED
      from gpiozero.pins.pigpiod import PiGPIOPin
      led = LED(PiGPIOPin(17, host='192.168.1.5'))
    • New:
      from gpiozero import LED
      from gpiozero.pins.pigpio import PiGPIOFactory
      factory = PiGPIOFactory(host='192.168.1.5')
      led = LED(17, pin_factory=factory)

While this example is one extra line, it does make it easier in many more cases, and when you’re using a class which doesn’t take a pin parameter, like a HAT with pre-defined pin numbers, you couldn’t previously change the pin library in the constructor. Now you can:

from gpiozero import LED
from gpiozero.pins.pigpio import PiGPIOFactory

factory2 = PiGPIOFactory(host='192.168.1.2')
factory3 = PiGPIOFactory(host='192.168.1.3')

local_hat = TrafficHat()
remote_hat2 = TrafficHat(pin_factory=factory2)
remote_hat3 = TrafficHat(pin_factory=factory3)

The flow for a device electing a pin factory is as follows:

Read more in the pins API documentation.

This opens up a lot of possibilities. Check out the remote recipes for some inspiration (and feel free to suggest some more!). I’m now going to be shouting much more about remote pins – it could be a real game changer. I wrote an article in The MagPi #60 introducing people to remote GPIO. You can buy or download the full issue, or read this article this PDF from the magazine

Mock pin

Another pin factory we provide is called mock. Dave created this to make it possible to test the GPIO Zero code base with a test suite. It just uses MockPin to simulate pins going high and low and having mock device objects reacting to each other as they should. It’s also handy for testing your code out without using a Raspberry Pi, or if you don’t have all the components you need handy. Simply launch a Python shell or editor with mock as your pin factory, and you can test that it behaves as it should:

$ GPIOZERO_PIN_FACTORY=mock python3
>>> from gpiozero import LED
>>> led = LED(22)
>>> led.blink()
>>> led.value
True
>>> led.value
False

You can even tell pins to change state (e.g. to simulate a button being pressed) by accessing an object’s pin property:

>>> from gpiozero import LED
>>> led = LED(22)
>>> button = Button(23)
>>> led.source = button.values
>>> led.value
False
>>> button.pin.drive_low()
>>> led.value
True

pinout command-line tool

There’s a pi_info function in GPIO Zero we use to determine which Pi model you’re on to give appropriate warnings when you’re trying to do stuff on, say, pins that don’t exist on your Pi. I suggested we add some pretty-print for the output of this function, particularly the pin header layout, and one thing led to another, Dave created ASCII art to show the Pi along with the information. A guy named Steward Adcock joined our sprint table at PyConUK last year and asked if there was something easy he could dip into. I suggested producing a command-line tool for pinout and he helped get it started. Et voila:

It works on any Pi model, and gives you the correct pinout for the Pi you’re on. Run it on your Pi:

$ pinout

You can even use it on your PC – just use an environment variable to use MockPin, and (optionally) provide a Pi revision (otherwise the default is a Pi 3):

$ GPIOZERO_PIN_FACTORY=mock pinout -r 9000c1

The great thing about pinout is that anyone can use it – regardless of whether they’re using GPIO Zero or Python, it’s just a stand-alone tool. Again, this was featured in The MagPi #60:

STATUS

Rachel designed two boards for The Pi Hut, which go on sale soon: STATUS, and STATUS Zero. They’re general purpose status indicators you can use for all sorts of projects.

STATUS is a HAT-sized board with five strips, each containing a red/green LED pair, and a button. There’s also a label space on the strip for you to write in what it’s monitoring. STATUS Zero is similar, but Zero-sized, and just contains three strips (without buttons). See an early prototype of the Zero.

Here’s an example using our internal device PingServer to show who’s home, based on pinging IP addresses of known devices:

from gpiozero import PingServer, StatusZero
from gpiozero.tools import negated

status = StatusZero('mum', 'dad', 'alice')

statuses = {
    PingServer('192.168.1.5'): status.mum,
    PingServer('192.168.1.6'): status.dad,
    PingServer('192.168.1.7'): status.alice,
}

for server, leds in statuses.items():
    leds.green.source = server.values
    leds.green.source_delay = 60
    leds.red.source = negated(leds.green.values)

This was really easy to implement, as our LEDBoard class can be constructed with nested LEDBoards, which can (optionally) be given names. An example implementation of a similar two-strip board would be:

strip = LEDBoard(
    one=LEDBoard(red=14, green=15),
    two=LEDBoard(red=17, green=18),
)

More examples of LEDBoard use are now included in the advanced recipes page.

Source/values

This is nothing new – but we added a source/values documentation page, explaining how the feature works and how you can use it in your projects. A quick recap:

led.source = button.values

This is a simple approach to connecting devices together using a declarative style of programming. In one line, we declare that the LED should get its values from the button: i.e. when the button is pressed, the LED should be on. Rather than write a while loop, constantly checking the button state and setting the LED state, we just tell one to follow the other. We also provide a set of source tools for applying common operators to process values in between devices, and show how you can write your own tools.

Since the last release, Martin O’Hanlon created a zero-boilerplate Python library for allowing users to control things on their Raspberry Pi remotely using their Android device: it’s called BlueDot. The API is very similar to GPIO Zero, and it even incorporates the value/values properties, which means you can hook it up to GPIO devices easily:

from bluedot import BlueDot
from gpiozero import LED

bd = BlueDot()
led = LED(17)

led.source = bd.values

i.e. the LED is lit when the blue dot is pressed

We even included a couple of BlueDot examples in our recipes.

What’s next?

In v1.5 we’re looking to add support for more GPIO devices, but particularly to introduce I2C support which will give us access to a lot more devices, and provide a seamless step-up from simple GPIO to more advanced projects using I2C sensors and such, without having to learn a new low-level library, just sticking with the nice high-level GPIO Zero API. This will also allow us to utilise the pin factory concept with I2C expander chips, meaning you’ll easily be able to add more GPIO pins to your Pi. We also plan to add support for SPI output devices and a wider variety of motors and robots.

We have plenty more on the roadmap, there are plenty of issues to work through, and some of it will be later rather than sooner, so I can’t be certain what will make the next release, but we will keep working on it and hopefully I’ll have some great new stuff to write about for v1.5.

Thanks as always to Dave Jones for the great effort he’s put into the library. Thanks also to Steward Adcock for contributing towards the pinout command-line tool.

Python and Raspberry Pi talk at FOSDEM

Earlier this month, I spoke on the Python track at FOSDEM 2017. My talk introduced the Raspberry Pi as a tool for physical computing and IoT to Python programmers in the free & open-source software community.

I talked about the Raspberry Pi Foundation’s mission, our education programmes, introduced the GPIO pinout, HATs, GPIO Zero, Remote GPIO, Picamera, Energenie, Sense HAT, Astro Pi and more. You can view the slides, and watch the video here:

What’s new in GPIO Zero v1.3?

One year ago today, I started the GPIO Zero project. We now have a core team of three (Dave Jones, Andrew Scheller and me). There have been 587 commits, we’ve released four major versions, and published a book. The library has great coverage of GPIO devices, and contains features I never even dreamed of. In the last year I’ve delivered workshops and given talks about it at conferences and events around the UK, the US and even in Russia. It’s being used in Raspberry Pi’s learning resources, and teacher training and by hobbyists around the world. Read on to find out what’s new in the latest release.

GPIO Zero v1.3 is out now! Install it (or upgrade) with:

sudo apt-get update
sudo apt-get install python3-gpiozero python-gpiozero

What does this release bring?

  • New ButtonBoard class
  • New Servo and AngularServo classes
  • New CPUTemperature class
  • Improved remote GPIO support
  • Plenty of behind-the-scenes changes
  • Lots of new recipes

ButtonBoard

The new ButtonBoard class is a composite device representing multiple buttons. The idea being you can pass the value set from a collection of buttons into another collection of the same size: e.g. matching buttons to LEDs. You can also process the value set to get the information you want, for example the number of buttons pressed; any pressed, all pressed, proportion pressed or more. We’re currently discussion options for documenting these examples and possibly extending the features of this class in Issue #425.

Servo

The long-discussed Servo classes have now arrived! Servo allows you to move a servo between its minimum, maximum and mid-point positions:

from gpiozero import Servo
from time import sleep

servo = Servo(17)
while True:
    servo.min()
    sleep(1)
    servo.mid()
    sleep(1)
    servo.max()
    sleep(1)

AngularServo allows you to move a servo to specific angles:

from gpiozero import Servo
from time import sleep

s = AngularServo(17, min_angle=-45, max_angle=45)

s.angle = 0.0
sleep(1)
s.angle = 15
sleep(1)
s.angle = 45
sleep(1)
s.angle = -45

We’re currently discussing what else should be added to the servo classes for different use cases.

CPUTemperature

We introduced some internal devices which act like regular GPIO Zero classes. CPUTemperature allows you to access the Pi’s CPU temperature, and use it to control other devices:

from gpiozero import LEDBarGraph, CPUTemperature

temp = CPUTemperature(min_temp=50, max_temp=90)
graph = LEDBarGraph(5, 6, 13, 19, 25, pwm=True)
graph.source = temp.values

Now the LEDs will light up according to the temperature: at 50 degrees all the LEDs will be off; at 90 degrees they’ll all be on; at 70 degrees half will be on; and anything in-between. There’s also a TimeOfDay class provided but it’s not working properly at the moment so I’ll cover that in the next release post.

Remote GPIO support

I talked about this last time. It’s really exciting. It was amazing to see this working in v1.2 but support wasn’t really very good, so its use was limited. However, significant work has gone into remote GPIO support since then and it’s going to be really powerful. Thanks to a pigpio, a C library from GitHub user joan2937, we can set up GPIO Zero devices connected to pins on Pis over a network. Easily.

Usually, you’d set up an LED like so:

from gpiozero import LED

led = LED(17)

led.blink()

Where the 17 refers to pin 17 on the Pi you’re executing the code. You can also do this:

from gpiozero import LED
from gpiozero.pins import PiGPIOPin

pin = PiGPIOPin(17, host='192.168.1.4')
led = LED(pin)

led.blink()

Here, rather than simply specifying the number 17, we’ve provided a reference to a pin on another Pi on the network. This means you can remotely control the LED connected to that other Pi.

However, an easier method is to supply the Pi’s IP address in an environment variable. Before running the python shell, IPython, IDLE or whatever you’re using, simply set the PIGPIO_ADDR environment variable:

$ PIGPIO_ADDR=192.168.1.4 python my_script.py

to run a file, or:

$ PIGPIO_ADDR=192.168.1.4 ipython3

to run the IPython shell, for example. Each of these will be set up so that when you type led = LED(17), it uses pin 17 on that remote Pi. Note: you’ll need to enable Remote GPIO, start the pigpio daemon, and install the pigpio python client on your host machine. See full instructions in this gist.

Of course, you can mix these methods and use a default pin factory but also specify pins on other Pis.

To set this up, you need the pigpio Python library installed on the device you’re running the code, and the pigpio daemon running on the remote Pi (and remote GPIO enabled in raspi-config). Read the pins documentation for more info.

Did I mention you can run this on any PC, not just on a Pi? That’s right. You can install GPIO Zero on your laptop and run GPIO Zero code which communicates with a Pi on the network. The Python code is running on your laptop, which communicates with the pigpio daemon using sockets. The daemon does all the GPIO stuff. I’ve seen this working on Linux, Mac and Windows. Admittedly, we need to provide much more solid instructions for getting this working if we want it to be seen as a viable solution for schools, Code Clubs and such. But it’s a start. Please give feedback if you’re using it (if you have any issues or not – we’d love to know). There’s also some interesting stuff around. If you know your way around installing with pip on Windows and Mac, we could use your help! See Issue #434.

What’s next?

We didn’t complete all the things we had on the list for this release, but we decided to push it out anyway, and leave the rest to the next release. As well as lots of minor improvements, we intend to add I2C support, including I2C expander chips (and probably SPI expander chips while we’re at it), which will be really handy. You’ll be able to set up an expander chip, and index it to refer to each pin, and create regular device objects on those pins, something like this:

from gpiozero import IOExtender, LED

ext = IOExtender()
led = LED(ext[0])

led.on()

Then there’s temperature sensors, different types of motors, servos and more. Feature requests welcome on GitHub! We’re also aiming to establish a release schedule for future releases.

Thanks

Again, thanks to Dave Jones and Andrew Scheller for all their hard work in the last release, and to everyone who provided useful feedback on GitHub. Keep it coming!

PyConUK

Dave and I will be giving a talk together on GPIO Zero at PyConUK this week. The video will be available shortly after the conference. We’re also running a sprint on GPIO Zero, welcoming new developers to the library. Come and have a go if you’re available, and we’ll show you where to start.

Simple Electronics with GPIO Zero book

Today the MagPi team released a new publication: Simple Electronics with GPIO Zero.

Essentials-07-GPIO-ZERO_Flat_Cover

This 100-page book takes you from the basics, like lighting an LED, all the way to building projects like an Internet radio using the GPIO Zero Python library.

This book is available as a free PDF, but you can also pay to get it for your iPad or Android device with the MagPi app. Soon it will also be released in print. It’s now available in print. All proceeds go towards the Raspberry Pi Foundation’s education programmes.

GPIOZeroSpread

I’ve been amazed with how the GPIO Zero project has grown. There have been three major releases (a fourth due later this year), and it has been featured in The MagPi many times, and in three Kickstarter projects: the RasPiO Pro HAT; Analog Zero; and GPIO Zero Ruler.

Also check out the GPIO Zero documentation, the Physical Computing with Python resource, and GPIO Zero on GitHub.