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.
I2C Transactions
An I2C master – the Raspberry Pi in our case – communicates in transactions. In each transaction it takes control of the bus, performs a number of reads and writes, and then relinquishes control of the bus allowing other masters to use it. Reading or writing to /dev/i2c-0 will perform a transaction containing only a single read or write. This is fine for some simple devices but most I2C devices have more complex control protocols. For example, to read the register contents from an ITG3200 gyro, the I2C master must write the id of a register to the device and then read one or more bytes, all in one transaction.
To perform multiple I2C reads and/or writes in a single transaction you can’t use the read and write system calls. You must use ioctl.
You Low-Down, Dirty System Call You
The ioctl syscall (short for input/output control) is used to control I/O devices that do not fit into the Linux file I/O abstraction. It’s a very low-level “get out of jail free” card for the Linux API. Being so low-level, ioctl is not easy to use from a high-level language like Python.
The ioctl call can be passed any type of C data structure. The correct type depends on the device being controlled and the operation to be performed. In Python, this means constructing a C data structure with the ctypes module and then passing the numerical address of that structure to the ioctl function. It’s really fiddly to get right and if when you get it wrong you can crash your program or lock up your I2C hardware. Or both.
The Quick2Wire I2C API
To make it easier to use our I2C devices from Python we are writing high-level APIs that take care of the ioctl calls and provide a convenient, “pythonic” programming interface.
The quick2wire.i2c module will make it easy to perform I2C transactions, as shown below:
import quick2wire.i2c as i2c
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)
This program creates an I2CBus object to communicate with an MCP23008 port expander chip at address 0x20. It first puts all the pins into input mode by writing to the chip’s IODIR register. It then repeatedly reads the level of the chip’s pins as a single byte from its GPIO register and prints the result. The I2CBus.transaction method returns a list of byte buffers, one for each read operation performed in the transaction. In this case, we’ve only performed one read and only read one byte, so the GPIO state is the first and only byte of the first and only byte buffer returned from the transaction.
Our I2C bus API is included in our Quick2Wire Python API, which you can download from our GitHub repository.
Device Specific APIs
Now we have released our I2C bus API, we are writing classes that make it easy to control specific I2C devices. We’re working on a class to control the MCP23008 chip used in the example above. The following program controls 8 LEDs with an MCP23008 using this API instead of issuing I2C transactions directly. As you can see, although still a work in progress, the chip-specific API makes for simpler code.
import quick2wire.i2c as i2c
from quick2wire.mcp23008 import MCP23008
import time
with i2c.I2CMaster() as master:
ports = MCP23008(master)
ports.set_io_direction(0x00)
n = 0
while True:
ports.set_gpio(n)
n = (n + 1) % 256
time.sleep(0.25)
Where it makes sense, our device abstractions have a common programming interface so that it is easy to modify a hardware design without having to rewrite a lot of the control software. For instance, in the example above we are setting the state of all the GPIO pins of the MCP23008 port expander in a single write for efficiency. But we can also treat an MCP23008 object as an array of pins that have the same programming interface as our GPIO Pin class if it makes sense to do so.
Concurrency
Unfortunately the I2C ioctl is a blocking call: the operating system pauses the program until the transaction has finished. This is a problem if you need to react to events from devices on different connectors, service network connections, or use I2C in a graphical application. While performing I2C transactions, the program won’t be able to react to those other events. A network server will be unable to serve many clients and interactive apps will feel unresponsive & jittery.
The solution is for the program to start multiple processes or multiple threads of control in a single process, so that while one thread is blocked waiting for an I2C transaction to complete, others can process other events.
Multiprocessing and multithreading introduces a lot of complexity of its own. But we’ve ideas about how to address that, which we’ll write about in a future article.

Thanks! Been waiting for this, to take out the pain of using the ioctl..looking forward to your updates.
Do you have any example code for reading all of the byte buffers which are returned on each transaction ? I am trying to get a MCP9800 temperature sensor to read which returns 2 bytes via i2c
Here’s an example that reads two bytes from sequential registers of an MCP23008 chip.
https://github.com/quick2wire/quick2wire-python-api/blob/master/examples/i2c-multibyte-read
There was a bug in the way we copied data from C into Python that broke multibyte reads. Maybe that’s why you’re having difficulty. It’s fixed now. Make sure you pull the latest version of the code from GitHub.
Thank you for the update and the code sample. I have now got the sensor to return data and just need to format it now for the data logging project.
Do I also have to use a modified kernel eg Chris Boot’s http://www.bootc.net/archives/2012/07/15/raspberry-pi-3-2-23-kernel/ ?
Chris
The latest raspbian kernel supports I2C and SPI, so you can use that.
Thanks Romilly – I should have read http://quick2wire.com/2012/08/racing-with-raspbian/ of course.
Chris
Just wanted to say thanks for the awesome work! I was planning on developing my custom I2C module but yours already fills my needs, so thanks for sharing!
This library is great, now I won’t have to rig something up for i2c. As of this post, the API seems to have changed so the example given doesn’t work. I had to change “i2c.I2CBus” to “i2c.I2CMaster” and all calls to “write_bytes” to “writing_bytes”.
Thanks for pointing out the API changes. I’ve updated the post.
Hi,
Presumably this API would work with the mcp23017 as supplied with this kit
http://www.hobbytronics.co.uk/raspberry-pi/mcp23017-port-expander-board
What I don’t understand is how it address the 2 8bit ports, the examples you have given seem to address a single 8bit port only.
Sorry if this is a stupid question.
Andy
It’s not a stupid question.
The example relates to the MCP23017′s baby brother, the MCP23008, which has only one 8-bit port.
We’re currently developing a library for the MCP23017, which is a more powerful chip with a more complex API. There’s some code up on github already but this is test code which is not designed as a tutorial example. We’ll be working on the library over this coming week.
In the meantime, here’s a brief explanation of how to use the MCP23017.
The I2C code needs to read and write values using the appropriate registers on the MCP23017. These are described in the manufacturer’s data sheet ( http://ww1.microchip.com/downloads/en/devicedoc/21952b.pdf ) but that’s not the easiest document to understand.
Let’s assume you want to write a byte of data (0xAA, for example) on Pins 0-7 of the GPIOB pins on the chip. You’d need to
Here’s the code:
how is the status off the mcp23017 libary ?
The quick2wire-python-api project on GitHub includes support for the MCP23017 in the quick2wire.parts.mcp23017 module. There are examples of its use in the examples/ subdirectory and the module itself is documented with docstrings.
thanks completely spitted the repository except the parts part :$