Recently, while developing a line of sensors, we had a little fun working with a Raspberry Pi module. Like all things open-source, documentation can be a little sparse, out of sync, inconsistent, and difficult to follow; worse, it’s often organized as a reference as opposed to a lesson.
We distilled everything we could find into the following guide to getting I2C support working in Python V3.
Linux Familiarity Helps
Having a long history with linux helps considerably in configuring a Raspberry Pi. Its heart is an ARM processor, for which there exists an actively maintained linux port. Instructions as far as downloading and installing 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 common language for embedded development, though C++ is making great inroads. In this case, Python makes accessibility easier and, we hope, allows users to develop applications more quickly than creating everything from scratch. Libraries can be imported, and so on.
We chose Python 3. 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 2 vs. 3? 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 Python2 only. Since this was not known when development started, it felt 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 Python3 installations. To mitigate this, a step-by-step guide to adding SMBus support to Python3 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 (newer releases may differ, but at the time of writing, they were), 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. If the file is empty, great! Nothing is blacklisted. 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 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
It’s the last two i2c-… modules that must be listed; if there are others in the file, don’t delete them! Save and exit. Now that the modules are listed in /etc/modules, ensure they’re installed:
...$ sudo apt-get install i2c-tools
Before continuing, if any of the above changes were made, a reboot 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 read-only; 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! For example, our temperature sensor can live at any of locations 0x68-0x6f; were a fully populated chain of sensors attached to the π’s I2C pins, the table would look 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: -- -- -- -- -- -- -- -- 68 69 6a 6b 6c 6d 6e 6f 70: -- -- -- -- -- -- -- --
If attempting to communicate with an I2C device, and it does not appear in the above table, errors will result when attempting to communicate.
More points of note:
- The above assumes a device is connected to I2C port #1 on the π’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
Note: (2019-01-15) Since writing this post, support for I2C in Python 3 appears to have been added; the steps shown below are no longer necessary. Before following the thread below, try talking to an I2C device in Python3. See next post, Raspberry Pi – Python V3 MCP3421 Support for an example.
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 supports only V2 of python. In response to a 5 line Python script, we got this:
Traceback (most recent call last): File "./i2ctest.py", line 4, in import smbus ImportError: No module named smbus
Something unsupported means your development project must be suspended until this problem is resolved. 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.