Blog

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.

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). Here’s an early prototype of the Zero:

https://twitter.com/RachelRayns/status/842413266185916416

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.

Google OnHub: the best router I’ve ever owned

My review of the Google OnHub router, and some tips on setting up advanced options like port forwarding.

While in the US for a trip in October, I decided to pick up a Google OnHub router, which is only available in America, and has since been superseded by Google WiFi, also only available in America. There are two different routers available with this brand name – one made by ASUS, the other by TP-Link. I have the TP-Link model.

The OnHub is the best router I’ve ever owned. It’s channel-hopping and will seamlessly switch you between 2.4GHz and 5GHz with a single SSID. My desktop is two floors up from the router and its WiFi connection is solid (just as good as wired), which is really handy. The OnHub’s a little unique, in that there’s no web interface to its configuration – it’s all done within an app. This has its positives and negatives, and had made a lot of nerdy friends of mine shriek at the idea, but it certainly does make things easy. You can add other users to be a “manager” of the hub, which allows them to control it from the app too.

It’s important to note that the OnHub is only a router, not a modem. It only has an Ethernet port for connection in – no DSL/ADSL/VDSL, so you have to use a modem, or another router, plugged into the wall, then connect to that with an Ethernet cable. You can use it to connect directly to your ISP (still via the modem), but that doesn’t seem to be possible at the point of setup. It assumes you have already connected it to the internet at this point – it expects a working internet connection provided by your modem. But if you want to reconfigure it later, you can (that was an important step for me – port forwarding wasn’t possible until I reconfigured).

You start by installing the app, available for Android and iOS (the app is now Google WiFi), and you tell it you want to set up a new WiFi point. The whole setup is done from the app: you connect wirelessly to the hub, choose your SSID name and password and that’s about it. You then manage the hub from the app – via the internet, not via the LAN. So you can manage it remotely using your phone, but you cannot plug an Ethernet cable in and configure it in a browser. It’s a shame configuration is limited to the app – a browser-based version would be really useful – over the internet and locally. The app can tell you the status of your network while you’re away from home too:

I’ve always preferred a wired connection to wireless, as it tends to be faster, and way more reliable. But with the OnHub I’m more than happy not to have had to run Ethernet cables all over my house, because the speed and reliability are spot on – it wouldn’t be worth cabling. It’s also handy as I use a lot of Raspberry Pis day-to-day, and the Pi 3 and the new Pi Zero W both have decent 2.4GHz WiFi so I can easily connect to them without worrying about wiring them all up. This is also the perfect time to mention that the app shows you all the devices on your network, and their IP address (all in the 192.168.86.x range), which makes it easy to connect to a headless Pi I just plugged in:

It’s really easy to reserve IPs for your devices:

The one difficulty I had was with port forwarding. Now, in theory, this is dead simple. The app has a “port forwarding” option in the menu, and it’s completely intuitive. You have to reserve an IP for your device in order to set up port forwarding, but that’s no problem. Any rules I set up just didn’t seem to work, and I didn’t understand why.

Of course, searching online for help only gave me simple instructions using the app – exactly what I’d done. But I figured something was missing. Originally, I’d been using the Plusnet router from my ISP as the modem, and just connecting the OnHub to it directly, but of course this meant that the PlusNet router was handling port forwarding requests. I reconfigured this router into its “modem” mode – but still no luck. I replaced it with a simpler modem, the Netgear DM200. Annoyingly the DM200 had both modem-only and router+modem modes too, so I switched to modem mode and simply couldn’t get it to work.

It was suggested to me (thanks Dave) to see if the OnHub had a PPPoE mode, as that would allow me to configure it with my ISP’s login credentials. Setting up PPPoE mode wasn’t easy, as you have to take the OnHub offline to do it. But the key was that previously, the hub’s own WAN IP was a local IP, given by the Netgear modem, but that in order to deal with outside requests, needed to be an external IP. I needed to give it my ISP’s info.

I tried this, and ended up having to restart the setup procedure on both devices multiple times, but eventually, managed to get it sorted – and port forwarding worked at last! I then had a working SSH gateway, and online tortoise cam:

The app also lets you perform a speed test, both on your internet connection and WiFi speed, and you can see historical data on your average speed each day:

The OnHub only has a single Ethernet port for connecting another device, so if you need to connect more than one wired device, you’ll need a hub.

The OnHub is quite pricey, but you get one hell of a router. I paid about $125 for it. You can’t really buy them any more, but Google WiFi costs about the same. It’s not clear to me what the differences are. People say the WiFi is superior, but they seem to recommend you get 3 of them and mesh them – so 3x the price of the OnHub… but perhaps this isn’t absolutely necessary. If it’s anything like the OnHub then one will reach all corners of a regular sized house just fine.

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:

Christmas Coding Challenge – Uno

During this Christmas break I decided to have a go at implementing the card game Uno in Python. It’s a fun and simple game for all ages, and the rules are easy to pick up and follow – but there’s quite a challenge in implementing the rules in a program! I spent a few hours over a few evenings working on it and now have a completed version, and wanted to share some of the interesting bits of the code.

Uno

Uno is played with a bespoke card deck (not a normal deck of playing cards). Uno cards have a colour and a card type (a number or a symbol). There are four regular colour cards, and there are special cards which are black.

Each player is dealt a hand of 7 cards, and they take turns to play a card, trying to become the first player to get rid of all their cards. A player can only play a card if it has the same colour or card type as the last played card. If they cannot play a regular card but have a special black card, this can be played instead. Some cards have consequences, such as the next player having to pick up extra cards, players skipping turns or reversing order of play.

Unit tests

I used a test-driven development (TDD) approach to further my progress in building up the game from simple isolated card logic all the way to simulating gameplay in a multi-player game. I used pytest for its ability to verify exceptions were raised, but otherwise simply used Python’s built-in assert statement.

An Uno Card

I started by writing some tests to check errors were thrown if you tried to create an invalid card, then proceeded to creating valid cards. The card was implemented as an UnoCard class, whose objects stored a colour and a card type.

Card rules

As well as validation on init, I added a method to determine whether a given card was playable on another card. The card playing rules are quite straightforward: you can place a card o

The deck

I started building up an UnoGame class, where a deck of UnoCard instances was created. The deck needed to contain a complete set of Uno cards, which I determined from this image on Wikipedia:

So it turns out there are digit cards 0-9 (and then 1-9 repeated) for each of the four colours, and two of each special card in each colour. There are also four of each of the two types of black cards.

I started by creating lists defining the colours and card types, and initially created the deck by combining these lists in a number of for-loops. However, I later refactored this to use product, repeat and chain from itertools in the standard library, which made it much neater:

color_cards = product(COLORS, COLOR_CARD_TYPES)
black_cards = product(repeat('black', 4), BLACK_CARD_TYPES)
all_cards = chain(color_cards, black_cards)
deck = [UnoCard(color, card_type) for color, card_type in all_cards]

The UnoGame’s deck was then simply a list containing UnoCard objects.

Gameplay

In order for there to be a game, there needed to be players. I made UnoGame initialise with a given number of players. This meant a new UnoPlayer class. Each player would be dealt a hand of 7 cards from the shuffled deck. But first, I wrote tests and made sure that an UnoPlayer object could be created with 7 cards, and couldn’t be created without them.

Reverse

In order for gameplay to take place, I needed to work out how I would designate a player as being the currently active player (whose turn it is). My first thought was itertools.cycle – a handy tool for infinitely iterating over an iterable object like a list, and just starting back at the beginning of the list once all items have been exhausted. However, Uno has a reverse card, meaning the order of play can be reversed at any moment.

I thought it through, and decided to design a new ReversibleCycle class to implement something like a cycle which could be reversed, as specified by the Uno rules. It took me quite a while to figure this out exactly, and get all my tests passing, but I was quite happy with the implementation I ended up with. A tricky part was the edge-case of what happens if the first card drawn (not played by a player – just the starting card) is a reverse card. According to the rules, play will start with the player to the right of what would have been the first player (i.e. the last player). Otherwise, in normal circumstances, the first player will play first, naturally.

I implemented the class as an iterator, so you could loop over it or manually next() it. Example usage of my ReversibleCycle class:

>>> rc = ReversibleCycle(range(3))
>>> next(rc)
0
>>> next(rc)
1
>>> rc.reverse()
>>> next(rc)
0
>>> next(rc)
2

I wrote tests of the ReversibleCycle in an isolated fashion. It works with any iterable, and I simply created an attribute within my UnoGame class which referred to an instance of ReversibleCycle, where the iterable was a list of UnoPlayer objects. I used a property to make it easy to look up which was the current player at any given moment.

More gameplay

Back to testing the gameplay, now I had a working model for cycling through players, with the ability to reverse order of play once a reverse card was played, I tested that players could play cards only when it was their turn, and that they could only play valid cards. I then tested that players playing cards would cause the correct consequences (e.g. the next player picks up 2, the new current player is the right one if a skip/reverse/etc. card is played, and so on).

The winner

I began testing gameplay by allowing the deck not to be shuffled, so that I had a predictable, testable order of cards. Looking back, I should have used a random seed instead (thanks for the tip, Dave). I took this game all the way to a player being designated the winner and the game ending.

Automated play

I then proceeded to write some code, similar to my test code for the gameplay, which would automate play for all players, and end at some point declaring one of the players the winner. There was nothing clever about the way the players played – no strategy – I just determined whether they had any playable cards, and made them play the first playable one in their hand.

AI

The last thing I did was to take the automated play code and embed it inside an AIUnoGame class, allowing for a single player to be controlled by the user using text input on request. The playable player would be shown their hand each round, and asked which card they would like to play. All actions of other players would be displayed (e.g. “Player 2 plays Red 5”, “Player 4 picks up 2”), and the winner would be declared as before. Again, nothing smart in the AI, but that’s something I may add later.

GUI?

I had wondered when I started whether it would be feasible for me to create this as a GUI – a real visual playable game. I obviously wanted to start from a text-based interface, and get the logic of the game down before I worried about graphics, but again, it’s something I would like to look at next. I wonder if PyGame Zero or guizero would be suitable. Watch this space!

The code

You can find my code on GitHub.

All Things Open 2016 – Raspberry Pi for everyone!

I recently attended All Things Open, an open-source conference in Raleigh, North Carolina, run by Red Hat. I was invited by my friends at opensource.com – and it was a great opportunity to meet the team and some of the moderators, columnists and contributors.

Ben Nuttall on Twitter

ATO2016

I was asked to give a lightning talk, and I decided to speak about the Raspberry Pi Foundation’s mission: what we do, how we do it, and where we’re at now! Here’s the video of the talk (3m20s):

What is the Raspberry Pi Foundation? 10 million computers sold

The Raspberry Pi Foundation began with the purpose to find a way to get more students at Cambridge University to study Computer Science. A small, cheap Linux computer called the Raspberry Pi was created as the solution, and since the first product in 2012, educators, hobbyists, and industrial users have been creating amazing projects with it.

My slides are published on speakerdeck. I also wrote an article on the same subject for my column that month: Raspberry Pi continues to blaze new trails.

I also got the opportunity to visit Red Hat Towers, and meet their CEO Jim Whitehurst to talk about why I contribute to opensource.com. And for the conference social afterparty, they’d booked nerdcore artist MC Frontalot!

Ben Nuttall on Twitter

You have more stickers on your laptop than I’ve ever seen. Would you like a Vim mug?” #ATO2016

Ben Nuttall on Twitter

There’s no money in open-source

Thanks to the All Things Open and opensource.com teams, to Red Hat, to Jim Whitehurst, and to all the speakers at ATO!