fbpixel
Tags: , , , ,

It can be practical, especially in home automation projects, to communicate between several devices. One of the commonly used techniques is the I2C (or TWI) protocol. The I2C protocol is a method which makes it possible to connect several “Master” cards and several “Slave” cards and to communicate up to 128 devices. It allows asynchronous connections between several components to share information via a “common bus”. We had seen the communication via the Serial port (called UART) which is used to send the code to the Arduino by a computer or to connect two devices in particular in Bluetooth.

Equipment

  • Computer
  • Arduino UNO x2 or more
  • Jumper cable M / M x3 times the number of cards

Connection diagram of the I2C bus between Arduino boards

With the I2C protocol, it is also possible to communicate between different systems (sensors, LCD screen, Raspberry Pi, etc.). An interesting example is the communication between several Arduino boards. For that, we have to write at least two programs, one for the “Mistress” card (Master) and the other for the “Slaves” cards.

An I2C communication is defined by a two-wire bus (sometimes called TWI, Two Wire Interface) and an address. The pins used by I2C communication are usually fixed for each device. One on which the data is sent (SDA Serial Data Line) and on the other the synchronization clock (SLC Serial Clock Line).

I2C / TWI pins:

  • Uno, Ethernet A4 (SDA), A5 (SCL)
  • Mega2560 20 (SDA), 21 (SCL)
  • Leonardo 2 (SDA), 3 (SCL)
  • Due 20 (SDA), 21 (SCL), SDA1, SCL1

In this example we use an Arduino Uno board, so the pins A4 and A5.

In order for the two cards to communicate with each other, they must be connected correctly (A4 with A4 and A5 with A5) and do not forget to connect the earths (GND) as shown in the following diagram.

Caution: If pins A4 and A5 are connected to the pins of a non-powered card, the code will freeze at the time of transmission.

connection-i2c-arduino Manage multiple Arduino with an I2C bus

Generally, one card will send information (Writer) and another will receive it (Reader).

I2C bus configuration code

The Wire.h library allows you to easily define the serial communication on the I2C bus. The functions are similar to the Serial library.

  • Wire.begin () initializes the device address. Function argument may be empty for master devices
  • Wire.write () allows you to send bytes.
  • Wire.requestFrom () handles the request receive function
  • Wire.beginTransmission () starts transmitting data and defines the receiver.
  • Wire.endTransmission ends data transmission
  • Wire.onRequest () handles the request receive function
  • Wire.onRecieve () manages the data reception function

Code of the “Master” card

#include <Wire.h>

# define I2C_SLAVE1_ADDRESS 11
# define I2C_SLAVE2_ADDRESS 12

#define PAYLOAD_SIZE 2

int n=0;

void setup()
{
  Wire.begin();        
  Serial.begin(9600);  

  Serial.println(F("-------------------------------------I am the Master"));
  delay(1000);

  //Request value of n to slave
  Wire.requestFrom(I2C_SLAVE1_ADDRESS, 1);   
  n = Wire.read(); 
  Serial.print(F("recieved value : "));
  Serial.println(n);

  //Send value 12 to slave
  Wire.beginTransmission(I2C_SLAVE1_ADDRESS); 
  Wire.write(12); 
   Serial.print(F("sending value : "));
  Serial.println(12);              
  Wire.endTransmission();   

  Serial.print(" ");

  //Request value of n to slave after change
  Wire.requestFrom(I2C_SLAVE1_ADDRESS, 1); 
  n = Wire.read();
  Serial.print(F(" new recieved value : "));
  Serial.println(n); 
}


void loop()
{
  delay(100);
}

“Slave” card code

#include <Wire.h>

# define I2C_SLAVE_ADDRESS 11 // 12 pour l'esclave 2 et ainsi de suite

#define PAYLOAD_SIZE 2

void setup()
{
  Wire.begin(I2C_SLAVE_ADDRESS);
  Serial.begin(9600); 
  Serial.println("-------------------------------------I am Slave1");
  delay(1000);               
  Wire.onRequest(requestEvents);
  Wire.onReceive(receiveEvents);
}

void loop(){}

int n = 0;

void requestEvents()
{
  Serial.println(F("---> recieved request"));
  Serial.print(F("sending value : "));
  Serial.println(n);
  Wire.write(n);
}

void receiveEvents(int numBytes)
{  
  Serial.println(F("---> recieved events"));
  n = Wire.read();
  Serial.print(numBytes);
  Serial.println(F("bytes recieved"));
  Serial.print(F("recieved value : "));
  Serial.println(n);
}

Open the slave card serial monitor before the master card monitor.

In the serial monitor of the “Master” card:

communication-i2c-maitre Manage multiple Arduino with an I2C bus

In the serial monitor of the “Slave 1” card:

communication-i2c-esclave Manage multiple Arduino with an I2C bus

We can see that the two cards are exchanging information. It is very easy to extend this example to several Arduino boards (Leonardo, Mini, etc.) by adapting the wiring and the address of the components in the “Slave” code.

Code to identify the devices connected to the I2C bus

A good test to know if your devices communicate well with each other is to use the code below (I2CScanner) ) which returns all the addresses of the devices connected to the Master card.

    #include <Wire.h>
     
     
    void setup()
    {
      Wire.begin();
      Serial.begin(9600);
      while (!Serial);             // Leonardo: wait for serial monitor
      Serial.println(F("\nI2C Scanner"));
    }
     
     
    void loop()
    {
      byte error, address;
      int nDevices;
     
      Serial.println(F("Scanning..."));
     
      nDevices = 0;
      for(address = 1; address < 127; address++ )
      {
        // The i2c_scanner uses the return value of
        // the Write.endTransmisstion to see if
        // a device did acknowledge to the address.
          Wire.beginTransmission(address);
          error = Wire.endTransmission();
          
        
        if (error == 0)
        {
          Serial.print("I2C device found at address 0x");
          if (address<16)
            Serial.print("0");
          Serial.print(address,HEX);
          Serial.println("  !");
     
          nDevices++;
        }
        else if (error==4)
        {
          Serial.print("Unknown error at address 0x");
          if (address<16)
            Serial.print("0");
          Serial.println(address,HEX);
        }    
      }
      if (nDevices == 0)
        Serial.println(F("No I2C devices found\n"));
      else
        Serial.println(F("done\n"));
     
      delay(5000);           // wait 5 seconds for next scan
    }

If you have difficulty setting up I2C communication between different devices, don’t hesitate to leave us a comment or contact us.

Sources

Find other examples and tutorials in our Automatic code generator
Code Architect