Press enter to see results or esc to cancel.

I2C – Inter-Integrated Circuit

I2C is a simple communication for connecting different devices which can have a different power supply.

It is based on a master-slave relationship. This means that the master or masters (e.g. Arduino) are giving tasks to slaves (other devices) and demanding reports from them. In contrast to I2C there is a popular CAN protocol where devices have “free will”, but in the case of simultaneous communication, the device with a higher priority wins.

Electronic circuit

The basic premise is very simple: the master or masters  are connected with slave(s) via two lines called SDA which transfer data, and an SCL clock which controls data flow. As mentioned before, this protocol makes it possible for devices with different voltage supplies to communicate, but the range of communication is rather short compared to some other methods. This protocol was developed for communication between different chips on the same circuit board, so when designing your projects keep the lines shorter than 1 m (3.3 ft). In the magic image below we can see how our cool gadgets are connected.

I2C communication protocol for Arduino

From the image we can deduce that the big boss is our Arduino and the slaves are different gadgets like a thermometer, an accelerometer, a display… But sometimes even Arduino can be a slave if we have Raspberry Pi  or more than one Arduino connected and working together.

Never forget to connect your devices to common ground.

On our image we can also see that SDA and SCL lines are connected (pulled up) to some voltage via pull-up resistors with the same value. This is because of the way I2C works. While communication like UART is done using an electrical signal “generated” in a transmitter, such as GPIO output, I2C protocol is manipulating communication SDA and SCL line voltage with a transistor pulling it to the ground – image below. When choosing voltage we need to look at two things:

  • That it is not so high that it can damage our device. The supply voltage is not the limit, in the datasheet you can find “port voltage tolerance”,
  • That it is not too low to detect logic high state. In Arduino logic low is under 2 V and logic high above 3 V.

I2C open drain

When connecting Raspberry Pi and Arduino, pull up your I2C line to 3.3 V. If it doesn’t work, use level shifters.

Now what type of resistors should we choose? It depends on many factors, but to be on the safe side anything between 2 kΩ and 10 kΩ. The bigger the resistor, the smaller the power consumption, but at the same time the slower the signal reaction, which is bad for fast communication – image below. Arduino I2C can use the standard mode (100 kb/s) which is the default, and the fast mode (400 kb/s). Higher baud rates are possible with stronger microprocessors.

I2C pull up resistor value effect

Data flow

Ok, we are almost done. The last thing that we have to understand are the rules of how devices should communicate. In the I2C world it is not polite if all gadgets talk at the same time 🙂 because there is only one SDA line available for both directions. The master is always the one that starts any conversation and using the SCL clock controls all data flow. In Image below we can see the most basic protocol that is used for writing to the slave.

I2C protocol write

In the image above we can see that the I2C master always starts communication with a start bit (S) and ends it with a spot bit (P). Between these two bits the master will send at least two bytes that represent the address (name) of the slave being appealed to and the register (message) that the master would like to send to it. On the other hand, the slave always acknowledges (A) every received byte, so that the master knows they understand each other. You probably noticed that the first address byte is actually only 7 bits long and there is a “0” at the end. The I2C address is a 7-bit number followed by a bit “0/1” which announces that the master is sending information, or a bit “1” which means that the master is demanding information from the slave.

I2C address is given in the datasheet. Some can be changed by soldering pins or by programming it.

But these days our projects are getting more and more complicated and the types of gadgets that we can control with I2C are getting smarter and more sophisticated. For this reason, in most I2C devices we can see a message longer than a byte and instead of a 7 bit address which has a limit of 128 devices, a 10 bit address which extends the number to 1024. In Image below we can see an example of communication between an Arduino and an accelerometer.

I2C protocol communication

Alongside their original function, accelerometers – like many other gadgets – offer additional inside processing and functions that relieve engineers and microprocessors of some tasks and often reduce the power consumption of the final electronic device. For example, accelerometers can monitor the environment while the Arduino is hibernating (saving energy) and wakes it when it detects a predefined move. Because of this, I2C slaves usually have registers where a configuration or measured parameters are saved. In upper Image we can see this register following the slave address.

Arduino can send a maximum of 32 bytes at once via I2C.

Give me the Code

Now we have finally got to the programming part 🙂  For I2C, Arduino uses a wire() library, for that reason we have to enable it in our project by #include <Wire.h> at beginning of our code. Table below shows the most relevant functions with the corresponding parameters and description.

Before we can test the I2C Arduino example code, we should take into account our knowledge from Chapter One and connect our Arduino and slave device on our test board. SDA and SCL pins are marked on all Arduinos.

I2C Arduino example code:

// MMA8451Q accelerometer
#include <Wire.h>  // library for I2C

char address = 0x1C;  // accelerometer address
char x_axis = 0;  // when using 8 bit values, use char instead of int
char y_axis = 0; 
char z_axis = 0;

void setup(){
  Wire.begin();  // join i2c party
}

void loop(){
  Wire.beginTransmission(address);  // announces start of transmission
  Wire.write(0x01);  // we want to access x-axis register
  Wire.write(0x03);  // we want to access x-axis register
  Wire.write(0x05);  // we want to access x-axis register
  Wire.endTransmission();  // ends transmission
// in this example we are reading only MSB - reduced resolution

Wire.requestFrom(address, 3);  // request 3 bytes from accelerometer
while(Wire.available()){  // if there is new data 
  x_axis = Wire.read();  // only after available, we read it
  y_axis = Wire.read();
  z_axis = Wire.read(); 
}

Bonus – Arduino I2C clock speed

As a little bonus we will have a look at how to enable Arduino I2C fast mode. This will increase the speed of communication from 100 kb/s to 400 kb/s, but don’t forget to use pull-up resistors 4.7 kΩ or smaller. The last thing to keep in mind is that not all slave devices can reply that fast.

void setup(){
  Wire.begin();  // join i2c party
  Wire.setClock(400000);  // default is 100000 
}

Now you are all done! Give me a virtual Hi5 because you are now an I2C Arduino master! 🙂

I promise no spam. 😉