We recently had a little fun working with a Raspberry Pi module while developing a line of sensors. Like all things open-source, documentation can be a little sparse, out of sync, inconsistent, and difficult to follow; it’s often organized as a reference as opposed to a lesson. Learning which documentation to trust, which to ignore, and how to cherry-pick the details one needs is a mental work-out to be sure.
The following looks more complicated than it really is. It’s a fairly simple step-by-step guide to getting I2C support working on the r-π module.
Linux Familiarity Helps
Having a long history with linux helps considerably in configuring a Raspberry Pi. In essence, it’s an ARM processor, for which exists an actively maintained linux port. Instructions as far as downloading raspbian, a derivative of the debian linux distribution, went remarkably smoothly.
Python, V2 or V3?
Python is new territory for J-Tech. C is the most commonly used language for embedded development, though we do stray into the C++ world from time to time. In this case, Python makes accessibility a little easier and, we hope, allows customers to develop applications more quickly than creating everything from scratch. Libraries can be released, and so on.
We chose Python 3, as it’s already released, and although there appears to be confusion about whether to go with V2 or V3, in our opinion the way forward is with the newer version.
I2C & SMB
Why all the fuss about Python V2 vs. V3? It didn’t seem all that important until connecting I2C devices. In the world of interfacing to serial devices, there is a standard called SMB, short for System Management Bus. This matters because SMB is a subset of I2C, and most hardware devices like ADCs are I2C compliant.
Bringing it all home, there is a great SMB library available for Python, V2 only. Since this was not known when development using V3 started, it felt (and still feels) like a regression to switch to V2 before continuing. The cost of continuing with V3 however, is imposing on users wishing to use these new sensors to make changes to their Python 3 installations.
If V3 is to be supported, a step-by-step guide to adding SMBus support to Python 3 is a must.
First, raspbian itself needs a little tweaking; out of the box, I2C support is present but disabled. This operation is documented elsewhere, but it’s easier on the reader if it’s all here.
I2C/SMB support is blacklisted in the raspbian kernel by default, so it needs to be enabled first. To enable it, open a terminal window and do this:
...$ cd /etc/modprobe.d ...$ sudo nano raspi-blacklist.conf
Several lines should be visible; anything that starts with a # is a comment. The file should look something like this:
# blacklist spi and i2c by default (many users don't need them) blacklist spi-bcm2708 blacklist i2c-bcm2708 blacklist snd-soc-pcm512x blacklist snd-soc-wm8804
Place a ‘#’ in front of the line with i2c in it; the file should now look like this:
# blacklist spi and i2c by default (many users don't need them) blacklist spi-bcm2708 #blacklist i2c-bcm2708 blacklist snd-soc-pcm512x blacklist snd-soc-wm8804
Type <ctrl>+x to exit, and a prompt to save the file should appear. The next thing is to add the required modules to the modules file in the /etc directory:
...$ sudo nano /etc/modules
The file may already have one of the required lines, but it should look as follows:
# /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-bcm2708 i2c-dev
Save the file and exit. Now that the modules are listed in /etc/modules, ensure they’re installed:
...$ sudo apt-get install i2c-tools
Before continuing, a reboot of the r-π module is required, as the now un-blacklisted and installed module needs to be loaded somehow:
...$ sudo shutdown -r now
A couple of points worth noting:
- Failure to put a ‘sudo’ in front of the nano … command line, will result in the file being opened in read-only mode; edits can’t be saved. Only the root user can edit system files.
- nano is not our preferred editor, but is included with the raspbian distribution. There’s nothing special about nano; use your editor of choice. vim is a personal favourite.
In order to test I2C communications, try this:
...$ sudo i2cdetect -y 1
The response should look something like this:
0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
In one or more of the positions occupied by a ‘——‘ a number may be present. If so, a device responded at that address. At least, I2C is enabled; at most, a device is alive and talking!
More points of note:
- The above assumes a device is connected to I2C port #1 on the r-π’s expansion header.
- If a response from your device was expected and not received, don’t assume I2C is broken! If the table of ‘—‘ markers is present, it’s a good bet I2C is enabled.
- I2C port #0 has an EEPROM on it, and should not be used. The module needs access to the memory device in order to boot.
- We’re not running Python yet! This is just a test that the operating system is able to see something.
I2C/SMB Support in Python
A couple of Python modules (not kernel modules!) are required in order to add support for I2C in Python. The python-smbus package must be installed:
...$ sudo apt-get install python-smbus
This is where the real trouble seems to start. The python smbus driver appears to only support V2 of python. In response to a 5 line Python script, this is the response:
Traceback (most recent call last): File "./i2ctest.py", line 4, in import smbus ImportError: No module named smbus
It’s enough to make any programmer grumpy. Something unsupported means your development project must be suspended until this problem is resolved. Without turning this into a giant How-To document, a short list of steps with minimal explanation follows:
...$ sudo apt-get install libi2c-dev ...$ sudo apt-get install python3-dev ...$ cd /usr/src ...$ sudo wget http://ftp.de.debian.org/debian/pool/main/i/i2c-tools/i2c-tools_3.1.0.orig.tar.bz2 ...$ sudo tar xvf i2c-tools_3.1.0.orig.tar.bz2 ...$ cd i2c-tools-3.1.0/py-smbus ...$ sudo mv smbusmodule.c smbusmodule.c.old ...$ sudo wget https://gist.githubusercontent.com/sebastianludwig/c648a9e06c0dc2264fbd/raw/2b74f9e72bbdffe298ce02214be8ea1c20aa290f/smbusmodule.c
Assuming that all worked, here’s what should have happened:
- Install the libi2c-development libraries.
- Install the python3-dev libraries.
- Download the current version of i2c-tools, at this moment 3.1.0, which will change over time.
- Extract and verify the contents of the i2c-tools archive.
- Switch into the source code directory for py-smbus.
- Rename the smbusmodule.c source file to something else.
- Grab the newest modified version of the code.
I must credit Sebastian Ludwig here; his code is what enables support for I2C in Python 3.
Now a new version of the module has been downloaded using wget, but the smbus driver still must be compiled and positioned so python can include it. The freshly downloaded distutils package features a make-like utility for building modules, discussed here. To build the smbus module, do the following:
...$ cd /usr/src/i2c-tools-3.1.0/py-smbus ...$ sudo python3 setup.py build
This almost worked. It came back with this complaint:
running build running build_ext building 'smbus' extension creating build creating build/temp.linux-armv6l-3.2 gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -I/usr/include/python3.2mu -c smbusmodule.c -o build/temp.linux-armv6l-3.2/smbusmodule.o smbusmodule.c:19:20: fatal error: Python.h: No such file or directory compilation terminated. error: command 'gcc' failed with exit status 1
We’re missing a header file, Python.h. It exists in the file system, so why didn’t the make utility find it? Opening the smbmodule.c file and adding a hard-coded path to the location is a hack worth trying:
This does not work. Doing so will include the Python.h file, but not any header files included inside the Python.h file itself!
After a lot of digging, and attempts at modifying setup.py, as well as modifying smbusmodule.c, this proved a red herring. Here’s what worked:
- Instructions were given for how setup.py works, suggesting modifications may be in order. Wrong.
- Instructions to copy the smbusmodule text presented at spinics.net. Correct.
- Instructions to modify the new smbusmodule’s include line as follows:
- Remove #include <linux/i2c-dev.h>
- Replace with #include “i2c-dev.h”
Wrong. Leave the file alone.
- Instructions to install python-dev: sudo apt-get install python3-dev… Correct.
Assuming the above steps have been taken, or if required un-taken, and you’re still in the directory /usr/src/i2c-tools-3.1.0/py-smbus, try building the module:
...$ sudo python3 setup.py build running build running build_ext building 'smbus' extension gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -I/usr/include/python3.2mu -c smbusmodule.c -o build/temp.linux-armv6l-3.2/smbusmodule.o creating build/lib.linux-armv6l-3.2 gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro build/temp.linux-armv6l-3.2/smbusmodule.o -o build/lib.linux-armv6l-3.2/smbus.cpython-32mu.so
Now install it:
...$ sudo python3 setup.py install running install running build running build_ext running install_lib copying build/lib.linux-armv6l-3.2/smbus.cpython-32mu.so -> /usr/local/lib/python3.2/dist-packages running install_egg_info Writing /usr/local/lib/python3.2/dist-packages/smbus-1.1.egg-info
No errors in either build or install; it should be working.
With I2C support for python3 installed, it’s now time to actually talk to a device. For that, documentation for the smbus functions would be nice. Function descriptions can be found at Raspberry Pi SMBus Commands. Unfortunately, the descriptions are a little vague as to what parameters are passed to each function and how data returns.
We usually don’t use the Idle interpreter for python, as it tends to slow down the development process. Instead, put a #!/usr/bin/python3 at the top of a text file and it can be run from the command line sans Idle:
#!/usr/bin/python3 import smbus deltasig = [0x68,0x69,0x6a,0x06b,0x6c,0x6d,0x6e,0x6f] # device addresses config_byte = 0x1c # continuous mode, 18-bit resolution, gain = 1. bus = smbus.SMBus(1) bus.write_byte(deltasig,config_byte) # configure the adc
Save the above as a text file; call it test_i2c.py.
The deltasig = … line requires a little explanation. The sensor under development uses a delta-sigma analogue-to-digital converter (ΔΣ ADC) from Microchip: MCP3421. Being a Microchip design partner encourages one to use Microchip parts.
The purpose of this post was to get I2C enabled on a Raspberry Pi, and I2C communications working in Python V3. Before testing the above code, something serious needs to happen: actual hardware needs to be connected to the I2C bus! See the next post for more.