In this article, I collected some methods you can use to connect an I2C device to a computer. For the uninitiated, I²C is a low-speed serial bus commonly used in communication with small devices, such as temperature sensors, accelerometers, gyroscopes, EEPROMs, and many more. The most common clock speed is 100 kHz, resulting in a transfer rate of approximately 10 kB/s in half-duplex. Usually, there’s no need to transfer more than just a couple of bytes, so the low speed is not a problem.
I²C is the basis for SMBus, which is often used in PC motherboards for internal communication, such as with temperature sensors and fan controllers.
I²C subsystem under Linux
To understand the basics of I²C under Linux, the best resource to start with is the Kernel documentation, which is surprisingly easy to digest.
You’ll also need the i2c-tools package, which contains the i2cdetect
tool that scans the I²C bus for devices.
Load the kernel module first:
modprobe i2c-dev
On my main machine, the output of i2cdetect -l
shows the following:
i2c-3 i2c AMDGPU DM i2c hw bus 0 I2C adapter
i2c-10 smbus SMBus PIIX4 adapter port 0 at 0b00 SMBus adapter
i2c-1 i2c AMDGPU SMU 0 I2C adapter
i2c-8 i2c AMDGPU DM aux hw bus 1 I2C adapter
i2c-6 i2c AMDGPU DM i2c hw bus 3 I2C adapter
i2c-13 i2c i2c-tiny-usb at bus 001 device 026 I2C adapter
i2c-4 i2c AMDGPU DM i2c hw bus 1 I2C adapter
i2c-11 smbus SMBus PIIX4 adapter port 2 at 0b00 SMBus adapter
i2c-2 i2c AMDGPU SMU 1 I2C adapter
i2c-0 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-9 i2c AMDGPU DM aux hw bus 2 I2C adapter
i2c-7 i2c AMDGPU DM aux hw bus 0 I2C adapter
i2c-5 i2c AMDGPU DM i2c hw bus 2 I2C adapter
i2c-12 smbus SMBus PIIX4 adapter port 1 at 0b20 SMBus adapter
Testing setup
To verify that the given I²C setup is practical, I used a Chinese GY-521 module, which is an accelerometer/gyroscope/thermometer based on the chip MPU6050.
The device shows up under the address 0x68
by default. The Python script below reads the temperature from the chip and prints it:
import struct
import sys
import time
from timeit import default_timer
from smbus2 import SMBus
bus = SMBus(int(sys.argv[1]))
dev_address = 0x68
bus.write_byte_data(dev_address, 0x19, 7)
bus.write_byte_data(dev_address, 0x6B, 1)
bus.write_byte_data(dev_address, 0x1A, 0)
time.sleep(0.1)
while True:
timer_start = default_timer()
buffer = bytes(bus.read_i2c_block_data(dev_address, 65, 2))
timer_end = default_timer()
raw_temp = struct.unpack('>h', buffer)[0]
temperature = (raw_temp / 340.0) + 36.53
print("Temperature: %.1f\xb0C, time: %.1f ms" % (temperature, 1000.0 * (timer_end - timer_start)))
time.sleep(0.1)
Calling it with python3 script.py {bus identifier}
gives the following output:
Temperature: 25.0°C, time: 23.9 ms
Temperature: 25.1°C, time: 23.9 ms
Temperature: 25.0°C, time: 23.9 ms
...
External adapters
External adapters usually connect to a USB port.
Programmable modules
The most convenient method, in my opinion, is I2C-Tiny-USB — firmware for various development boards. I use it with a Digispark board, which fits directly into a USB port. The Digispark needs to be flashed with firmware, which is described on GitHub too.
Commercial USB-I²C converters
There are plenty of proprietary converters, though I haven’t used any of them yet. One of the most popular boards is based on MCP2221:
Bus Pirate
Bus Pirate is a universal adapter that can be used for many protocols, including I²C. It’s connected via USB and exposes a virtual serial port. You can talk to I²C devices using its custom textual protocol. There is also an out-of-tree kernel driver for it, though I haven’t tested it.
Various parallel port adapters
Supported by the i2c-parport
kernel module, as described in the Kernel documentation. I haven’t tested any of them.
Video interfaces: HDMI, DVI, VGA
Video interfaces: VGA, DVI and HDMI all have +5 V SDA and SCL lines for DDC communication. They are used to read the monitor’s EDID data, which contains information about the monitor’s capabilities, such as supported resolution, refresh rate, etc. DisplayPort, however, uses different signaling and does not have I²C lines.
SDA is usually marked as DDC Data, and SCL as DDC Clock. Under Linux, graphics card drivers expose these lines to the I²C subsystem. I checked the following configurations:
- Open-source ATI/AMD driver
amdgpu
— works. - Open-source NVIDIA driver
nouveau
— works. - Proprietary NVIDIA driver
nvidia
— works only if the monitor is connected. You may also experiment with the Hot Plug Detect pin; perhaps shorting it to +5 V will enable the I²C lines. - Open-source Intel driver
i915
— works.
Some time ago, for my old computer, I made a DVI cable with exposed I²C lines for quick testing:
Directly on the motherboard
You may sometimes find the I²C lines directly on the motherboard. Look for chips in larger sockets, such as SOIC or DIP. Usually, these are EEPROMs, fan controllers, or temperature sensors. Sometimes, there are even specialized headers placed on the motherboard.
For example, I found a fan controller chip ADT7490 on my old workstation, ThinkStation C20. It operates on 3.3 V. I found some soldering pads large enough to solder the wires to. The chip is visible in the photo below:
There were many other devices on bus 0
, but the GY-521 was indeed working:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- 08 -- -- -- 0c -- -- --
10: -- -- -- -- -- -- -- -- 18 -- 1a -- 1c -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- 2c -- 2e --
30: 30 -- 32 -- 34 -- -- -- -- -- -- -- -- -- 3e --
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- --
50: 50 -- 52 -- 54 -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 69 -- -- -- -- 6e --
70: -- -- -- -- -- -- -- --
Conclusion
If you know of any other methods, please let me know.