If you don’t know what I2C (read “I-squared-C”) is, you may want to check out its Wikipedia page, but in a few words, it’s an amazing protocol that allows communication between one (or more, according to Wikipedia) master devices and one or more slave device, using only 4 wires, two for power supply and two for data. Every slave device has its own unique address that the master can address to request information from the slave.
There are sooo many applications and useful electronic gadgets that use this protocol, like screens and sensors, and if you have an Arduino you might have already used it before without even knowing. Check if your sensor/display/toaster has two pins named SDA and SCL. If it does, it’s probably I2C-compatible, and you might be able to use it with your computer.
But I’m not going to talk about it in this article. This article will only cover the connection and the discovery of devices connected to the bus on GNU/Linux. If you want to know when I publish an article about cool I2C gadgets, make sure you subscribe to my newsletter and select the “I2C” tag!
I found out there is an I2C hidden in almost all graphics cards. It’s used for DDC, Display Data Channel. It’s an I2C-based protocol where the master (the graphics card) can arrange with the slave (the monitor) for a good screen resolution, to adjust brightness, etc. As usual, the Linux kernel exposes these features so we can all benefit from them 😉 Mac OS X also seems to do, but it needs a custom C library to use them. Windows seems not to support it, sorry!
Somebody reported this doesn’t work on Intel integrated graphics. I can confirm it does work on my Intel GM45 graphics through the VGA output. It also worked on my nVidia GeForce FX5500 (or something like that). I haven’t tested any ATIs/AMDs, but people say they should work.
Make the connections
Here are the pinouts for the three main display connectors.
█ Red = +5V, █ Dark gray = ground, █ Yellow = SCL (serial clock), █ Blue = SDA (serial data).
Just connect the pins to your I2C device and go on to the next step. Don’t forget to use a logic level shifter if your device requires a 3V power supply!
- Get a root shell. You’re going to need root access to perform the following operations. You may use “sudo -s” on Ubuntu/Debian, or just “su” if you set a root password and you don’t have sudo installed.
- Install i2c-tools. You can just run “apt-get install i2c-tools” on Ubuntu/Debian. If you’re not using a Debian-based distro, just look for the package that contains “i2cdetect” and install it.
- Load the i2c-dev kernel module. Run “modprobe i2c-dev”.
- Detect all the I2C buses on the system. Run “i2cdetect -l” (that’s a lowercase L). Here’s some sample output:
23456789i2c-0 i2c i915 gmbus ssc I2C adapteri2c-1 i2c i915 gmbus vga I2C adapteri2c-2 i2c i915 gmbus panel I2C adapteri2c-3 i2c i915 gmbus dpc I2C adapteri2c-4 i2c i915 gmbus dpb I2C adapteri2c-5 i2c i915 gmbus dpd I2C adapteri2c-6 i2c DPDDC-B I2C adapteri2c-7 i2c DPDDC-D I2C adapter
- Look for your device in all the I2C buses. You need to know the device’s default I2C address. Run “i2cdetect – y #”, where # is the number of one of the buses found in the previous step. Some sample output (0x39 is the address of my APDS-9930 sensor):
23456789100 1 2 3 4 5 6 7 8 9 a b c d e f00: -- -- -- -- -- -- -- -- -- -- -- -- --10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- --40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --70: -- -- -- -- -- -- -- --
- Did you find your sensor! Great, you’re good to go! If you didn’t, make sure you connected all the wires correctly and try again. Also make sure you scan all of your buses, and that the address you’re looking for is in the right base! It must be hexadecimal! For example, 0x39 (in programming the 0x# syntax is usually used to indicate 8-bit hexadecimal numbers, and 0b# for binary numbers) is 57 in decimal base, and 0b111001 if the base is two. Read more about number bases on Wikipedia.
Build an adapter
If your computer was able to find your I2C device, you can make an adapter so that you can keep it connected while a display is also attached (as long as the I2C addresses do not conflict! Use i2cdetect -y <bus number> to check the addresses your display is using, and connect your device only if it uses a different address).
There are many ways to build one, like taking the four wires out of an extension cord. However the one I made is a lot more compact. I bought these otherwise pretty useless VGA male to female adapters (which come in pairs, so if you break one you still have an extra one), took them apart and soldered four of these rainbow colored 🌈 female jumper wires. Here are the instructions on how to take them apart:
- Pull off the screws with some tweezers. Pull them towards you wile rotating them counter-clockwise.
- With some tweezers, find a way to squeeze the two little rivets that hold the whole thing together (the ones that housed the screws).
- You should then be able to pull the metal cover off the female side with some tweezers. Without using too much force, also pull the black mask that holds the golden pins into place.
- Take off 4 pins and solder the wires on the little pad in the middle.
- Put everything back together. I found it easier to put the pins into the female side and then pressing a very strong vibrator (yes one of those you buy at the sexy shop, phone vibrators aren’t strong enough) onto it to make the pins match back the male side of the adapter.
- Put the metal parts and the rivets back into place as best as you can and try it 😉
Some pictures of the adapter I built:
If you build your own, make sure you share your pictures in the comments below and I’ll add them to the article! 🙂
Use the device
Probably, the easiest way to use a I2C device on Linux is the Python SMBus module, available on Ubuntu/Debian in the python-smbus package, and on PyPi as smbus-cffi. You just need to import the module and create an instance of the smbus.SMBus class, passing the bus number as a positional argument. You’ll then be able to write and read to and from a specific address with the object’s methods.
Check out my Python module for the APDS-9930 as a working implementation 😉
Thanks for reading! Comment below your opinions, or email me using the contact form!