Il peut être pratique, notamment dans des projets de domotique, de communiquer entre plusieurs appareils. Une des techniques couramment utilisée est le protocole I2C (ou TWI). Le protocole I2C est une méthode qui permet de connecter plusieurs cartes « Maîtres » et plusieurs cartes « Esclaves » et de faire communiquer jusqu’à 128 appareils. Elle permet des connexions asynchrones entre plusieurs composants pour partager des informations via un « bus commun ».  Nous avions vu la communication via le port Série (dit UART) qui est utilisée pour envoyer le code à l’Arduino par un ordinateur ou pour connecter deux appareils notamment en Bluetooth.

Matériel

  • Ordinateur
  • Arduino UNO x2 ou plus
  • Jumper cable M/M x3 fois le nombre de carte

 

Schéma de connexion entre cartes Arduino

Avec le protocole I2C, il est aussi possible de communiquer entre différents systèmes (capteurs, écran LCD, Raspberry Pi, etc.). Un exemple intéressant est la communication entre plusieurs cartes Arduino. Pour cela, il nous faut écrire au moins deux programmes, un pour la carte « Maîtresse » (Master) et l’autre pour les cartes « Esclaves » (Slaves).

Une communication I2C est défini par un bus de deux fils (parfois appelé TWI, Two Wire Interface) et une adresse. Les broches utilisées par la communication I2C sont généralement fixé pour chaque appareil. L’une sur laquelle sont envoyées les données (SDA Serial Data Line) et sur l’autre l’horloge de synchronisation (SLC Serial Clock Line).

Broches I2C/TWI:

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

Dans cet exemple nous utilisons une carte Arduino Uno, donc, les broches A4 et A5.

Afin que les deux cartes communiquent entre elles il faut les relier correctement (A4 avec A4 et A5 avec A5) et ne pas oublier de relier les masses (GND) comme indiqué sur le schéma suivant.

Attention : Si les broches A4 et A5 sont reliées aux broches d’une carte non alimentée, le code se figera au moment de la transmission.

Généralement, une carte va envoyer des informations (Writer) et une autre va les recevoir (Reader).

 

Code de configuration des cartes

La libraire Wire.h permet de les définir simplement la communication série. Les fonctions sont similaires à la librairie Serial.

  • Wire.begin() permet d’initialiser l’adresse de l’appareil. L’argument de la fonction peut être vide pour les appareils maître
  • Wire.write() permet d’envoyer des bytes.
  • Wire.requestFrom() gère la fonction de réception de requête
  • Wire.beginTransmission() débute la transmission de donnée et définit le récepteur.
  • Wire.endTransmission termine la transmission de donnée
  • Wire.onRequest() gère la fonction de réception de requête
  • Wire.onRecieve() gère la fonction de réception de donnée

 

Code de la carte « Maîtresse »

#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);
}

 

Code de la carte « Esclave »

#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);
}

 

Ouvrez le moniteur série des cartes esclaves avant le moniteur de la carte maître.

Dans le moniteur série de la carte « Maîtresse »:

Dans le moniteur série de la carte « Esclave 1 »:

 

Nous pouvons voir que les deux cartes échangent des informations. Il est très facile d’étendre cet exemple à plusieurs cartes Arduino (Leonardo, Mini, etc.) en adaptant le câblage et l’adresse du composants dans le code « Esclave ».

 

Code pour identifier les périphériques I2C branchés

Un bon test pour savoir si vos appareils communiquent bien entre eux est d’utiliser le code ci-dessous (I2CScanner) qui retourne toutes les adresses des appareils branchés à la carte Maîtresse.

    #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
   }

 

 

Si vous avez des difficultés pour mettre en place une communication I2C entre différents appareils n’hésitez pas à nous laisser un commentaire ou à nous contacter.

 

Sources

La référence sur la librairie Wire d’Arduino

Note sur le concept I2C Master Writer et Master Reader