A Guide to Arduino & the I2C Protocol (Two Wire)

Allows the communication between devices or sensors connected via Two Wire Interface Bus.

This article was revised on 2021/11/18 by Karl Sƶderby.

The I2C protocol involves using two lines to send and receive data: a serial clock pin (SCL) that the Arduino Controller board pulses at a regular interval, and a serial data pin (SDA) over which data is sent between the two devices. As the clock line changes from low to high (known as the rising edge of the clock pulse), a single bit of information - that will form in sequence the address of a specific device and a a command or data - is transferred from the board to the I2C device over the SDA line. When this information is sent - bit after bit -, the called upon device executes the request and transmits it's data back - if required - to the board over the same line using the clock signal still generated by the Controller on SCL as timing.

Because the I2C protocol allows for each enabled device to have it's own unique address, and as both controller and peripheral devices to take turns communicating over a single line, it is possible for your Arduino board to communicate (in turn) with many devices, or other boards, while using just two pins of your microcontroller.

Hardware & Software Needed

Please note that the I2C bus is attached to different pins depending on the board you are using. For example, the pins used for MKR WiFi 1010 are D11, D12, while the pins for UNO are D18, D19. See the image below to understand how to locate the correct pins on your board.

I2C pins on the UNO.
I2C pins on the UNO.

I2C pins on the MKR WiFi 1010
I2C pins on the MKR WiFi 1010

Tutorials

Check out the following tutorials to get a more detailed step-by-step on how to use I2C on Arduino boards:

While the above tutorials were written specifically for the Nano Family boards, they can be adopted to any Arduino board.

Controller Reader

In some situations, it can be helpful to set up two (or more!) Arduino boards to share information with each other. In this example, two boards are programmed to communicate with one another in a Controller Reader/Peripheral Sender configuration via the I2C synchronous serial protocol. Several functions of Arduino's Wire Library are used to accomplish this. Arduino 1, the Controller, is programmed to request, and then read, 6 bytes of data sent from the uniquely addressed Peripheral Arduino. Once that message is received, it can then be viewed in the Arduino Software (IDE) serial monitor window.

Controller Reader Sketch

1// Wire Controller Reader
2// by Nicholas Zambetti <http://www.zambetti.com>
3
4// Demonstrates use of the Wire library
5// Reads data from an I2C/TWI peripheral device
6// Refer to the "Wire Peripheral Sender" example for use with this
7
8// Created 29 March 2006
9
10// This example code is in the public domain.
11
12
13#include <Wire.h>
14
15void setup() {
16 Wire.begin(); // join i2c bus (address optional for master)
17 Serial.begin(9600); // start serial for output
18}
19
20void loop() {
21 Wire.requestFrom(8, 6); // request 6 bytes from peripheral device #8
22
23 while (Wire.available()) { // peripheral may send less than requested
24 char c = Wire.read(); // receive a byte as character
25 Serial.print(c); // print the character
26 }
27
28 delay(500);
29}

Peripheral Sender Sketch

1// Wire Peripheral Sender
2// by Nicholas Zambetti <http://www.zambetti.com>
3
4// Demonstrates use of the Wire library
5// Sends data as an I2C/TWI peripheral device
6// Refer to the "Wire Master Reader" example for use with this
7
8// Created 29 March 2006
9
10// This example code is in the public domain.
11
12
13#include <Wire.h>
14
15void setup() {
16 Wire.begin(8); // join i2c bus with address #8
17 Wire.onRequest(requestEvent); // register event
18}
19
20void loop() {
21 delay(100);
22}
23
24// function that executes whenever data is requested by master
25// this function is registered as an event, see setup()
26void requestEvent() {
27 Wire.write("hello "); // respond with message of 6 bytes
28 // as expected by master
29}

Controller Writer

In some situations, it can be helpful to set up two (or more!) Arduino boards to share information with each other. In this example, two boards are programmed to communicate with one another in a Controller Writer/Peripheral Receiver configuration via the I2C synchronous serial protocol. Several functions of Arduino's Wire Library are used to accomplish this. Arduino 1, the Controller, is programmed to send 6 bytes of data every half second to a uniquely addressed Peripheral. Once that message is received, it can then be viewed in the Peripheral board's serial monitor window opened on the USB connected computer running the Arduino Software (IDE).

Controller Writer Sketch

1// Wire Master Writer
2// by Nicholas Zambetti <http://www.zambetti.com>
3
4// Demonstrates use of the Wire library
5// Writes data to an I2C/TWI Peripheral device
6// Refer to the "Wire Peripheral Receiver" example for use with this
7
8// Created 29 March 2006
9
10// This example code is in the public domain.
11
12
13#include <Wire.h>
14
15void setup()
16{
17 Wire.begin(); // join i2c bus (address optional for master)
18}
19
20byte x = 0;
21
22void loop()
23{
24 Wire.beginTransmission(4); // transmit to device #4
25 Wire.write("x is "); // sends five bytes
26 Wire.write(x); // sends one byte
27 Wire.endTransmission(); // stop transmitting
28
29 x++;
30 delay(500);
31}

Peripheral Receiver Sketch

1// Wire Peripheral Receiver
2// by Nicholas Zambetti <http://www.zambetti.com>
3
4// Demonstrates use of the Wire library
5// Receives data as an I2C/TWI Peripheral device
6// Refer to the "Wire Master Writer" example for use with this
7
8// Created 29 March 2006
9
10// This example code is in the public domain.
11
12
13#include <Wire.h>
14
15void setup()
16{
17 Wire.begin(4); // join i2c bus with address #4
18 Wire.onReceive(receiveEvent); // register event
19 Serial.begin(9600); // start serial for output
20}
21
22void loop()
23{
24 delay(100);
25}
26
27// function that executes whenever data is received from master
28// this function is registered as an event, see setup()
29void receiveEvent(int howMany)
30{
31 while(1 < Wire.available()) // loop through all but the last
32 {
33 char c = Wire.read(); // receive byte as a character
34 Serial.print(c); // print the character
35 }
36 int x = Wire.read(); // receive byte as an integer
37 Serial.println(x); // print the integer
38}

Example 1: Ultra-Sonic Range Finder

This example shows how to read a Devantech SRFxx , an ultra-sonic range finder which communicates via the I2C synchronous serial protocol.

Hardware Required

  • Arduino Board
  • Devantech SRFxx Range Finder (models SRF02, SRF08, or SRF10).
  • 100 uf capacitor
  • Jumper Wires
  • Breadboard

Circuit

Attach the SDA pin of your SRFxx to analog pin 4 of your board, and the SCL pin to analog pin 5. Power your SRFxx from 5V, with the addition of a 100uf capacitor in parallel with the range finder to smooth it's power supply.

Image developed using Fritzing.
Image developed using Fritzing.

Schematic

SRF sch

Code

If using two SRFxxs on the same line, you must ensure that they do not share the same address. Instructions for re-addressing the range finders can be found at the bottom of the code below.

Example 2: Digital Potentiometer

1// I2C SRF10 or SRF08 Devantech Ultrasonic Ranger Finder
2// by Nicholas Zambetti <http://www.zambetti.com>
3// and James Tichenor <http://www.jamestichenor.net>
4
5// Demonstrates use of the Wire library reading data from the
6// Devantech Utrasonic Rangers SFR08 and SFR10
7
8// Created 29 April 2006
9
10// This example code is in the public domain.
11
12#include <Wire.h>
13
14void setup() {
15
16 Wire.begin(); // join i2c bus (address optional for master)
17
18 Serial.begin(9600); // start serial communication at 9600bps
19}
20
21int reading = 0;
22
23void loop() {
24
25 // step 1: instruct sensor to read echoes
26
27 Wire.beginTransmission(112); // transmit to device #112 (0x70)
28
29 // the address specified in the datasheet is 224 (0xE0)
30
31 // but i2c addressing uses the high 7 bits so it's 112
32
33 Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
34
35 Wire.write(byte(0x50)); // command sensor to measure in "inches" (0x50)
36
37 // use 0x51 for centimeters
38
39 // use 0x52 for ping microseconds
40
41 Wire.endTransmission(); // stop transmitting
42
43 // step 2: wait for readings to happen
44
45 delay(70); // datasheet suggests at least 65 milliseconds
46
47 // step 3: instruct sensor to return a particular echo reading
48
49 Wire.beginTransmission(112); // transmit to device #112
50
51 Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
52
53 Wire.endTransmission(); // stop transmitting
54
55 // step 4: request reading from sensor
56
57 Wire.requestFrom(112, 2); // request 2 bytes from peripheral device #112
58
59 // step 5: receive reading from sensor
60
61 if (2 <= Wire.available()) { // if two bytes were received
62
63 reading = Wire.read(); // receive high byte (overwrites previous reading)
64
65 reading = reading << 8; // shift high byte to be high 8 bits
66
67 reading |= Wire.read(); // receive low byte as lower 8 bits
68
69 Serial.println(reading); // print the reading
70
71 }
72
73 delay(250); // wait a bit since people have to read the output :)
74}
75
76/*
77
78// The following code changes the address of a Devantech Ultrasonic Range Finder (SRF10 or SRF08)
79
80// usage: changeAddress(0x70, 0xE6);
81
82void changeAddress(byte oldAddress, byte newAddress)
83
84{
85
86 Wire.beginTransmission(oldAddress);
87
88 Wire.write(byte(0x00));
89
90 Wire.write(byte(0xA0));
91
92 Wire.endTransmission();
93
94 Wire.beginTransmission(oldAddress);
95
96 Wire.write(byte(0x00));
97
98 Wire.write(byte(0xAA));
99
100 Wire.endTransmission();
101
102 Wire.beginTransmission(oldAddress);
103
104 Wire.write(byte(0x00));
105
106 Wire.write(byte(0xA5));
107
108 Wire.endTransmission();
109
110 Wire.beginTransmission(oldAddress);
111
112 Wire.write(byte(0x00));
113
114 Wire.write(newAddress);
115
116 Wire.endTransmission();
117
118}
119
120*/

Hardware Required

  • Arduino Board
  • AD5171 Digital Potentiometer
  • LED
  • 680 ohm resistor
  • 2 4.7k ohm resistors
  • Hook-up wires
  • Breadboard

Circuit

Connect pins 3, 6, and 7 of the AD5171 to GND, and pins 2 and 8 to +5V.

Connect pin 4, the digital pot's clock pin (SCL), to analog pin 5 on the Arduino, and pin 5, the data line (SDA), to analog pin 4. On both the SCL and SDA lines, add 4.7K ohm pull up resistors, connecting both lines to +5 V.

Finally, wire an LED to pin 1, the AD5171's "wiper", with a 680 ohm LED in series.

Image developed using Fritzing.
Image developed using Fritzing.

When the AD5171's pin 6, ADO, is connected to ground, it's address is is 44. To add another digital pot to the same SDA bus, connect the second pot's ADO pin to +5V, changing it's address to 45.

You can only use two of these digital potentiometers simultaneously.

Schematics

AD5171 sch

AD5171 pinconfig

Code

1// I2C Digital Potentiometer
2// by Nicholas Zambetti <http://www.zambetti.com>
3// and Shawn Bonkowski <http://people.interaction-ivrea.it/s.bonkowski/>
4
5// Demonstrates use of the Wire library
6// Controls AD5171 digital potentiometer via I2C/TWI
7
8// Created 31 March 2006
9
10// This example code is in the public domain.
11
12// This example code is in the public domain.
13
14
15#include <Wire.h>
16
17void setup()
18{
19 Wire.begin(); // join i2c bus (address optional for master)
20}
21
22byte val = 0;
23
24void loop()
25{
26 Wire.beginTransmission(44); // transmit to device #44 (0x2c)
27 // device address is specified in datasheet
28 Wire.write(byte(0x00)); // sends instruction byte
29 Wire.write(val); // sends potentiometer value byte
30 Wire.endTransmission(); // stop transmitting
31
32 val++; // increment value
33 if(val == 64) // if reached 64th position (max)
34 {
35 val = 0; // start over from lowest value
36 }
37 delay(500);
38}

Contribute to Arduino

Join the community and suggest improvements to this article via GitHub. Make sure to read out contribution policy before making your pull request.

Missing something?

Check out our store and get what you need to follow this tutorial.

Suggest Changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. You can read more on how to contribute in the contribution policy.