Scanner I2C pour Arduino

Programme commenté pour un scanner de bus I2C avec une carte Arduino Uno

Introduction

Dès que l’on désire rajouter des possibilités à notre chère carte Arduino, pour un écran, Oled ou non, une horloge RTC ou autres périphériques, et pour moins de fils dans nos montages, on se tourne immanquablement vers le bus I2C.
Sur le web, pour ce sujet, il ne manque pas de tutoriels plus ou moins pertinents, tant sur la partie électronique que sur la partie logique et notre ami Google saura vous y mener sans faillir. :magicien:
Malgré tout cela, même après avoir beaucoup lu et collecté nombre d’articles traitant du sujet, il est difficile de trouver les informations nécessaires à une programmation pertinente et une bonne compréhension des mécanismes mis en œuvre.

Le but :

  • trouver les adresses I2c des périphériques car les achats faits en Chine sont souvent mal documentés.
  • connaitre la validité des périphériques connectés sur le bus (gestion des erreurs)
  • avoir un affichage des résultats du scan, sur port série, condensé, pertinent et résilient.
  • éventuellement, mieux comprendre le fonctionnement de la bibliothèque Wire.h

Le sketch Arduino de scanner de bus I2C développé ici, inspiré d’exemples trouvés sur le web, est simple mais efficace.
Après initialisation, les 127 adresses possibles du bus I2C sont scannées une à une et le résultat est affiché sur le port série, sous forme d’un tableau facilement interprétable.
Plusieurs périphériques reconnus

L’écran de résultat est divisé en trois partie :
  • Le titre, une bannière en code ASCII pour le fun (vieux souvenir :honte: )
  • Le tableau de résultat du scan de 16 lignes de 8 colonnes (de l’adresses 0 à 127)
  • Un résumé du scan (qui gère les situations et les pluriels selon les trouvailles. :waw: )
Les valeurs du tableau de résultats représentent :
  • . . . . Adresse scannée et non utilisée
  • 0xxx Périphérique valide et fonctionnel (valeur hexadécimale)
  • #### Périphérique présent mais en défaut

Pour relancer un scan du bus I2C, il suffit d’appuyer sur le bouton Reset de la carte Arduino.
Cela supprime toute valeur en mémoire, ré-initialise les registres internes et relance l’affichage sur le port série.

Vous trouverez le code complet en bas de page et un lien de téléchargement.
Le code n’est pas optimisé car écrit dans un but didactique et vous pourrez sans aucun doute faire mieux. :D

Bonne lecture et bon developpement !
Webby

Matériel utlisé

Pour réaliser ce scanner I2C, le matériel suivant à été utilisé :

Le module horloge RTC et l’écran Oled ne sont la que pour leurs adresses I2C.
Tout autre périphérique I2C fera l’affaire.
Sans périphérique I2C, la détection sera plus compliquée…forcément ! :p

Le montage

(Montage de l'ensemble

Le montage ne recèle pas de difficultés particulières.
Le module écran Oled et le module RTC sont enfichés sur une platine de prototypage sans soudure (breadboard).
Deux résistances de tirage, de 10 K ohms 5% chacune (code couleur Marron, Noir, Orange, Or), sur les lignes SDA et SCL du bus I2C complètent le montage. Si vous n’utilisez pas plusieurs périphériques I2C, vous pouvez activer les resistances de pull-up internes à la carte Arduino. Elles devrait être suffisantes.
La breadboard utilisée comporte deux zones d’alimentation, matérialisées par des filets rouges pour les tensions positives et des filets bleus pour le 0V/Gnd.
La zone d’alimentation supérieure (en haut de la photo du montage) sert pour l’alimentation des modules.
La zone d’alimentation inférieure (en bas de la photo du montage) sert comme bus I2C, pour éviter la prolifération des fils de connexion.

Photos du montage : Montage de l'ensemble Montage - Breadboard Montage - Résistances de tirage Zone d'alimentation du montage Zone I2C

Le cablâge

Le câblage de la carte Arduino se résume à 4 fils :

  • 1 fil rouge entre la Broche 5V et le pôle positif de la zone d’alimentation en haut de la breadbord (surmontée d’un filet rouge)
  • 1 fil noir entre la broche GND et le pôle négatif de la zone d’alimentation en haut de la breadbord (soulignée d’un filet Bleu)

  • 1 fil mauve entre la Broche A5 (SCL) et le pôle positif de la zone d’alimentation en bas de la breadbord (surmontée d’un filet rouge)

  • 1 fil jaune entre la broche A4 (SDA) et le pôle négatif de la zone d’alimentation en bas de la breadbord (soulignée d’un filet Bleu)

Les fils du bus I2C sont "twistés" (tortillés ensemble façon scoubidou) pour réduire la sensibilité aux parasites (surtout) et discipliner le câblage en lui donnant plus de rigidité (un peu).
Quelques straps (les fils rigides plaqués sur la breadboard) ont assurés les liaisons manquantes.

Vue de la platine d'essai

Le schéma

Schéma de câblage

Le code expliqué

Celui-ci est assez simple, ne comporte pas d’astuces et est accessible, même au plus débutant pour peu qu’il maîtrise le minimum.
Le code est copieusement commenté pour les plus novices, toute les lignes (ou presque) le sont, même les plus basiques.
Chaque traitement est ainsi plus compréhensible même si cela nuit un peu à la lisibilité.
Dans les explications suivantes, les portions de code citées ne sont pas forcément contiguës. Pour certains passages et pour plus de clarté, des lignes non nécessaires ont été supprimées. Elles figureront dans d’autres explications.

Le programme est divisé en quatre sections:
  • Les commentaires, en en-tête du programme pour explications de celui-ci
  • La partie déclarative, réduite à la seule inclusion de la librairie Wire.h
  • La fonction setup(), qui contient tout le code (bannière,scan et résumé)
  • la fonction loop(), inutilisée mais qui doit être présente

La fonction loop() n’est pas utilisée car tout le code est dans la fonction setup()pour avoir une seule exécution du programme, sans boucle. Ainsi l’affichage est condensé sur le terminal série et ne défile pas (voir les copies d’écrans en fin de rubrique Résumé).

Les variables sont déclarée dans la fonction setup().
Elles ne sont donc valables que pour cette fonction !
Pensez-y si vous devez modifier ce programme.

Synoptique du programme

Très simple, il se résume à 3 blocs:

Synoptique de programmation

Les commentaires en en-tête

Mettre des commentaires en tout début du programme, est une bonne habitude à prendre.
Ils vous permettent une bonne introduction en indiquant le but du programme, sa philosophie, sa version, …
Bref on peut y mettre une information servant à éclairer celui qui découvre autant que celui qui redécouvre ;) comme :

  • le but et les moyens du programme
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* SCANNER I2C  Version 2.00 --------------------------------------------

   Scan le bus i2c  a la recherche de peripheriques.
   Scan des adresses 1 (0x01) a 127 (0x7F)
   L'adresse 0 (reservee) est simulee et non lue.

   Affichage du resultat en grille de 16 lignes de 8 colonnes (16x8=128).

   L'affichage de chaque adresse est signalee par :
    - son adresse en hexadecimale (0x00) si elle est  occupee,
    - 4 dieses (####) si elle est  occupee par un peripherique en defaut
    - 4 points (....) si elle est inoccupee,

   Un resume indique, en fin de programme, le nombre de peripheriques trouves
   et, eventuellement, le nombre de peripheriques en defaut.
  • la mise en œuvre
1
2
3
4
5
/*
  Pour refaire un scan, il suffit d'appuyer sur le bouton Reset car :
    - tout le code est dans la fonction setup()
    - la fonction loop() n'est pas utilisee
*/
  • les améliorations possibles
1
2
3
4
5
6
7
8
9
/*
   Ameliorations possibles :
    - Affichage en hexadecimal des adresses en defaut
    - Affichage des adresses en hexadecimmal et des valeurs de retour
      de la fonction  Wire.endTransmission()
    - Affichage du type de peripherique detecte
    - ...

  ----------------------------------------------------------------------*/

La partie déclarative

Généralement, l’en-tête du programme sert à inclure les bibliothèques, déclarer et initialiser les variables globales, puis initialiser les objet comme un port série, un bus I2c ou 1Wire, une horloge RTC…

Petit rappel :
- les variables déclarées dans l’en-tête ont une portée globales et sont accessibles à toutes les fonctions.
- les variables déclarées dans une fonction ne sont valables que pour celle-ci

Inclusion de la bibliothèque Wire.h pour la gestion du bus I2C

L’inclusions d’une bibliothèque externe doit figurer avant la déclaration des variables et l’initialisation des objets car certains de ceux-ci dépendent des bibliothèques incluses.
La gestion du bus I2C est facilité par la bibliothèque Wire.h qui est fournie avec l’IDE Arduino. C’est la seule dont nous aurons besoin.
Pour l’inclure, toujours sous l’IDE Arduino, dans la fenêtre d’édition, du code positionnez votre curseur à l’endroit voulu puis Menu [Croquis] [Inclure une bibliothèque][Wire].
Inclure la bibliothèque Wire.h sous l'IDE Arduino

1
#include <Wire.h> /* Indispensable pour la gestion de l'I2C */.

D’elle, nous utiliserons seulement 3 fonctions :

  • Wire.begin pour l’initialisation de la communication I2C
  • Wire.beginTransmission() pour un dialogue à l’adresse indiquée
  • Wire.endTransmission() pour la récupération de la réponse
1
 Wire.begin();       /* Initialise le bus i2C */
1
2
3
4
5
6
7
8
    Wire.beginTransmission(adresse);    /* Commence une transmission a l'adresse indiquee */
    resultat = Wire.endTransmission();
    /* resultat = statut de la transmission :
        0 : succès (peripherique OK)
        1 : donnée trop longue pour le buffer d'émission (erreur)
        2 : NACK reçu sur l'adresse de transmission (pas de peripherique)
        3 : NACK reçu sur la transmission de donnée (pas de peripherique)
        4 : autre erreur  (erreur donc...) */
Déclaration des variables

Les déclarations et l’initialisation des variables se font dans la fonction setup() car c’est la seule du programme.
Elles pourraient figurer dans la partie déclarative, pour nous cela ne changerait rien.
Sous la déclaration des variables, il y a l’initialisation des objets Wire (bus I2C) et Serial (port série).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void setup()
{
  /* Les variables du programme */
  byte adresse;       /* variables de gestion des adresses */
  byte resultat;      /* Variable de retour d'interrogation d'une adresse */
  byte nb_existe = 0; /* compteur de peripheriques trouves */
  byte nb_defaut = 0; /* compteur de peripheriques en defaut */

  Wire.begin();       /* Initialise le bus i2C */
  Serial.begin(9600); /* Initialise le port serie */

`

La bannière

Quand seul le mode texte existait, les programmeurs rêvaient d’égayer les tristes écrans (monochrome de surcroît) des applications.
Leur astuce a été d’utiliser un affichage de caractères sur plusieurs lignes pour former une représentation plus graphique qu’un texte seul. Ainsi est né ce que l’on nomma l’ASCII Art.
L’ASCII est un tableau de définition de caractères (explication sur Wikipédia), mais je suis sûr que je ne vous apprend rien ;) .
Le moniteur série ne permettant que l’affichage texte, et ne voulant pas me contenter d’un "bête" titre, même agrémenté d’un cadre lui aussi en texte, j’ai donc choisi de faire revivre cette solution…pour un résultat plutôt sympa, non ?

En langage C, pour afficher dans une chaîne de caractères les signes ’(guillemet), "(double guillemet) ou \ (antislash),
il faut les préfixer avec le caractère \. C’est à dire qu’il faut écrire \’ \" ou \\.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  /* Petite banniere en ASII... juste pour le fun
     Veuillez bien recopier les lignes ci-dessous pour un affichage correct
     PS: n'oubliez pas que le \ doit etre precede d'un \ pour son affichage */

  Serial.println("  ____                             ___   ____     ____ ");
  Serial.println(" / ___|    ___    __ _   _ __     |_ _| |___ \\   / ___|");
  Serial.println(" \\___ \\   / __|  / _` | | '_ \\     | |    __) | | |    ");
  Serial.println("  ___) | | (__  | (_| | | | | |    | |   / __/  | |___ ");
  Serial.println(" |____/   \\___|  \\__,_| |_| |_|   |___| |_____|  \\____|");
  Serial.println();

Voici ce que cela donne :
Banniere en code ASCII du scanner I2C

Le scan

Le démarrage automatique du scan est signalé par le message "Debut du scan".
La visualisation du résultat se fait en tableau de 16 lignes de huit colonnes (soit 128 cases).

Question : Pourquoi l’affichage comporte 128 cases alors que l’on scanne 127 adresses (1 à 127)  ?

1
for (adresse = 1; adresse < 128; adresse++ )

Réponse  : Parce que c’est plus beau d’avoir un tableau complet :p  !

Qestion  : Alors pourquoi on ne commence pas le scan à partir de l’adresse 0x00  ?
Réponse : Parce que l’adresse 0x00 est réservée. Elle a une fonction spéciale : "message à tous les périphériques"

Qestion  : Donc elle n’est pas testée mais simulée à l’affichage en première case du tableau  ?
Réponse : Hé oui, t’as tout compris :D  !

Le tableau est décalé sur la gauche de l’affichage d’une tabulation (\t) pour une meilleure visibilité.

1
2
3
4
  /* Debut de la grille d'affichage ou on simule l'adresse 0 par 4 points (.... )
     car elle est reservee par les fabriquants pour des fonctions speciales.
     PS: le tableau est decale d'une tabulation pour une meilleure lisibilite */
  Serial.print("\t.... ");
1
2
3
4
5
6
7
    if (((adresse + 1) % 8) == 0) /* Pour formatage de l'affichage en 8 colonnes */
      /* le +1 c'est parce que l'on part de l'adresse 1 et non de l'adresse 0 */
    {
      Serial.println();   /* Passage a la ligne apres avoir affiche 8 adresses */
      if (adresse < 127)Serial.print("\t");
      /* tabulation pour la mise en forme de l'affichage sauf apres la derniere adresse */
    }

Les adresses sont testées les unes après les autres, de 1 à 127.

1
Wire.beginTransmission(adresse);    /* Commence une transmission a l'adresse indiquee */

Le résultat est récupéré par la fonction Wire.endTransmission().

1
2
3
4
5
6
7
    resultat = Wire.endTransmission();
    /* resultat = statut de la transmission :
        0 : succès (peripherique OK)
        1 : donnée trop longue pour le buffer d'émission (erreur)
        2 : NACK reçu sur l'adresse de transmission (pas de peripherique)
        3 : NACK reçu sur la transmission de donnée (pas de peripherique)
        4 : autre erreur  (erreur donc...) */

Il est représenté par :

.... pour une adresse vide
0x00 l’adresse d’un périphérique valide
#### pour un périphérique présent mais en défaut

Quand un périphérique est trouvé, la variable nb_existe est incrémentée et s’il est en défaut, on incrémente aussi la variable nb_defaut.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    /* Traitement du resultat */
    if (resultat == 0)  /* Il y a un peripherique OK a cette adresse */
    {
      /* Mise en forme et affichage de l'adresse du peripherique en Hexadecimal */
      if (adresse < 16)Serial.print("0x0");
      else Serial.print("0x");
      Serial.print(adresse, HEX);
      Serial.print(" ");
      nb_existe++; /* Increment le nombre de peripheriques trouves */
    }
    else if ((resultat == 4) || (resultat == 1)) /* Indication d'un peripherique en defaut */
    {
      Serial.print("#### ");
      nb_existe++;  /* Increment le nombre de peripheriques trouves */
      nb_defaut++;  /* Incremente le nombre de peripheriques en defaut */
    }
    else  Serial.print(".... ");/* adresse inoccupee */
    /* resultat = 2 ou 3 (NAK) -> pas de reponse d'un peripherique*/

Les adresses inférieures à 16 (en décimal) sont mises en forme sur 4 digits pour l’esthétisme et le parfait alignement du tableau..

1
2
3
4
5
      /* Mise en forme et affichage de l'adresse du peripherique en Hexadecimal */
      if (adresse < 16)Serial.print("0x0");
      else Serial.print("0x");
      Serial.print(adresse, HEX);
      Serial.print(" ");
Code complet du scan :
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  Serial.println("\nDebut du scan"); /* Affiche un saut de ligne puis le debut du scan */

  /* Debut de la grille d'affichage ou on simule l'adresse 0 par 4 points (.... )
     car elle est reservee par les fabriquants pour des fonctions speciales.
     PS: le tableau est decale d'une tabulation pour une meilleure lisibilite */
  Serial.print("\t.... ");

  /* Demarre le scan */
  for (adresse = 1; adresse < 128; adresse++ )
    /* Il y a 128 adresses disponibles au maximun (de 0 a 127)
        rappel : on ne teste pas l'adresse 0 (voir ci-dessus) */
  {
    Wire.beginTransmission(adresse);    /* Commence une transmission a l'adresse indiquee */
    resultat = Wire.endTransmission();
    /* resultat = statut de la transmission :
        0 : succès (peripherique OK)
        1 : donnée trop longue pour le buffer d'émission (erreur)
        2 : NACK reçu sur l'adresse de transmission (pas de peripherique)
        3 : NACK reçu sur la transmission de donnée (pas de peripherique)
        4 : autre erreur  (erreur donc...) */

    /* Decommenter les lignes ci-dessous pour test de peripherique en defaut
       Adaptez les adresses (de 1 a 127) en fonction de votre configuration */
     //if (adresse == 17) resultat = 4;
     //if (adresse == 93) resultat = 1;

    /* Traitement du resultat */
    if (resultat == 0)  /* Il y a un peripherique OK a cette adresse */
    {
      /* Mise en forme et affichage de l'adresse du peripherique en Hexadecimal */
      if (adresse < 16)Serial.print("0x0");
      else Serial.print("0x");
      Serial.print(adresse, HEX);
      Serial.print(" ");
      nb_existe++; /* Increment le nombre de peripheriques trouves */
    }
    else if ((resultat == 4) || (resultat == 1)) /* Indication d'un peripherique en defaut */
    {
      Serial.print("#### ");
      nb_existe++;  /* Increment le nombre de peripheriques trouves */
      nb_defaut++;  /* Incremente le nombre de peripheriques en defaut */
    }
    else  Serial.print(".... ");/* adresse inoccupee */
    /* resultat = 2 ou 3 (NAK) -> pas de reponse d'un peripherique*/

    /* Decommentez la ligne ci-dessous si vous avez des erreurs de lecture */
    //delay(25); /* temps d'attente pour ne pas generer d'erreur de lecture */

    if (((adresse + 1) % 8) == 0) /* Pour formatage de l'affichage en 8 colonnes */
      /* le +1 c'est parce que l'on part de l'adresse 1 et non de l'adresse 0 */
    {
      Serial.println();   /* Passage a la ligne apres avoir affiche 8 adresses */
      if (adresse < 127)Serial.print("\t");
      /* tabulation pour la mise en forme de l'affichage sauf apres la derniere adresse */
    }
    /* Fin du traitement du resultat et passage a l'adresse suivante */
  }

Le résumé

En fin de scan, un résumé mis en forme (gestion du singulier et des pluriels, point terminal en fin de phrase) indique :

  • le nombre (ou l’absence) de périphériques I2C reconnus
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* Affichage des resultats complementaires **/
  if (nb_existe == 0)Serial.println("Aucun peripherique I2C !"); /* Pas de peripherique I2c */
  else
  {
    /* Affichage d'un resume sous la forme :
        X peripherique(s) reconnu(s).
       ou
        X peripherique(s) reconnu(s) dont X peripherique(s) en defaut.
       avec gestion des pluriels et du point terminal */

    Serial.print(nb_existe); /* nombre de peripheriques I2c reconnus */
    if (nb_existe < 2)Serial.print(" peripherique reconnu"); /* affichage pour un seul */
    else Serial.print(" peripheriques reconnus");         /* affichage pour plusieurs */
  • éventuellement, le nombre de périphériques en défaut
1
2
3
4
5
6
7
8
if (nb_defaut != 0)  /* S'il y a des peripheriques en defaut */
    {
      Serial.print(" dont ");  /* poursuit la phrase de resume */
      Serial.print(nb_defaut); /* Nombre de peripheriques en defaut */
      if (nb_defaut < 2)Serial.println(" peripherique en defaut."); /* affichage pour un seul */
      else Serial.println(" peripheriques en defaut.");             /* affichage pour plusieurs */
    }
 else Serial.println("."); /* Termine la phrase par un point s'il n'y a pas de peripherique en defaut */
  • la façon de relancer un scan
1
2
3
  /* Message de fin de scan */
  Serial.println("\nAppuyez sur le bouton Reset de l'Arduino pour recommencer.\n");
  Serial.println(); /* Saut de ligne supplémentaire */
Visuels des différents écrans de résultat possibles :

1  péripherique reconnu Pas de périphériques reconnus Plusieurs périphériques reconnus Un périphérique reconnu mais en défaut. Plusieurs périphériques reconnus dont 1 en défaut. Plusieurs périphériques reconnus dont plusieurs en défauts.
Cliquez sur une des images ci-dessus

Le code complet, copiable et téléchargeable

Voici le code complet, copieusement documenté (presque chaque ligne en fait :p )
Vous pouvez faire un copier-coller, depuis cette fenêtre, jusqu’à votre éditeur.
Si vous préférez la version toute faite, cliquez ici !

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* SCANNER I2C  Version 2.00 --------------------------------------------

   Scan le bus i2c  a la recherche de peripheriques.
   Scan des adresses 1 (0x01) a 127 (0x7F)
   L'adresse 0 (reservee) est simulee et non lue.

   Affichage du resultat en grille de 16 lignes de 8 colonnes (16x8=128).

   L'affichage de chaque adresse est signalee par :
    - son adresse en hexadecimale (0x00) si elle est  occupee,
    - 4 dieses (####) si elle est  occupee par un peripherique en defaut
    - 4 points (....) si elle est inoccupee,

   Un resume indique, en fin de programme, le nombre de peripheriques trouves
   et, eventuellement, le nombre de peripheriques en defaut.

   Pour refaire un scan, il suffit d'appuyer sur le bouton Reset car :
    - tout le code est dans la fonction setup()
    - la fonction loop() n'est pas utilisee

   Ameliorations possibles :
    - Affichage en hexadecimal des adresses en defaut
    - Affichage des adresses en hexadecimmal et des valeurs de retour
      de la fonction  Wire.endTransmission()
    - Affichage du type de peripherique detecte
    - ...

  ----------------------------------------------------------------------*/

#include <Wire.h> /* Indispensable pour la gestion de l'I2C */

void setup()
{
  /* Les variables du programme */
  byte adresse;       /* variables de gestion des adresses */
  byte resultat;      /* Variable de retour d'interrogation d'une adresse */
  byte nb_existe = 0; /* compteur de peripheriques trouves */
  byte nb_defaut = 0; /* compteur de peripheriques en defaut */

  Wire.begin();       /* Initialise le bus i2C */
  Serial.begin(9600); /* Initialise le port serie */

  /* Petite banniere en ASII... juste pour le fun
     Veuillez bien recopier les lignes ci-dessous pour un affichage correct
     PS: n'oubliez pas que le \ doit etre precede d'un \ pour son affichage */
  Serial.println("  ____                             ___   ____     ____ ");
  Serial.println(" / ___|    ___    __ _   _ __     |_ _| |___ \\   / ___|");
  Serial.println(" \\___ \\   / __|  / _` | | '_ \\     | |    __) | | |    ");
  Serial.println("  ___) | | (__  | (_| | | | | |    | |   / __/  | |___ ");
  Serial.println(" |____/   \\___|  \\__,_| |_| |_|   |___| |_____|  \\____|");
  Serial.println();

  Serial.println("\nDebut du scan"); /* Affiche un saut de ligne puis le debut du scan */

  /* Debut de la grille d'affichage ou on simule l'adresse 0 par 4 points (.... )
     car elle est reservee par les fabriquants pour des fonctions speciales.
     PS: le tableau est decale d'une tabulation pour une meilleure lisibilite */
  Serial.print("\t.... ");

  /* Demarre le scan */
  for (adresse = 1; adresse < 128; adresse++ )
    /* Il y a 128 adresses disponibles au maximun (de 0 a 127)
        rappel : on ne teste pas l'adresse 0 (voir ci-dessus) */
  {
    Wire.beginTransmission(adresse);    /* Commence une transmission a l'adresse indiquee */
    resultat = Wire.endTransmission();
    /* resultat = statut de la transmission :
        0 : succès (peripherique OK)
        1 : donnée trop longue pour le buffer d'émission (erreur)
        2 : NACK reçu sur l'adresse de transmission (pas de peripherique)
        3 : NACK reçu sur la transmission de donnée (pas de peripherique)
        4 : autre erreur  (erreur donc...) */

    /* POUR TEST UNIQUEMENT : (on a pas forcement de peripherique en defaut sous la main)
     *  Decommenter une ou les lignes ci-dessous pour simuler un ou plusieurs peripherique en defaut
     *  Adaptez les adresses (de 1 a 127) en fonction de votre configuration
     *  Adapter les resultats en fonction du traitement que vous envisagez */
    //if (adresse == 17) resultat = 4; /* simule une erreur autre que 1,2 ou 3 */
    //if (adresse == 93) resultat = 1; /* simule une erreur de données */

    /* Traitement du resultat */
    if (resultat == 0)  /* Il y a un peripherique OK a cette adresse */
    {
      /* Mise en forme et affichage de l'adresse du peripherique en Hexadecimal */
      if (adresse < 16)Serial.print("0x0");
      else Serial.print("0x");
      Serial.print(adresse, HEX);
      Serial.print(" ");
      nb_existe++; /* Increment le nombre de peripheriques trouves */
    }
    else if ((resultat == 4) || (resultat == 1)) /* Indication d'un peripherique en defaut */
    {
      Serial.print("#### ");
      nb_existe++;  /* Increment le nombre de peripheriques trouves */
      nb_defaut++;  /* Incremente le nombre de peripheriques en defaut */
    }
    else  Serial.print(".... ");/* adresse inoccupee */
    /* resultat = 2 ou 3 (NAK) -> pas de reponse d'un peripherique*/

    /* Decommentez la ligne ci-dessous si vous avez des erreurs de lecture */
    //delay(25); /* temps d'attente pour ne pas generer d'erreur de lecture */

    if (((adresse + 1) % 8) == 0) /* Pour formatage de l'affichage en 8 colonnes */
      /* le +1 c'est parce que l'on part de l'adresse 1 et non de l'adresse 0 */
    {
      Serial.println();   /* Passage a la ligne apres avoir affiche 8 adresses */
      if (adresse < 127)Serial.print("\t");
      /* tabulation pour la mise en forme de l'affichage sauf apres la derniere adresse */
    }
    /* Fin du traitement du resultat et passage a l'adresse suivante */
  }

  /* Fin du scan */
  Serial.println("Fin du Scan\n");

  /* Affichage des resultats complementaires **/
  if (nb_existe == 0)Serial.println("Aucun peripherique I2C !"); /* Pas de peripherique I2c */
  else
  {
    /* Affichage d'un resume sous la forme :
        X peripherique(s) reconnu(s).
       ou
        X peripherique(s) reconnu(s) dont X peripherique(s) en defaut.
       avec gestion des pluriels et du point terminal */

    Serial.print(nb_existe); /* nombre de peripheriques I2c reconnus */
    if (nb_existe < 2)Serial.print(" peripherique reconnu"); /* affichage pour un seul */
    else Serial.print(" peripheriques reconnus");         /* affichage pour plusieurs */

    if (nb_defaut != 0)  /* S'il y a des peripheriques en defaut */
    {
      Serial.print(" dont ");  /* poursuit la phrase de resume */
      Serial.print(nb_defaut); /* Nombre de peripheriques en defaut */
      if (nb_defaut < 2)Serial.println(" peripherique en defaut."); /* affichage pour un seul */
      else Serial.println(" peripheriques en defaut.");             /* affichage pour plusieurs */
    }
    else Serial.println("."); /* Termine la phrase par un point s'il n'y a pas de peripherique en defaut */
  }
  /* Message de fin de scan */
  Serial.println("\nAppuyez sur le bouton Reset de l'Arduino pour recommencer.\n");
  Serial.println(); /* Saut de ligne supplémentaire */
}


void loop()
{
  /* Rien ici car tout s'execute dans le setup ! */
}

`

N’hésitez pas à me faire part de vos retour.
N’hésitez pas non plus à diffuser ce code, il doit servir à tous.

5 commentaires

Bonjour

J’ai un souci avec ce code ou du moins avec le scan de mon bus I2C. J’ai essayé différents codes de scan du bus I2C et j’ai toujours la même réponse : aucun périphérique I2C. J’utilise un Arduino Uno et j’ai testé avec différents capteurs (bus I2C connecté et capteur alimenté). Capteur BMP280, un écran OLED, un RTC, tous ensemble ou bien séparément. J’ai mis des résistances (4.7k) tirées au + sur les lignes CLK et SDA, mais toujours le même résultat. J’ai pris les pins A4(SDA) et A5(CLK), j’ai éssayé aussi avec les 2 pins après la pin AREF, toujours la même chose. Une idée ? Merci

Merci pour cela.

Je suggère l’ajout d’une ligne dans l’en-tête, une ligne référant à la présente page. Ainsi:

/* SCANNER I2C Version 2.00 ——————————————————————

Scan le bus i2c a la recherche de peripheriques. Scan des adresses 1 (0x01) a 127 (0x7F) L’adresse 0 (reservee) est simulee et non lue. Source: https://zestedesavoir.com/billets/1824/scanner-i2c-pour-arduino/#5-le-code-complet-copiable-et-telechargeable […] */

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte