Intro to I2C: Multi-Device Communication with Minimal Pins
September 29, 2021
I2C also often spelled I2C, and (rarely) expanded out to its full “Inter-Integrated Circuit” moniker, is a device-to-device communication protocol invented by Philips Semiconductor (now NXP) in 1982.
I2C has stayed in use since that time for communication between computing devices and their peripherals, and since October 6, 2006, it’s free to implement without any sort of licensing fees.
Caption: I2C pins SDA and SCL broken out on a JC Pro Macro 2 Keypad
Image Credit: Jeremy Cook
The system is extremely simple to use, requiring only an SDA (serial data) and SCL (serial) clock line between two or more devices, as well as a pull-up resistors to keep the lines normally high. In its most basic usage, you might have one I2C device, such as an Arduino board, acting as a master (controller), while a single slave device (peripheral) is set up to signal and even send data back when a request is made.
I’ve used this interface in a number of setups for basic one-on-one communication–such as the display on my JC Pro Macro computer input device–but in fact a large number of devices can use a single bus to pass messages around. The protocol includes an addressing byte, followed the transmission of additional data. 128 devices can normally be accessed by 7 addressing bits, as the 8th bit is used to indicate whether a read or write operation is to be initiated.
That’s a very sort intro to how the protocol works, and if you want to dive deeper, TI explains the details in more depth here. There’s also a wide range of information available via i2c-bus.org. It’s an interesting technology from a conceptual standpoint, but if you just want to get things working, the good news is that many of the details of using it can be handled via pre-written libraries.
As noted earlier, the big advantage of I2C is its wiring simplicity, while still allowing communication between multiple devices. Another option is SPI, which is faster and capable of one-to-many transmission, but requires four wires. RS-232, for its part, is even simpler than I2C, as it doesn’t require a pull-up, but is only capable of one-to-one communication. Notably, many I2C devices include a pull-up resistor already, so in that case you don’t even have to add that extra component.
One might have also heard the terms TWI (Two-Wire Interface), and SMBus (System Management Bus), which are both subsets of I2C. There are also a variety of styles within the I2C banner with different capabilities, including 8 and 10-bit addressing schemes and clock speeds from 100kbit/s to 3.2Mbit/s. The good news is that after 39 years of development and standardization, there’s a good chance that if you have an I2C-like device, and you throw it on the bus, it will work. In the context of an Arduino-based system, transmission is handled via the Wire library on pre-defined pins, making this even simpler.
Arduino I2C Example
The JC Pro Macro 2, shown above, can mount an OLED monitor on its I2C pins for user feedback, which are broken out from pins 2 (SDA) and 3 (SCL) on its “Arduino” Pro Micro controller. Setup here is mostly copy/pasted from the Adafruit ssd1306 library example. I designed this device, and the one change that I had to make was changing line 36 to 0x3C from 0x3D, since the address of the eBay-purchased screen I’m using is different than those sold by Adafruit.
When researching this article, I loaded the Adafruit example code (with the address change) directly onto the Pro Micro controlling my keypad. Without any further tinkering, it started showing the Adafruit example on-screen. Note that line 35 in the code shown above isn’t an issue, even though I don’t have a reset pin hooked up. Whether a consequence of this or not, it does seem to take a while to start working again after hitting the development board reset, but this isn’t a big deal normally. As noted earlier, things just seem to work, if you have the correct address.
I2C Scanner: Easily Obtain Correct I2C Addresses
Caption: Multiple devices connected, but what are their addresses?
Caption: I2C Scanner to the rescue!
If you don’t have the proper address, all is not lost, and you don’t have to poke around online forums and/or guess. It’s actually a snap to find with this Arduino I2C scanner. To install, just search for i2c scanner in the Arduino IDE library manager. Load the Execute example code onto your development board and open your serial monitor. If you have any I2C devices connected to your Arduino, their address(es) will be displayed via the serial monitor. It’s a tool that can certainly save a large amount of frustration!
Ready to Use!
While this brief overview should get you started on your I2C journey, there are a wide range of devices and techniques to explore. I2C is a great way to communicate from device to device, at a reasonable speed for many applications, while using a minimum of GPIO pins.
Jeremy Cook is a freelance tech journalist and engineering consultant with over 10 years of factory automation experience. An avid maker and experimenter, you can follow him on Twitter, or see his electromechanical exploits on the Jeremy Cook YouTube Channel!