Ecran i2c sur Arduino esclave

dimanche 17 juillet 2016
par  Finizi
popularité : 22%

L’utilisation d’un petit écran Oled avec la librairie u8glib nécessite, selon les polices utilisée, une place importante dans la ROM. Tellement importante, qu’il ne reste parfois plus de place pour implémenter les fonctions principales prévues. Une des solutions consiste à utiliser un second Arduino, esclave, dédié à l’affichage, et relié à l’Arduino principal grâce au bus i2c. L’écran oled est lui aussi sur le même bus i2c.


 Le matériel

Le matériel utilisé est composé de :


 Le montage

Pour la démo, les 2 Arduino nano sont enfichés dans un grand Breadboard, avec l’écran oled entre les deux.
8 fils suffisent pour relier :

  • les masses des Arduinos et de l’écran
  • les alimentations +5V des Arduinos et de l’écran
  • les bornes A4 des Arduinos à la borne SDA de l’écran
  • les bornes A5 des Arduinos à la borne SCl de l’écran
    PNG - 99.5 ko

 Le programme maître

Ici, c’est relativement basique.
C’est juste un sketch qui envoie à l’Arduino esclave des positions aléatoires et des ordres d’affichage de message prédéfinis.
Le bus i2c n’étant pas toujours disponible, une vérification de la bonne réception est effectuée afin de renvoyer éventuellement les ordres d’affichage.


 Le programme esclave

L’Arduino esclave écoute le bus i2c.
Quand nécessaire, il prend le contrôle du bus afin de piloter l’afficheur.
Toute la ROM est disponible pour héberger des polices de caractère.


 Les codes complets

Le code est aussi disponible sur GitHub.

Et voici l’intégralité du code de l’Arduino maître.

  1.  
  2. // --- Programme Arduino ---
  3. // Copyright finizi - Created july 2016 the 1st
  4. // www.DomoEnergyTICs.com
  5. //  Code sous licence GNU GPL :
  6. //  This program is free software: you can redistribute it and/or modify
  7. //  it under the terms of the GNU General Public License as published by
  8. //  the Free Software Foundation, either version 3 of the License,
  9. //  or any later version.
  10. //  This program is distributed in the hope that it will be useful,
  11. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //  GNU General Public License for more details.
  14. //  You should have received a copy of the GNU General Public License
  15. //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. /*************************************************************
  18. I2C master
  19. Envoie via le bus i2c des informations à afficher sur un écran également i2c,
  20. typiquement, un écran oled utilisant la librairie "U8glib".
  21. L'utilisation de la librairie "U8glib", et notamment de différentes polices,
  22. peut amener a utiliser une grande partie de la ROM disponible.
  23. Pour palier cela, une des possibilité est d'utiliser un Arduino dédié à l'affichage,
  24. qui va gérer l'affichage et uniquement l'affichage, en fonction des informations
  25. reçues via le bus i2c.
  26. Cet exemple concerne le code qui ne fait qu'envoyer les informations à afficher.
  27. Envoie des informations : exemples de coordonnées géographiques et de message
  28. *************************************************************/
  29. #include <Wire.h>
  30. /*************************************************************
  31. *   Les constantes
  32. */
  33. const byte i2cRemoteDisplay = 0x08;
  34. /*************************************************************
  35. *   Déclaration des variables globales
  36. */
  37. int x = 0;
  38. int y = 0;
  39. /**************************************************************************
  40. **
  41. **  setup()  ==>  Fonction obligatoire d'initialisation
  42. **
  43. **************************************************************************/
  44. void setup() {
  45.   Serial.begin(115200);
  46.   Serial.println("Serial Init !");
  47.   Serial.println("==> i2cMaster <==");
  48.   // Joindre le bus i2c en tant que maitre
  49.   Wire.begin();
  50.   Serial.println("Wire begin !");
  51. }
  52. /**************************************************************************
  53. **
  54. **  loop()  ==>  Fonction obligatoire. Boucle infinie de fonctionnement
  55. **
  56. **************************************************************************/
  57.  
  58. void loop() {
  59.   // On envoie de nouvelle coordonnées à chaque fois
  60.   i2cSendCoordinate(i2cRemoteDisplay, "Lat", 47 + (random(1000) / 99.0) );
  61.   i2cSendCoordinate(i2cRemoteDisplay, "Lng", -3 - (random(1000) / 99.0) );
  62.   x++;
  63.   switch (x) {
  64.     case 5:
  65.       // Envoyer le message à afficher
  66.       i2cSendString(i2cRemoteDisplay, "Msg", String(y) );
  67.       y = y == 0 ? 1 : 0;
  68.       // Afficher le message
  69.       i2cSendString(i2cRemoteDisplay, "Disp", "Message" );
  70.       break;
  71.     case 10:
  72.       // afficher autre chose
  73.       i2cSendString(i2cRemoteDisplay, "Disp", "toto" );
  74.       x = 0;
  75.       break;
  76.     default:
  77.       // Afficher les coordonnées précédement envoyées
  78.       i2cSendString(i2cRemoteDisplay, "Disp", "Coordinates" );
  79.       break;
  80.   }
  81.   delay(1000);
  82. }
  83. /**************************************************************************
  84. **
  85. **  Fonctions diverses
  86. **
  87. **************************************************************************/
  88.  
  89. // Convertion d'un float en String
  90. String floatToString(float val, unsigned char dec) {
  91.   char str[30];
  92.   dtostrf(val, 1, dec, str);
  93.   return(String(str));
  94. }
  95. // Envoie d'une chaine de caractères
  96. void i2cSendString(int i2cAddrs, String what, String txt) {
  97.   byte n;
  98.   byte error;
  99.   // On va essayer 5 fois, car l'esclave n'est pas toujours à l'écoute
  100.   for (int i = 0; i < 5; i++) {
  101.     Wire.begin();                // Reprendre le contrôle du bus i2c à chaque fois
  102.     Wire.beginTransmission(i2cAddrs);
  103.     Wire.write(what.c_str());
  104.     Wire.write("=");  
  105.     Wire.write(txt.c_str());
  106.     error = Wire.endTransmission();    // stop transmitting
  107.     if (error == 0) {
  108.       // Si error vaut 0, c'est que c'est passé, ne pas réessayer
  109.       break;
  110.     }
  111.     // Il est possible que l'arduino esclave soit occupé à afficher
  112.     delay(100);    // On attend un peu avant nouvel essai
  113.   }
  114. }
  115. // Envoie d'une coordonnée (latitude ou longitude)
  116. void i2cSendCoordinate(int i2cAddrs, String what, float val) {
  117.   i2cSendString(i2cAddrs, what, floatToString(val, 6));
  118. }

Et voici l’intégralité du code de l’Arduino esclave.

  1.  
  2. // --- Programme Arduino ---
  3. // Copyright finizi - Créé le 1er juillet 2016
  4. // www.DomoEnergyTICs.com
  5. //  Code sous licence GNU GPL :
  6. //  This program is free software: you can redistribute it and/or modify
  7. //  it under the terms of the GNU General Public License as published by
  8. //  the Free Software Foundation, either version 3 of the License,
  9. //  or any later version.
  10. //  This program is distributed in the hope that it will be useful,
  11. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //  GNU General Public License for more details.
  14. //  You should have received a copy of the GNU General Public License
  15. //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. /*************************************************************
  18. I2C display
  19. Réception via le bus i2c des informations à afficher sur un écran également i2c,
  20. typiquement, un écran oled en utilisant la librairie "U8glib".
  21. L'utilisation de la librairie "U8glib", et notamment de différentes polices,
  22. peut amener a utiliser une grande partie de la ROM disponible.
  23. Pour palier cela, une des possibilité est d'utiliser un Arduino dédié à l'affichage,
  24. qui va gérer l'affichage et uniquement l'affichage, en fonction des informations
  25. reçues via le bus i2c.
  26. Cet exemple concerne le code qui gère l'affichage.
  27.  - Réception des informations
  28.  - Afficher les informations demandée par l'autre arduino
  29. *************************************************************/
  30. #include "U8glib.h"
  31. #include <Wire.h>
  32. /*************************************************************
  33. *   Déclaration des objets
  34. */
  35. U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST);  // Fast I2C / TWI
  36. /*************************************************************
  37. *   Les constantes
  38. */
  39. const byte i2cRemoteDisplay = 8; //0x08;
  40. const String messages[2] = {
  41.   "Message n" + String(char(176)) + " 1",
  42.   "Second message"
  43.   };  
  44. /*************************************************************
  45. *   Déclaration des variables globales
  46. */
  47. int message = 0;
  48. double gpsLng = -999.0;
  49. double gpsLat = 99.0;
  50. String toDisp = "Coordinates";
  51. /**************************************************************************
  52. **
  53. **  Fonctions exécutées suite à une évènement
  54. **
  55. **************************************************************************/
  56.  
  57. // function that executes whenever data is received from master
  58. void receiveEvent(int howMany) {
  59.   String str = "";
  60.   // Construction de la chaine reçue, caractère par caractère
  61.   while (Wire.available() > 0) {
  62.     char c = Wire.read();
  63.     str += String(c);
  64.   }
  65.   Serial.print("receiveEvent : ");
  66.   Serial.println(str);
  67.   i2cParse(str);
  68. }
  69. /**************************************************************************
  70. **
  71. **  setup()  ==>  Fonction obligatoire d'initialisation
  72. **
  73. **************************************************************************/
  74. void setup(void) {
  75.   Serial.begin(115200);             // Initialiser le port série pour le terminal
  76.   Serial.println("Serial Init !");
  77.   oledClear();                      // Efface l'écran
  78.   oledDrawStr("oled setup", 0, 20); // Affiche une info
  79.   Serial.println("oLed init !");
  80.   delay(500);
  81.   Wire.begin(i2cRemoteDisplay);     // Initialisation du bus i2c
  82.   Wire.onReceive(receiveEvent);     // On commence à écouter l'évènement
  83.   Serial.println("i2c init !");
  84. }
  85. /**************************************************************************
  86. **
  87. **  loop()  ==>  Fonction obligatoire. Boucle infinie de fonctionnement
  88. **
  89. **************************************************************************/
  90.  
  91. void loop(void) {
  92.   //Wire.begin(i2cRemoteDisplay);     // Initialisation du bus i2c
  93.   //oledDraw();                       // Affiche ce qui est demandé par le maitre
  94.   Serial.println(millis());         // Envoie une valeur au terminal pour monter que ça tourne
  95.   delay(1000);                      // Attendre une seconde. L'écra sera au mieux mis à jour toutes les secondes
  96. }
  97. /**************************************************************************
  98. **
  99. **  Fonctions diverses
  100. **
  101. **************************************************************************/
  102.  
  103. // Analyse de la chaine reçue
  104. void i2cParse(String s) {
  105.   int egalPlace = 0;
  106.   String arg = "";
  107.   String val = "";
  108.   egalPlace = s.indexOf("=");
  109.   if (egalPlace > 0) {
  110.     arg = s.substring(0, egalPlace);
  111.     val = s.substring(egalPlace+1);
  112.     //Serial.println(arg);
  113.     //Serial.println(val);
  114.     if (arg == "Lat") {
  115.       gpsLat = val.toFloat();
  116.     }
  117.     if (arg == "Lng") {
  118.       gpsLng = val.toFloat();
  119.     }
  120.     if (arg == "Msg") {
  121.       message = val.toInt();
  122.     }
  123.     if (arg == "Disp") {
  124.       toDisp = val;
  125.       oledDraw();                       // Affiche ce qui est demandé par le maitre
  126.     }
  127.   }
  128. }
  129. /**************************************************************************
  130. **
  131. **  Fonctions d'affichage
  132. **
  133. **************************************************************************/
  134.  
  135. // Gestion des informations à afficher, en fonction de la demande du maitre
  136. void oledDraw() {
  137.   if (toDisp == "Coordinates") {
  138.     oledDrawPosition();
  139.     return;
  140.   }
  141.   if (toDisp == "Message") {
  142.     oledDrawStr(messages[message], 0, 30);
  143.     return;
  144.   }
  145.   oledDrawStr("Toto fait v" + String(char(233)) + "lo", 0, 20);
  146. }
  147. // Effacement complet de l'écran Oled
  148. void oledClear() {
  149.   Serial.println("Clear");
  150.   u8g.setColorIndex(0);
  151.   u8g.drawBox(0, 0, 127, 63);
  152.   u8g.setColorIndex(1);
  153. }
  154. // affichage d'une chaine à l'emplacement indiqué
  155. void oledDrawStr(String lStr, int x, int y) {
  156.   int i = 0;
  157.   Wire.end();
  158.   u8g.firstPage();  
  159.   do {
  160.     u8g.setFont(u8g_font_unifont);
  161.     u8g.drawStr( x, y, lStr.c_str());
  162.   } while( u8g.nextPage() );
  163.   delay(10);
  164.   Wire.begin(i2cRemoteDisplay);                
  165. }
  166. // Affichage sur l'écran Oled
  167. void oledDrawPosition(void) {
  168.   char str[12];
  169.   // On commence par ne plus écouter le bus i2c
  170.   Wire.end();
  171.   u8g.firstPage();  
  172.   do {
  173.     //u8g.setFont(u8g_font_unifont);
  174.     u8g.setFont(u8g_font_9x18);
  175.     //u8g.setFont(u8g_font_osb21);
  176.     u8g.drawStr( 0, 16, Hms(gpsLat, 'l').c_str());
  177.     u8g.drawStr( 0, 40, Hms(gpsLng, 'g').c_str());
  178.   } while( u8g.nextPage() );
  179.   // On reprend l'écoute du bus i2c
  180.   Wire.begin(i2cRemoteDisplay);                
  181. }
  182. // Formatage d'une coordonnée
  183. // l:  valeur décimale de la coordonnée
  184. // lg: caractère indiquant le type de coordonnée : L = latitude; G = longitude
  185. String Hms(double c, char lg) {
  186.   const int dec = 1;      // Affiche une décimale après les secondes
  187.   String disp;
  188.   int nbC = 2;            // Nombre de chiffres pour la latitude
  189.   disp.reserve(16);
  190.   bool neg = c > 0 ? 0 : 1;
  191.   char sign, coord;
  192.   if ((lg == 'G') || (lg == 'g')) {
  193.     sign = c > 0 ? 'E' : 'W';
  194.     disp = String(sign) + " ";
  195.     nbC = 3;
  196.   }
  197.   else {
  198.     sign = c > 0 ? 'N' : 'S';
  199.     disp = String(sign) + "  ";
  200.   }
  201.   c = abs(c);
  202.   int entPart = (int) c;
  203.   float rest = c - (float)entPart;
  204.   int min = (int) (60.0 * rest);
  205.   rest -= min / 60.0;
  206.   int sec = (int) (3600.0 * rest);
  207.   rest -= sec / 3600.0;
  208.   int decSec = 0.5 + (pow(10.0, dec) * ((3600.0 * rest) - (int) (3600.0 * rest)));
  209.   if (decSec >= 10) {
  210.     decSec = 0;
  211.     sec += 1;
  212.   }
  213.   if (sec >= 60) {
  214.     sec = 0;
  215.     min += 1;
  216.   }
  217.   if (min >= 60) {
  218.     min = 0;
  219.     entPart += 1;
  220.   }
  221.   disp += addLeadingZeroes(String(entPart), nbC);
  222.   disp += String(char(176));
  223.   disp += addLeadingZeroes(String(min), 2);
  224.   disp += "'";
  225.   disp += addLeadingZeroes(String(sec), 2);
  226.   disp += "\"";
  227.   disp += addLeadingZeroes(String(decSec), dec);
  228.   return(disp);  
  229. }
  230. // Ajout de "0" devant la chaine
  231. String addLeadingZeroes(String s, int lMax) {
  232.   return(addLeadingChar(s, '0', lMax));
  233. }
  234. // Ajout du caractère passé en paramètre devant la chaine
  235. String addLeadingChar(String s, char c, int lMax) {
  236.   String l;
  237.   l.reserve(lMax);
  238.   l = "";
  239.   for(int i = s.length(); i < lMax; i++) {
  240.     l += String(c);
  241.   }
  242.   return(l + s);
  243. }

Commentaires  forum ferme