Débuter avec le bus I2C sur Arduino ESP8266 ESP32. Librairie Wire.h

Partager sur facebook
Partager sur twitter
Partager sur linkedin
Partager sur pinterest
Partager sur email
Partager sur telegram

Le bus I2C est très utilisé pour récupérer des mesures ou piloter des équipements sur les projets Arduino, ESP32, ESP8266 et Raspberry Pi à l’aide de la librairie standard Wire.h. Le bus I2C est un bus de communication série mis au point par Philips à partir de 1982 qui permet à deux appareils (ou plus) de communiquer. Plus précisément, c’est un bus de données série synchrone bidirectionnel half-duplex. Les appareils connectés au bus peuvent agir en tant qu’équipement principal – c’est à dire l’appareil qui «contrôle» le bus – ou secondaire. 

 

Dans cet article, nous allons aborder le principe de fonctionnement de la librairie Wire.h qui permet de faire communiquer simplement des Arduino entre eux. Par contre nous aborderons pas le fonctionnement technique du bus I2C qui est déjà très bien expliqué sur cette page Wikipedia.

Présentation rapide du bus I2C

Le bus I2C est donc une amélioration de la communication série destinée à faire communiquer des accessoires domotiques développé à partir des années 1980 par Philipps. La plupart du temps le bus I2C est constitué d’un équipement principal et un ou plusieurs équipements secondaires, mais la norme permet des organisations plus complexes.

Le bus I2C ne nécessite que deux lignes de communication (fils) pour connecter les appareils entre eux :

  • SDA (Serial Data Line), ligne de données bidirectionnelle,
  • SCL (Serial Clock Line), ligne d’horloge de synchronisation bidirectionnelle. Le signal est généré uniquement par l’équipement principal

D’un point de vue électrique, tous les équipements doivent être connectés à la même masse électrique GND.

Les deux lignes doivent être connectées à une tension de référence (Vdd) via des résistances pull-up.

Lorsqu’on achète des modules I2C breakout pré-assemblés, le circuit intègre déjà la résistance de pull-up.

Voici un exemple de réseau I2C. Un Arduino Uno constitue l’équipement principal du réseau. Deux équipements secondaires sont connectés au bus I2C. A l’adresse 0x76 est connecté un capteur BME280. A l’adresse 0x5A se trouve un capteur d’intensité lumineuse BH1750FVI.

Deux résistances de pull-up sont représentées pour le principe mais elles sont déjà intégrées aux modules (breakout) BME280 et BH1750.

 

Architecture bus i2c arduino breakout

 

Le nombre maximum d’équipements est limité par le nombre d’adresses disponibles. Comme l’adressage des périphériques est constitué par un mot de 7 bits, on pourra adresser jusqu’à 128 périphériques (27). Le nombre de périphériques I2C dépend également de la qualité du circuit (sa capacité) ainsi que de la vitesse de transmission désirée.

Notons qu’il est très rare de dépasser 10 équipements par montage dans la majorité des projets d’objets connectés.

Cinq vitesses de transmission sont possibles :

  • Standard mode (Sm)  ≤ 100 kbit/s
  • Fast mode (Fm)  ≤ 400 kbit/s
  • Fast plus mode (Fm+) ≤ 1 Mbit/s
  • High-speed mode (Hs-mode) ≤ 3,4 Mbit/s
  • Ultra-fast mode (UFm) ≤ 5 Mbit/s, unidirectionnel uniquement.

La vitesse maximale est imposée par le fabricant. La plupart du temps, la vitesse est inférieure à 1 Mbit/s, en général elle se situe entre 100 et 400 kbit/s pour les capteurs.

Les adresses I2C

Chaque équipement secondaire dispose donc d’une adresse unique qui prend la forme d’une chaîne hexadécimale, par exemple 0x69.

Cette adresse est attribuée par le fabricant du composant. Il n’existe pas de norme mais les fabricants s’organisent pour ne pas utiliser une adresse déjà utilisée par un composant existant. En général, une adresse I2C secondaire est disponible. Elle est attribuable par programmation (rarement) ou en modifiant le circuit (à l’aide d’un jumper à souder ou en appliquant une tension pré-définie).

Voici deux exemples

Sélecteur i2c du BME280 en soudant un jumper Attribuer l'adresse 0x5C au BH1750FVI en applicant une tension supérieure à 0,7V sur la broche ADDR
Pour attribuer l’adresse i2c 0x77, il faut souder un jumper entre les deux broches de droite et couper la piste entre les deux broches de gauche Pour attribuer l’adresse 0x5C, il faut appliquer une tension supérieur à 0,7V sur la broche ADDR

En cas de doute sur l’adresse I2C du breakout, vous pouvez consulter cette liste des modules les plus courant pour les projets Arduino, ESP32, ESP8266 et Raspberry Pi

A LIRE AUSSI :
Adresses I2C des capteurs et actionneurs les plus courants

Broches i2c par défaut des cartes de développement les plus courantes

Il est possible d’attribuer manuellement par programmation (nous verrons comment au prochain paragraphe) les broches du bus I2C. La plupart des fabricants de cartes de développements (Arduino, ESP32, ESP8266, STM32, Raspberry Pi…) exposent les broches I2C standards.

Voici la liste des broches par défaut des cartes de développement les plus courantes

SDA SCL SDA1 SCL1
Bus principal Bus secondaire
Uno* A4 A5
Ethernet A4 A5
Mega2560 20 21
Leonardo 2 3
Due 20 21 SDA1 SCL1
ESP8266 D1 D2
ESP32 IO21 IO22 Utilisateur Utilisateur
STM32 PB7 ou PB9 PB6 ou PB8 PB11 PB10

(*) Ne pas utiliser les broches repérées SDA et SCL sur l’Arduino Uno.

Présentation de la librairie Wire.h pour Arduino, ESP32 et ESP8266

Le bus i2c est pris en charge par la librairie Wire.h qui a également été portée sur ESP32 et ESP8266.

C’est une librairie standard qui ne nécessite aucune installation complémentaire. La version dédiée aux ESP32 et ESP8266 est installée en même temps que le SDK.

Pour utiliser le bus I2C dans un projet Arduino, il suffit déclarer la librairie wire en début de code.

#include <Wire.h>

On initialise le bus i2c à l’aide de la méthode Wire.begin(adresse).

Sur Arduino, les broches ne sont pas spécifiées, la librairie utilise les broches par défaut pour la carte de développement ciblée. L’adresse correspond à l’adresse que l’on désire attribuer à l’équipement secondaire. On utilisera par exemple ce paramètre lorsqu’on développe un réseau I2C entre plusieurs Arduino. Nous verrons deux exemples juste après.

Wire.begin();

Les modules I2C du commerce disposent d’une librairie qui s’occupe de récupérer les données ce qui facilite grandement de travail de développement puisqu’on n’a pas à s’occuper du décodage des paquets envoyés.

Il est toutefois possible d’utiliser la librairie Wire.h pour développer notre propre protocole de communication entre deux (ou plus) Arduino (ou tout autre micro-contrôleurs d’ailleurs)

Pour faire simple, la librairie Wire.h dispose de 4 méthodes principales. Toutes les méthodes sont détaillées ici.

Wire.beginTransmission(adresse_i2c) permet de démarrer un envoi de données vers l’équipement dont l’adresse est passée en paramètre.

Wire.write(donnees_a_envoyer) permet d’envoyer une donnée vers l’équipement spécifié auparavant. Plusieurs formats de données sont supportés par la librairie

Wire.write(value) une valeur à envoyer sur un octet (byte)

Wire.write(string) une chaîne à envoyer sous forme de série d’octets (byte). La conversion d’une chaîne de caractère en byte est gérée par la librairie

Wire.write(data, length)

  • data un tableau d’octets (bytes)
  • length le nombre d’octets à transmettre

Wire.endTransmission() permet de fermer la communication avec l’équipement

Nous verrons d’autres méthodes utiles dans les exemples suivants

Limitations de la librairie Wire.h pour les cartes Arduino

Pour les cartes Arduino, la méthode Wire.begin() permet de spécifier manuellement l’adresse d’un équipement. Par contre, on ne pourra pas spécifier manuellement les broches SDA et SCL.

Wire.begin(0x09);

Limitations de la librairie Wire.h pour ESP32 et ESP8266

L’adaptation de la librairie Wire.h pour les cartes de développement ESP32 et ESP8266 ne permet pas d’attribuer une adresse à un équipement secondaire. On ne pourra donc pas créer un réseau I2C pour faire communiquer plusieurs ESP32 ou ESP8266 ensembles.

Par contre, on peut spécifier les broches SDA et SCL. Ici par exemple on attribue manuellement les broches 21 (SDA) et 22 (SCL) au bus I2C en appelant la méthode comme ceci Wire.begin(broche_sda,broche_scl).

Wire.begin(21,22);

En résumé

Arduino ESP32 / ESP8266
Attribuer une adresse à un équipement secondaire sur le réseau I2C Oui Non
Créer un réseau I2C Oui Non
Attribuer les broches SDA et SCL Non Oui
Piloter et récupérer des données d’équipements I2C Oui Oui

Exemple 1, comment faire communiquer deux Arduino via le bus I2C

Pour comprendre les bases de la librairie wire.h, nous allons faire communiquer deux Arduino via un bus I2C.

Voici le schéma de câblage.

2 cartes Arduino communiquent via le bus I2C

Code à téléverser sur l’Arduino Principal

L’Arduino principal envoie à l’adresse 0x9 un entier toutes les 500ms.

void loop() {
  Wire.beginTransmission(0x09);  // transmit to device #9 / transmission sur l arduino secondaire a l adresse 0x09 (=9 en decimale)
  Wire.write(x);                 // sends x / envoi de la valeur de x
  Wire.endTransmission();        // stop transmitting / arret de la transmission
  x++;                           // Increment x / incremente x
  delay(500);                    // delay of 500  milli seconde between two send / delay de 500 milli seconde entre 2 envoi sur le bus I2C
}

Créer un premier croquis sur l’IDE Arduino et coller le code suivant.

Ensuite connecter l’Arduino principal à l’ordinateur et téléverser le projet.

/*Arduino 1 code
  Example of code for use I2C communication between two Arduino :

  Arduino 1                 Secondary Arduino
    A4                         A4
    A5                         A4
    GND                        GND

    MIT License
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
    merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished
    to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Include the required Wire library for I2C / integrer la librairie Wire pour utiliser l I2C
#include <Wire.h> 

int x = 0; // creation of the variable x for stockage the state of the led on the secondary arduino / creation d une variable x qui stocke l etat de la led sur l arduino secondaire

void setup() {
  // Start the I2C Bus / innitialisation du bus I2C
  Wire.begin();
}

void loop() {
  Wire.beginTransmission(0x09);  // transmit to device #9 / transmission sur l arduino secondaire a l adresse 0x09 (=9 en decimale)
  Wire.write(x);                 // sends x / envoi de la valeur de x
  Wire.endTransmission();        // stop transmitting / arret de la transmission
  x++;                           // Increment x / incremente x
  delay(500);                    // delay of 500  milli seconde between two send / delay de 500 milli seconde entre 2 envoi sur le bus I2C
}

Code à installer sur l’Arduino secondaire

Créer un second croquis et coller le code suivant. Connecter le second Arduino, sélectionner le port sur lequel il est connecté et téléverser le projet.

/*
Secondary arduino code
  Example of code for use I2C communication between two Arduino :

  Main Arduino (Uno)       Secondary Arduino (Uno)
     A4                         A4
     A5                         A4
    GND                         GND

    MIT License
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
    merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished
    to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Include the required Wire library for I2C / integrer la librairie Wire pour utiliser l I2C
#include <Wire.h> 

int LED = 13;
int x = 0;

void setup() {
  pinMode (LED, OUTPUT);          // Define the LED pin as Output / definir le pin de la led comme sortie

  Wire.begin(9);                  // Start the I2C Bus with address 9 in decimal (= 0x09 in hexadecimal) / innitialisation du bus I2C avec comme adresse 9 en decimal soit 0x09 en hexadecimal

  Wire.onReceive(receiveEvent);   // Attach a function to trigger when something is received / Attacher une fonction a declencher lorsque quelque chose est recu
}

void receiveEvent() {
  x = Wire.read();                // read one character from the I2C / lire le caractere recu sur le bus I2C
}

void loop() {

  //If value received is a multiple of 2  turn on the led / si la valeur recu sur l I2C  est un multiple de 2 alors on allume la led
  if (x % 2 == 0) {
    digitalWrite(LED, HIGH);
  }

  //Else turn off the led / sinon eteindre la led
  else {
    digitalWrite(LED, LOW);
  }
} 

Comment fonctionne ce code ?

On spécifie l’adresse de l’équipement, ici 0x9 ou 9, les deux écritures fonctionnent.

Wire.begin(9);

Dès qu’un nouveau message est reçu par l’équipement, on exécute la méthode receiveEvent().

Wire.onReceive(receiveEvent);

Ici la méthode receiveEvent actualise une variable temporaire en lisant le message reçu

void receiveEvent() {
   x = Wire.read();                // read message from I2C / lire le message recu sur le bus I2C
}

A chaque passage dans la boucle loop(), la LED est allumée si la valeur de la variable x est paire et inversement.

Démonstration

Dès que le second Arduino démarre, la LED présente sur la carte se met à clignoter attestant que les deux Arduino communiquent entre eux via le bus I2C.

Exemple 2, faire communiquer trois Arduino via le bus I2C

On ajoute un deuxième Arduino (Mega) secondaire au réseau I2C.

3 cartes Arduino communiquent via le bus I2C

Le réseau est constitué

  1. Arduino Uno principal
  2. Arduino Uno secondaire à l’adresse 0x9 (9)
  3. Arduino Mega secondaire à l’adresse 0x01 (10)

IMG 2726 2

Code à téléverser sur l’Arduino Principal (1)

Le code est similaire au code précédent. On envoie la valeur d’une variable entière sur l’Arduino secondaire situé à l’adresse 0x0A sur le réseau I2C.

/* Arduino 1 code (main)
  Example of code for use I2C communication between two Arduino
  Arduino 1(Uno)   Arduino 2(Uno)   Arduino 3 (Mega)    
        A4             A4                 20
        A5             A5                 21
        GND           GND                 GND

    MIT License
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
    merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished
    to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Include the required Wire library for I2C / integrer la librairie Wire pour utiliser l I2C
#include <Wire.h> 

int x = 0; // creation of the variable x for stockage the state of the led on the secondary arduino / creation d une variable x qui stocke l etat de la led sur l'arduino secondaire

void setup() {
  // Start the I2C Bus / initialisation du bus I2C
  Wire.begin();
}

void loop() {
  Wire.beginTransmission(0x09);  // transmit to device #9 / transmission sur arduino 2 a l adresse 0x09 (=9 en decimale)
  Wire.write(x);                 // sends x / envoi de la valeur de x
  Wire.endTransmission();        // stop transmitting / arret de la transmission
  Wire.beginTransmission(0x0A);  // transmit to device #10 / transmission sur arduino 3 a l adresse 0x01 (=10 en decimale)
  Wire.write(x);                 // sends x / envoi de la valeur de x
  Wire.endTransmission();        // stop transmitting / arret de la transmission
  x++;                           // Increment x / incremente x
  delay(500);                    // delay of 500  milli secondes between two send / delay de 500 milli secondes entre 2 envoi sur le bus I2C
}

Code à téléverser sur l’Arduino secondaire n°2

Le code est inchangé

Code à téléverser sur l’Arduino secondaire n°3

L’Arduino Mega prend l’adresse 10 (0x0A).  La LED est allumée si x est paire et inversement.

/*Arduino 3 code
  Example of code for use I2C communication between tree Arduino
  Arduino 1(Uno)   Arduino 2(Uno)   Arduino 3 (Mega)    
        A4             A4                 20
        A5             A5                 21
        GND           GND                 GND

    MIT License
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
    merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished
    to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
// Include the required Wire library for I2C / integrer la librairie Wire pour utiliser l I2C
#include <Wire.h>

int LED = 13;
int x = 0;

void setup() {
  pinMode (LED, OUTPUT);          // Define the LED pin as Output / definir le pin de la led comme sortie

  Wire.begin(10);                  // Start the I2C Bus with address 10 in decimal (= 0x0A in hexadecimal) / initialisation du bus I2C avec comme adresse 9 en decimal soit 0x09 en hexadecimal

  Wire.onReceive(receiveEvent);   // Attach a function to trigger when something is received / Attacher une fonction a declencher lorsque quelque chose est recu
}

void receiveEvent() {
  x = Wire.read();                // read one character from the I2C / lire le caractere recu sur le bus I2C

}

void loop() {
  //If value received is a multiple of 2  turn on the led / si la valeur recu sur l I2C  est un multiple de 2 alors on allume la led
  if (x % 2 == 0) {
    digitalWrite(LED, LOW);
  }
  //Else turn off the led / sinon eteindre la led
  else {
    digitalWrite(LED, HIGH);
  }
}

Démonstration

Dès que l’Arduino Mega redémarre, la led commence à clignoter.

Mises à jour

24/08/2020 Première publication de l’article

Avez-vous aimé cet article ?
[Total: 2 Moyenne: 5]
Partager sur facebook
Partager sur twitter
Partager sur linkedin
Partager sur pinterest
Partager sur email
Partager sur telegram

Vous avez aimé ce projet ? Ne manquez plus aucun projet en vous abonnant à notre lettre d’information hebdomadaire!

quel modèle esp8266 choisir
Quel modèle d'ESP8266EX choisir en 2020 ?
guide choix esp32 development board
Quel ESP32 choisir en 2020 ?

Vous rencontrez un problème avec ce sujet ?

Peut-être que quelqu’un a déjà trouvé la solution, visitez le forum avant de poser votre question

Nous serions ravis de connaître votre avis

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Domotique et objets connectés à faire soi-même