Écoute-moi, j’aimerais te parler

11 novembre 2022

Communication entre deux Arduino


1 – Mise en situation

Dans les exemples suivants, nous verrons comment transmettre des informations d’un Arduino vers un autre en utilisant le protocole série (UART / RS-232 / COM PORT).

Le protocole série utilise deux broches pour l’échange des données; une broche pour la réception (RX) et une broche pour la transmission (TX)

Dans le cas d’un Arduino Uno ces broches sont D0 (RX0) et D1 (TX0).

L’Arduino Uno ne possède qu’un seul port série.

De plus, si nous utilisons la console série pour y afficher des messages alors le port série du UNO ne sera pas disponible pour le transfert d’information vers d’autres appareils.

Ce n’est pas le cas pour tous les Arduino.

Par exemple, l’Arduino Mega propose 4 ports séries (UART0-UART3).


1.1 – Simulation d’un port série via la librairie SotfwareSerial

Il est possible d’utiliser des broches digitales standards pour reproduire le comportement et les fonctions d’un port UART en utilisant la librairie SotfwareSerial. Cette librairie est disponible par défaut dans l’environnement de Arduino IDE, c-a-d, qu’il n’est pas nécessaire de l’installer.


1.2 – Connexion de deux appareils pour la communication via UART

Si nous désirons relier deux appareils ensemble qui utiliseront leur port UART pour la communication, il faut relier la broche RX du premier sur la broche TX du deuxième. Puis la broche TX du premier sur la broche RX du deuxième.

Avec l’utilisation du Grove Hat, il sera possible d’inverser les broches RX/TX dans les paramètres de configurations du port UART logiciel.

Par exemple, deux Arduino UNO reliés par un câble sur le port D4 du Hat Grove pourront être configurés de la façon suivante:

SoftwareSerial portSerieTransmetteur(4, 5); // RX, TX
SoftwareSerial portSerieRecepteur(5, 4);    // RX, TX

2 – Exemple de communication UART entre deux Arduino

Pour le prochain laboratoire, il faut relier un Arduino Mega et un Arduino UNO en plaçant un câble entre les deux connecteurs D6.

2.1 – Code source du transmetteur

/*
 Code du transmetteur - sur le MEGA
 Note: Relier deux Arduino via le port D6
*/

#include <SoftwareSerial.h>
#include "rgb_lcd.h"
#include "Streaming.h"

#define UART_RX             6
#define UART_TX             7
#define UART_VITESSE        9600
#define MAX_NB_ALEATOIRE    99
#define UNE_SECONDE         1000
#define LCD_DEUXIEME_LIGNE  1
#define LCD_NB_LIGNE        2
#define LCD_NB_COL          16

rgb_lcd lcd;
SoftwareSerial portSerieTransmetteur(UART_RX, UART_TX); // RX, TX

void setup() {
  Serial.begin(UART_VITESSE); // port natif utilisé pour le débogage
  Serial.println("Démarrage du transmetteur...");
  lcd.begin(LCD_NB_COL,LCD_NB_LIGNE);
  lcd.print("Transmetteur");
  // Initialiser le port serie 'SoftwareSerial'
  portSerieTransmetteur.begin(UART_VITESSE);
} // setup()

void loop() {
   // Transmettre un code à chaque seconde  
   // générer un nombre entre 0 et MAX_NB_ALEATOIRE - 1
   byte unCode = random(MAX_NB_ALEATOIRE); 
   portSerieTransmetteur.write(unCode);
   Serial << "Envoi du code suivant: " << unCode << endl;
   lcd.setCursor(0, LCD_DEUXIEME_LIGNE);
   lcd << "Envoi de: " << unCode;
   delay(UNE_SECONDE);
} // loop()

2.2 – Code source du récepteur

/*
 Code du récepteur - sur le UNO
 Note: Relier deux Arduino via le port D6
*/

#include <SoftwareSerial.h>
#include "rgb_lcd.h"
#include "Streaming.h"

#define UART_RX             6
#define UART_TX             7
#define UART_VITESSE        9600
#define UNE_SECONDE         1000
#define LCD_DEUXIEME_LIGNE  1
#define LCD_NB_LIGNE        2
#define LCD_NB_COL          16

rgb_lcd lcd;

// Le RX du UNO doit être connecté au TX du MEGA et TX -> RX
// Il faut donc inverser les signaux:
SoftwareSerial portSerieRecepteur(UART_TX, UART_RX); // RX, TX

void setup() {
  Serial.begin(UART_VITESSE);
  Serial.println("Démarrage du récepteur...");
  lcd.begin(LCD_NB_COL,LCD_NB_LIGNE);
  lcd.print("Recepteur");
  // Initialiser le port serie 'SoftwareSerial'
  portSerieRecepteur.begin(UART_VITESSE);
}

void loop() {  
  byte unCode;
  if (portSerieRecepteur.available()) {
    unCode = portSerieRecepteur.read();
    // afficher la valeur du byte reçu
    Serial << "Code recu = " << unCode << endl;;
    lcd.setCursor(0, LCD_DEUXIEME_LIGNE);
    lcd << "Code recu: " << unCode;
  } // si byte disponible sur le UART
}

NOTE IMPORTANTE:

L’Arduino MEGA ne permet pas d’implémenter le UART, via la librairie SoftwareSerial, en mode réception sur toutes ses broches. Il faut se limiter aux broches suivantes:

Par contre, en mode transmission, il n’y a pas cette contrainte.

Il n’y pas non plus de contraintes si nous utilisons les ports natifs UART du MEGA.

De plus, sur certains Arduino, la vitesse MAXIMUM de transmission est limitée à 57600bps.


Documentation de la classe :: SoftwareSerial


2.5 – Laboratoire

Il faut modifier les vitesses de transmission/réception de l’exemple précédent à 115 200 bauds et tester le système.

Question: Est-il toujours fonctionnel?


2.6 – Laboratoire

Il faut relier les deux Arduino par leur port natif UART; 1) Mega sur Serial3 et 2) UNO sur Serial.

Note: Il ne faut plus afficher dans la console série du projet UNO, seulement sur l’écran LCD.

Question: Est-ce que le système est toujours fonctionnel? Si non, il faut expliquer pourquoi.

// Exemple d'initialisation du UART no 3 sur le MEGA:
Serial3.begin(9600);


3 – Transmission d’une chaine de caractères

3.1 – Code source du transmetteur

/*
 Code du transmetteur
 Version chaine de caractères
 Note: Relier deux Arduino via le port D6
*/

#include <SoftwareSerial.h>
#include "rgb_lcd.h"
#include "Streaming.h"

rgb_lcd lcd;
const String messages[] = {
  "Haddock", 
  "Tournesol", 
  "Dupond et Dupont", 
  "Tintin",
  "Milou",
  "Bachi-bouzouk",
  "Boit-sans-soif",
  "Brontosaure",
  "Calembredaine",
  "Cataplasme",
  "Clysopompe",
  "Cornemuse",
  "Cornichon",
  "Cyclotron"
};

// Le RX du premier doit être connecté au TX du deuxième et TX -> RX
#define UART_RX             6
#define UART_TX             7
#define UART_VITESSE        9600
#define MAX_NB_ALEATOIRE    14
#define UNE_SECONDE         1000
#define LCD_DEUXIEME_LIGNE  1
#define LCD_NB_LIGNE        2
#define LCD_NB_COL          16

SoftwareSerial portSerieTransmetteur(UART_RX, UART_TX); // RX, TX

void setup() {
  Serial.begin(UART_VITESSE); // port natif utilisé pour le débogage
  Serial.println("Démarrage du transmetteur...");
  lcd.begin(LCD_NB_COL,LCD_NB_LIGNE);
  lcd.print("Transmetteur");
  // Initialiser le port serie 'SoftwareSerial'
  portSerieTransmetteur.begin(UART_VITESSE);
} // setup()

void loop() {  
   // Envoyer un message aléatoire à toutes les secondes:
   byte unCode = random(MAX_NB_ALEATOIRE);
   portSerieTransmetteur.print(messages[unCode]);
   Serial << "Envoi du message suivant: " << messages[unCode] << endl;
   lcd.setCursor(0, LCD_DEUXIEME_LIGNE);
   lcd << "Envoi du msg: " << unCode << " ";
   delay(UNE_SECONDE * 2);
}

3.2 – Code source du récepteur

/*
 Code du récepteur
 Version chaine de caractères
 Note: Relier deux Arduino via le port D6
*/

#include <SoftwareSerial.h>
#include "rgb_lcd.h"
#include "Streaming.h"

rgb_lcd lcd;

// Le RX du UNO doit être connecté au TX du MEGA et TX -> RX
// Il faut donc inverser les signaux:
#define UART_RX             6
#define UART_TX             7
#define UART_VITESSE        9600
#define UNE_SECONDE         1000
#define LCD_DEUXIEME_LIGNE  1
#define LCD_NB_LIGNE        2
#define LCD_NB_COL          16

SoftwareSerial portSerieRecepteur(UART_TX, UART_RX); // RX, TX

void setup() {
  Serial.begin(UART_VITESSE);
  Serial.println("Démarrage du récepteur...");
  lcd.begin(LCD_NB_COL,LCD_NB_LIGNE);
  lcd.print("Recepteur");
  // Initialiser le port serie 'SoftwareSerial'
  portSerieRecepteur.begin(UART_VITESSE);
  delay(UNE_SECONDE);
}

void loop() {  
  String unMessage;
  static unsigned long compteurMessage = 0;
  if (portSerieRecepteur.available()) {
    unMessage = portSerieRecepteur.readString();
    // afficher la valeur du byte reçu
    Serial << "Message recu = " << unMessage << endl;
    lcd.setCursor(0,0);
    lcd << "MSG RECUS: " << ++compteurMessage;
    effacerLigne2LCD();
    lcd << unMessage;
  } // si données disponibles sur le UART
}

void effacerLigne2LCD(){
  lcd.setCursor(0, LCD_DEUXIEME_LIGNE);
  for (int i = 0 ; i < LCD_NB_COL; i++){
    lcd << " ";
  }
  lcd.setCursor(0, LCD_DEUXIEME_LIGNE); 
}

4 – Exemple d’un système d’alarme à 2 Arduino

4.1 – Fichier d’entête

/*
    Fichier:  code.alarme.h
    Auteur:   Alain Boudreault
    Date:     2021.11.21
    ---------------------------------------------------------
    Description:  Définir un type enum à utiliser pour les
                  différents états du système d'alarme.

                  Note: Inclure ce fichier dans le dossier de
                        votre projet.

                        Puis, programmer la syntaxe suivante 
                        pour utiliser ce type:

                        codesAlarme code;
                        ...
                        code = evenement_systeme_enligne;
    ---------------------------------------------------------
    M-A-J: 2022.11.11 A.B. - Ajout de 'evenement_nb_codes'
    ---------------------------------------------------------
*/

// S'assurer que ce fichier ne sera inclus qu'une seule fois lors de la compilation du projet
#ifndef LES_CODES_ERREUR_H
#define LES_CODES_ERREUR_H
enum codesAlarme {
  evenement_temperature_depassee,
  evenement_dectection_mouvement,
  evenement_code_invalide,
  evenement_code_valide,
  evenement_alarme_active,
  evenement_alarme_inactive,
  evenement_systeme_enligne,
  evenement_nb_codes
};

#endif // LES_CODES_ERREUR_H_ _

4.2 – Code source du transmetteur

/*
    Simulation d'un système d'alarme UNO, qui envoie des événements
    à une centrale de controle (MEGA) via le protocole UART.

    Le UNO est connecté par son port D8 au port UART2 du MEGA.

    Étant donné que le UNO n'a qu'un seul port UART et qu'il est utilisé
    pour déboger l'application, la librairie 'SoftwareSerial' est utilisée
    pour simuler un port UART à partir d'une GPIO.

*/

#include <SoftwareSerial.h>
#include "code.alarme.h"
#include "rgb_lcd.h"
#include "Streaming.h"


rgb_lcd lcd;

// Le RX du UNO doit être connecté au TX du MEGA et TX -> RX
// Il faut donc inverser les signaux:
SoftwareSerial mySerial(5, 4); // RX, TX

codesAlarme unCodeAlarme = evenement_systeme_enligne;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // S'assurer que le port serie est pret.  Ceci n'est pas requis pour un SoftwareSerial.
  }
  Serial.println("Demarrage du systeme d'alarme...");
  lcd.begin(16,2);
  lcd.print("Master");
  // Initialiser le port serie 'SoftwareSerial'
  mySerial.begin(9600);

  // Envoyer l'état du système d'alarme à la centrale (Mega)
  mySerial.write(unCodeAlarme); // Attention, pour envoyer un seul octet (Byte), il faut utiliser la méthode write et non pas print.
  delay(1000);
}

void loop() {  
  unCodeAlarme = evenement_dectection_mouvement;
  // Envoyer un code de détection du mouvement à toutes les secondes:
    byte i;
    i = random(evenement_nb_codes); 
   mySerial.write(i);
   Serial << "Envoi du code suivant: " << i << endl;
   delay(1000);

}

4.3 – Code source du récepteur

/*
    Simulation d'un système d'alarme UNO, qui envoie des événements
    à une centrale de controle (MEGA) via le protocole UART.

    Le UNO est connecté par son port D8 au port UART2 du MEGA.

    Étant donné que le UNO n'a qu'un seul port UART et qu'il est utilisé
    pour déboger l'application, la librairie 'SoftwareSerial' est utilisée
    pour simuler un port UART à partir d'une GPIO.

*/

#include <SoftwareSerial.h>
#include "code.alarme.h"
#include "rgb_lcd.h"
#include "Streaming.h"

rgb_lcd lcd;

// Le RX du UNO doit être connecté au TX du MEGA et TX -> RX
// Il faut donc inverser les signaux:
SoftwareSerial mySerial(5, 6); // RX, TX

codesAlarme unCodeAlarme = evenement_systeme_enligne;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // S'assurer que le port serie est pret.  Ceci n'est pas requis pour un SoftwareSerial.
  }
  Serial.println("Demarrage du systeme d'alarme...");
  lcd.begin(16,2);
  lcd.print("Slave ready");
  // Initialiser le port serie 'SoftwareSerial'
  mySerial.begin(9600);

  // Envoyer l'état du système d'alarme à la centrale (Mega)
  //mySerial.write(unCodeAlarme); // Attention, pour envoyer un seul octet (Byte), il faut utiliser la méthode write et non pas print.
  delay(1000);
}

void loop() {  
   codesAlarme code;
  if (mySerial.available()) {
    code = mySerial.read();
    // afficher la valeur du byte reçu
    Serial.print("\nCode = "); Serial.print(code, DEC); Serial.println("");
    lcd.setCursor(0, 1);
    lcd << "Code recu: " << code;
    // Tester les événements du système d'alarme
    if (code == evenement_systeme_enligne) Serial.print("Systeme client en ligne !");
    if (code == evenement_dectection_mouvement) Serial.print("DANGER: Il y a intrusion ....");
    
  }
}

5 – Laboratoire

Mettre en place un système de deux Arduino, reliés ensemble VIA le protocole UART qui s’échangent des données.

Défi supplémentaire


6 – Laboratoire

Il faut relier, correctement, deux Arduino via le protocole UART.

Chaque Arduino possède un écran LCD.

Le premier Arduino envoi vers le deuxième, un nombre aléatoire compris entre 0 et NB_CITATIONS. Le nombre aléatoire est affiché sur la ligne 1 du LCD du premier Arduino en respectant la forme suivante:

1: Citation no: nn
2:

Le deuxième Arduino reçoit le nombre aléatoire et l’utilise pour obtenir une citation dans le tableau des citations. Le nombre aléatoire est affiché sur la ligne no 2 du deuxième Arduino.

1: Arduino no 2
2: Envoi de: nn

La citation obtenue est envoyée vers le premier Arduino et affichée sur la ligne no 2 de ce dernier.

1: Citation no: nn
2: Texte de la citation

Voici le tableau des citations:

const String citations[] = {
  "Bachi-bouzouk",
  "Boit-sans-soif",
  "Brontosaure",
  "Calembredaine",
  "Catachrese",
  "Coupe-jarret",
  "Doryphore",
  "Dynamiteur",
  "Empoisonneur",
  "Hurluberlu",
  "Cataplasme",
  "Clysopompe",
  "Cornemuse",
  "Cornichon",
  "Cyclotron"
};


NOTE IMPORTANTE

Le contenu de ce document pourrait être évalué dans un Quiz