Pantech.AI

“How to I2C protocol works in Arduino”

Introduction to I2C

I2C (Inter-Integrated Circuit) is a serial bus interface protocol, also known as TWI (Two-Wire Interface) because it requires only two lines for communication: SDA (Serial Data) and SCL (Serial Clock).

I2C operates as an acknowledgment-based protocol, meaning the transmitter confirms data reception by the receiver through an acknowledgment signal after each data transmission.

Operating Modes of I2C

I2C works in two modes:

  1. Master Mode
  2. Slave Mode

In this setup:

  • The SDA line handles data exchange between the master and slave devices.
  • The SCL line provides the clock signal for synchronizing communication between master and slave.

The master device initiates communication with a slave device by sending the unique address of the target slave. The slave device then responds to the master only when addressed.

I2C Device Addressing

Each I2C device has a unique 7-bit or 10-bit address, which the master uses to identify and communicate with specific devices on the bus.

Common I2C Applications

I2C is widely used for tasks such as reading data from Real-Time Clocks (RTC), accessing external EEPROM memory, and interfacing with various sensor modules (e.g., gyroscopes, magnetometers).

I2C Communication Lines

I2C communication is established through two lines:

  • Serial Clock (SCL): This clock line is controlled by the master device, providing the timing for data exchange.
  • Serial Data (SDA): This data line enables data transmission between the master and slave devices.

I2C Master-Slave Communication

The I2C bus uses an open-drain configuration, which allows devices to pull the signal lines (SCL and SDA) low but not drive them high. Without additional circuitry, this could leave the lines in an undefined state. To prevent this, pull-up resistors are added to the SCL and SDA lines, ensuring they return to a high state when not actively pulled low by a device.

Arduino I2C Pins

The Arduino Uno board has I2C pins, as shown in the image below.

Arduino I2C Pins

The Arduino Uno board has a single I2C module, with the SDA and SCL lines available at two separate locations.

Note: When using the I2C communication protocol, it’s essential to include pull-up resistors on the SDA and SCL lines. The resistor values may vary depending on the specific devices in use.

I2C functions for Arduino

Wire.write (data)

It is used to transmit data to a master or slave device.

Parameter

The data can be a single byte, a string, or an array of values.

Returns

 No. of bytes written.

e.g.

Wire.write(7);               //send data byte
Wire.write(“i2c”);           //send string to slave device
Wire.write(a, 6);            //here a is an array

Wire.available()

This function is used by a master or slave to check if the requested data is available, returning the number of bytes that are accessible.

Wire.read()

It is used by the master to read requested data from the slave or to read data transmitted from the master to the slave.

Functions for Arduino I2C Master

Note: Every I2C slave device has a unique address. When communicating via the I2C protocol, the master must use this specific slave address.

The Arduino includes a Wire Library, which enables communication with I2C devices.

The Arduino provides a Wire Library to facilitate communication with I2C devices.

  • Wire.begin() This function initializes the Wire library and joins the I2C bus as a master.
  • Wire.beginTransmission(slave_address) This function starts a transmission with the I2C slave device specified by the given slave address.
    • slave_address: The 7-bit address of the device you want to communicate with.

Example:

Wire.beginTransmission (50)     //begin transmission with slave having address 50

Wire.requestFrom(address, numBytes)

or

Wire.requestFrom(address, numBytes, stop)

This function is used by the master to request data from a slave device. After requesting, the data can be read using Wire.read().

Parameters:

  • address: The address of the device you wish to communicate with.
  • numBytes: The number of bytes to request.
  • stop: A Boolean value.
    • true: Sends a stop condition after the request, releasing the bus.
    • false: Sends a repeated start after the request, keeping the connection active.

Returns:

  • The number of bytes received from the slave device.

Example:

Wire.requestFrom(50, 4)          //request 4 no. of bytes from slave having address 50
Wire.requestFrom(50, 4, true)    //will stop receiving data after 4 bytes, releasing bus

Wire.endTransmission()

This function concludes a transmission to a slave device that was initiated with beginTransmission() and sends any bytes that were queued with write().

Returns:

  • A byte indicating the status of the transmission.

Functions for Arduino I2C Slave Mode

  • Wire.begin(address) Initializes the Wire library and joins the I2C bus as a slave with the specified address.
    • Parameter:
    • address: The 7-bit slave address. If not specified, it joins the bus as a master.
  • Wire.onReceive(handler) Sets a handler function to be called when the slave device receives data from a master.

Example:

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}
void receiveEvent (int howmany){
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read();        // receive byte as a character
   	Serial.print(c);             // print the character
  }
}

Wire.onRequest(handler)

Specifies a handler function to be called when the master requests data from the slave device. This function takes no parameters and does not return anything.

Example:

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}
void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup ()
void requestEvent() {
  Wire.write("hello "); // respond with message of 6 bytes
  // as expected by master
}

I2C Interfacing Diagram

I2C Communication between two Arduino

I2C Communication between Two Arduino

To transfer data from the master to the slave device, we will use two Arduinos: one as the master and the other as the slave.

We will utilize the built-in examples from the Wire library provided by the Arduino IDE for I2C communication.

For this example, we will use master_writer for the master Arduino and slave_receiver for the slave Arduino. The master will send a number to the slave, and the slave will display it on the serial monitor.

You can access these examples from:

  • FileExamplesWiremaster_writer
  • FileExamplesWireslave_receiver

Sketch for Arduino as a master writer

// Wire Master Writer
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Writes data to an I2C/TWI slave device
// Refer to the "Wire Slave Receiver" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin(); // join i2c bus (address optional for master)
}

byte x = 0;

void loop() {
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte
  Wire.endTransmission();    // stop transmitting

  x++;
  delay(500);
}

Sketch for Arduino as Slave receiver

// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

Slave’s Serial Monitor Output

Two-way communication between two Arduino using I2C

Let’s write a program where the master Arduino Uno will send a “Hello” message to the slave Arduino Uno. Upon receiving the message, the slave will respond with “Hi.” Two Arduino Unos will be used, one as the master and the other as the slave.

Sketch for Master

#include <Wire.h>

void setup() {
 Serial.begin(9600); /* begin serial comm. */
 Wire.begin(); /* join i2c bus as master */
 Serial.println("I am I2C Master");
}

void loop() {
 Wire.beginTransmission(8); /* begin with device address 8 */
 Wire.write("Hello Slave");  /* sends hello string */
 Wire.endTransmission();    /* stop transmitting */

 Wire.requestFrom(8, 9); /* request & read data of size 9 from slave */
 while(Wire.available()){
    char c = Wire.read();/* read data received from slave */
  Serial.print(c);
 }
 Serial.println();
 delay(1000);
}

Sketch for Slave

#include <Wire.h>

void setup() {
 Wire.begin(8);                /* join i2c bus with address 8 */
 Wire.onReceive(receiveEvent); /* register receive event */
 Wire.onRequest(requestEvent); /* register request event */
 Serial.begin(9600);           /* start serial comm. */
 Serial.println("I am I2C Slave");
}

void loop() {
 delay(100);
}

// function that executes whenever data is received from master
void receiveEvent(int howMany) {
 while (0 <Wire.available()) {
    char c = Wire.read();      /* receive byte as a character */
    Serial.print(c);           /* print the character */
  }
 Serial.println();             /* to newline */
}

// function that executes whenever data is requested from master
void requestEvent() {
 Wire.write("Hi Master");  /*send string on request */
}

Master’s Serial Monitor Output

Slave’s Serial Monitor Output

Leave a Comment

Your email address will not be published. Required fields are marked *