We’re still experimenting with our API design, trying to find an abstraction for GPIO that works for both on-board GPIO pins and pins of GPIO expansion devices like our MCP23017 board. We realise that this creates a bit of churn in the API and that changes have been landing in the GitHub repository without warning.

To improve the situation we’re changing our development process so that big API changes are done in public and accepted into the API project after a review period. We’re using GitHub’s pull requests to manage the process.

In future, when writing a new API feature or changing an existing API we will make a personal fork of the quick2wire-python-api repository. When the change is ready to be integrated into the API we will issue a pull request, and the change will appear in the issue tracker. Other team members will then review the code, suggest improvements or raise objections. And it’s not limited to the team members either. If you use the API and are interested in upcoming changes you can see what we’re changing and make comments too. Or, fork the library yourself and send us pull requests.

Here’s an example: https://github.com/quick2wire/quick2wire-python-api/pull/22

We hope this will help avoid unpleasant surprises and make the evolution of the software more transparent to folks using it.

When you put together one of our interface boards you’ll see that it breaks out the GPIO pins into their own header and labels them 0 to 7. Our GPIO library numbered Pins 0..23, after the header pins on the Raspberry Pi. That’s pretty awkward in practice so we’re experimenting with ways to make it easier to write programs that work with broken out GPIO headers.

Our current approach is to replace the Pin type with two pin classes: GPIOPIn and HeaderPin. A GPIOPin is created with a pin number from 0 to 7 that identifies the pin on the interface board. A HeaderPin is created with a pin number from 0 to 23 that identifies the pin on the Pi’s header.

For example:

#!/usr/bin/python3

import sys
from time import sleep
from quick2wire.gpio import GPIOPin, In, Out

led = GPIOPin(1, direction=Out)
button = GPIOPin(0, direction=In)

while True:
      led.value = button.value
      sleep(0.1)

To ensure that existing code keeps working, we define Pin as a synonym of HeaderPin. But when we’ve found an approach that works well we’ll deprecate Pin and eventually remove it from the API.

Most of our users started out with revision 1 of the Raspberry Pi, but a growing number have revision 2 boards, and some have both.

A few things changed in revision 2, including GPIO pin numbering and the I2C interface.

Until yesterday our GPIO library did not handle header pin 13 correctly on revision 2 RasPis, and the I2C library required you to specify that you wanted to use I2C bus  1.

We’ve updated the library and those problems should now be a thing of the past.

If you are using the quick2wire-python-api libraries, you will need to pull the new versions from github. If you got to the directory in which you cloned the library, just run git pull from the command line and you should get the new version of the code.

25. November 2012 · 16 comments · Categories: API, GPIO

Quick2Wire’s Python GPIO library allows your program to be notified when something interesting has happened to a device attached to a GPIO pin. Instead of repeatedly checking the pin, an application can wait for the pin to trigger an event as soon as its state changes. Interrupt-driven GPIO also works with the Python epoll library, so you can integrate your GPIO code with other core system objects such as network sockets and files.

Polling for changes

Traditional GPIO programming works by polling the state of the pins. You write a “busy loop”, continually checking the status of a pin until you see the signal you want.

pin = pins.pin(0, direction=In)
while True:
     if pin.value == 1:
         handle_pin_1()

This code is fine for dedicated devices, such as an Arduino, but is not appropriate for the Pi because it monopolizes the Pi’s processor. Linux will struggle to give enough processor time to other tasks, such as serving web pages or reading from the command line. Swapping between tasks will result in degraded performance for the entire system.

An obvious solution is to check the value of the pin less frequently:

pin = pins.pin(1, direction=In) 
while True: 
    while pin.value == 0: # Are we there yet? 
        sleep(1) # Take a quick nap 
        handle_pin_1()

This improves the responsiveness of the Pi, but it’s still problematic. How long should it sleep? Too little and there will still be performance problems, too long and the application might not respond promptly to a pin event. More interestingly, what if you’re on waiting for more than one input? You’ll need to expand your loop to check each pin in turn:

pin1 = pins.pin(0, direction=In) 
pin2 = pins.pin(1, direction=In) 
while True: 
    if pin1.value == 1 
        handle_pin_1() 
    if pin2.value == 0 
        handle_pin_2() 
    sleep(1)

There’s a better solution, but it requires a new programming model:

Using Python’s epoll API with GPIO

GPIO interrupts allow an application to use epoll to wait for a GPIO events instead of continually checking the value of the pin in a busy loop  Epoll is a Linux facility for efficiently handling event notifications. Quick2Wire’s GPIO Pins integrate into Python’s epoll library with a little extra code:

import select 
pin1 = pins.pin(0, direction=In, interrupt=Rising)
pin2 = pins.pin(1, direction=In, interrupt=Falling)

epoll = select.epoll() 
epoll.register(pin1, select.EPOLLIN | select.EPOLLET) 
epoll.register(pin2, select.EPOLLIN | select.EPOLLET) 
with pin1, pin2
    while True: 
        events = epoll.poll() 
        for fileno, event in events: 
            if fileno == pin1.fileno(): 
                handle_pin_1() 
            if fileno == pin2.fileno(): 
                handle_pin_2()

This might look confusing if you aren’t used to epoll, so we’ll step through the code:

pin1 = pins.pin(0, direction=In, interrupt=Rising)
pin2 = pins.pin(1, direction=In, interrupt=Falling)

Notice the new parameter to the Pin constructor. Adding this parameter enables interrupts on the pin, which will be used to wake your application. The pin can be configured to wake when high (‘Rising’), low (‘Falling’) or either (‘Both’).

epoll = select.epoll()
epoll.register(pin1, select.EPOLLIN | select.EPOLLET) 
epoll.register(pin2, select.EPOLLIN | select.EPOLLET)

Here we create an epoll object and register our pins. The flags tell epoll to wake up whenever there is something to read, but only on “edge triggers”. Unlike the older Linux functions select and poll, epoll can be configured to wake up with either “level triggers” or “edge triggers”. Level triggers (the default) will cause epoll.poll() to return whenever the condition is true. Edge triggers will cause epoll.poll() to return only once after a transition from low-to-high or high-to-low.

while True: 
    events = epoll.poll() 
    for fileno, event in events: 
        ...

Here’s our main loop. Each call to epoll.poll() returns a list of events, one event for each of the registered objects that have been triggered since the previous call. Each event is a pair of the file number for the object being triggered and flags describing the type of event that happened (for example EPOLLIN for an input).

Adding more input types

Python will allow you to register any file descriptor with epoll. Because “everything is a file” in Linux, we can, for example, listen for network sockets and the keyboard in the same loop as our pin.

sock = initialize_socket() 
pin = pins.pin(0, direction=In, interrupt=Both) 
pin.open()
epoll = select.epoll()
epoll.register(sock, select.EPOLLIN)
epoll.register(pin, select.EPOLLIN | select.EPOLLET)
epoll.register(sys.stdin, select.EPOLLIN) 
while True: 
    events = epoll.poll() 
    for fileno, event in events: 
        if fileno == sys.stdin.fileno(): 
            # read from the keyboard (or stdin) 
        if fileno == pin.fileno(): 
            # read from the pin 
        if fileno == sock.fileno(): 
            # read from the socket

For another example of GPIO and epoll, take a look at the Quick2Wire Python epoll example in GitHub.

While we’re waiting for the first interface boards to arrive we’ve been checking out our libraries against the new Raspbian distro.

The latest Raspbian kernel supports I2C and SPI, and today we got all our loopback tests passing. This means we have working Python libraries on Raspbian for GPIO, I2C and SPI.

Raspbian is now the official recommended kernel, so we’re fast approaching the point where a new Pi user can plug in our hardware, install our libraries, run an example and expect everything to work.

At the moment the Python GPIO library needs our gpio-admin library as well as the Python code. Soon we’ll package the two together as a .deb file, which will make things even easier. The gpio-admin and Quick2Wire Python libraries are on github so if you feel like experimenting, clone away and let us know how you get on! The libraries have examples and test code included.

If you want to try out the SPI code, you’ll need to upgrade to the latest Raspbian firmware. The kernel in the current Raspbian image (2012-07-15-wheezy-raspbian) supports I2C but not SPI; you can use Hexxeh’s firmware updater to get a later kernel, which supports both.

There are a couple of other things you’ll need to do: uncomment the two lines in /etc/modprobe.d/raspi-blacklist.conf which stop needed modules from loading, and add a line i2c-dev to /etc/modules to ensure that the relevant i2c module is loaded on boot-up.

Your raspi-blacklist.conf should then look like this:

# don't blacklist spi and i2c by default
# blacklist spi-bcm2708
# blacklist i2c-bcm2708

and  /etc/modules should look like this:

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.
snd-bcm2835
i2c-dev

PS Things are moving fast. If you want to keep up-to-date with what’s happening here at Quick2Wire, sign up for our free newsletter. Just fill in your email at the top of the page and click sign up. We won’t spam you, and you can unsubscribe at any time.

 

25. July 2012 · 4 comments · Categories: API, I2C

Our APIs are still immature and in heavy development. As we and others use them we discover aspects that are confusing or hard to use. We are correcting these problems now even though it breaks some code that uses the API, because there is currently very little code out there that depends on our API. Eventually we will publish a stable version but until then be aware that the API in our repository may change. We’ll announce any changes on this blog.

Users of our I2C API reported a few design flaws. The name of the I2CBus class was confusing: because there is only one I2C bus on the Pi, the name seemed to indicate that there could only be one instance of I2CBus active at any time. In fact it the class performs the master side of the I2C protocol on the bus. Many instances can be active at any time in different processes or even in the same process. So we have renamed it to I2CMaster.

The functions read, write and write_bytes in the quick2wire.i2c module were also confusingly named. They didn’t actually read or write but returned I/O actions that are performed by the I2CMaster.transaction method. We’ve renamed them to reading, writing and writing_bytes to make it more obvious that they don’t perform the I/O themselves.

So the I2C example program now looks like:

#!/usr/bin/env python3

from quick2wire.i2c import I2CMaster, writing_bytes, reading
import time

address = 0x20
iodir_register = 0x00
gpio_register = 0x09

with I2CMaster() as master:    
    master.transaction(
        writing_bytes(address, iodir_register, 0xFF))
	
    while True:    
        read_results = master.transaction(
            writing_bytes(address, gpio_register),
            reading(address, 1))
        
        gpio_state = read_results[0][0]
        
        print("%02x" % gpio_state)
        
        time.sleep(1)

We’ve done the same in the SPI module as well to keep the two consistent.

There are likely to be further changes as we write classes to control our boards and other peripherals. But the APIs will settle down before long. We’ll keep you informed of any changes on this blog.

We are writing our APIs for Python 3. Python 3 a relatively new version of the language and is not entirely backwards compatible with Python 2. A number of libraries have not yet been ported to this new version. But we believe Python 3 is a good choice for programming our educational kits because it removes many confusing aspects of Python 2.

Why Python 3?

We selected Python 3 for a number of reasons:

  1. We’re developing educational kits and Python 3 is the Python version selected for the Raspberry Pi educational release.
  2. Python 3 makes a clear distinction between byte buffers and text strings, which makes doing I/O, working with raw data and working with text strings easier than in Python 2. Python 2 does have a bytes function to create byte buffers that has the same syntax as that of Python 3. However, it has different and confusing behaviour. For example, in Python 3 it acts as you’d expect:
    Python 3.1.2 (release31-maint, Dec  9 2011, 20:50:50) 
    [GCC 4.4.5] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> bytes([1,2,3]) == b'x01x02x03'
    True

    But in Python 2:

    Python 2.7.0+ (r27:82500, May  9 2011, 20:29:29) 
    [GCC 4.4.5] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> bytes([1,2,3]) == b'x01x02x03'
    False
    >>> bytes([1,2,3]) == b'x5bx31x2cx20x32x2cx20x33x5d'
    True

    Can you work out why?

  3. Python 3 has only one, universal, type system/object model, and so avoids confusing errors that can happen in Python 2 when you accidentally forget to declare that your class extends object and then try to use an API that uses the binding protocol or metaclasses. If that last sentence sounds like total gibberish, that demonstrates why we’re using Python 3! We shouldn’t have to expose those advanced language features to new programmers but it’s easy to bump into them by accident in Python 2.
  4. Many other small quirks and annoyances that remain in Python 2 to ensure backward compatability have been removed, making the language and standard library easier to use.

Unfortunately some popular libraries have not yet been ported to Python 3, including (at the time of writing):

Happily, more libraries are becoming available as time goes by. Those above may have been ported by the time you read this.

Using Python 3 on the Raspberry Pi

Python 3 is not included in the default Raspberry Pi disk image at the moment. You’ll need to install it with the command:

sudo apt-get install python3

Then you use the python3 command to run Python 3 scripts:

python3 my-script.py

To make an executable Python script run with Python 3, the script should start with the following line:

#!/usr/bin/env python3

For example, the following script should report that it is running Python 3:

#!/usr/bin/env python3

import sys

print(sys.version)

The devices we’re building at Quick2Wire will connect to the Raspberry Pi over its I2C bus. Linux lets user-level programs control I2C devices via the device file /dev/i2c-0. However, the I2C bus protocol doesn’t fit well to the Linux file-like device model because the protocol is transactional. The Linux API to perform I2C transactions is awkward to use from Python so we are writing APIs for communicating with our devices, and any other device that connects to the Pi by I2C. You can download the API from our GitHub repository.

More »