Étiquettes : , , , ,

La manière la plus simple de communiquer avec une carte Arduino est d’utiliser le moniteur série. C’est d’ailleurs l’outil le plus puissant pour tester et pour débugger votre programme. La communication série peut aussi servir à échanger des données avec d’autres appareils (Arduino, Raspberry ou autre).

Matériel

  • Ordinateur
  • Arduino UNO x2
  • Jumper cable M/M x3

Communication via le moniteur série

Les fonctions à connaitre sont:

  • Serial.begin() permet d’initialiser la vitesse de communication du port. Si cette fonction est mal paramétrée, la communication ne fonctionnera pas
  • Serial.print() et Serial.println() permettent d’envoyer des chaînes de caractères (String) sur le port série. La dernière permet de finir l’envoie par un saut de ligne
  • Serial.write() permet d’envoyer un byte à la fois.
  • Serial.available() permet de vérifier que le port série a reçu des données
  • Serial.read() permet de recevoir les données du le port série

Chargez le code suivant pour tester la communication entre l’ordinateur et la carte Arduino.

char cmd="";
char old_cmd;
boolean enable=false;


void setup(){
  // Initialize serial port
  Serial.begin(9600);
  Serial.println("ENTER Commands:");        
}

void loop(){
  old_cmd=cmd;
  if (Serial.available()){ // Verify that data are available
    cmd=Serial.read();  // Read data on serial port
  } 
  
  if(cmd!=old_cmd){
    if(cmd=='O'){
      Serial.println("Set to ON"); // Send data to serial port
      enable=true;
    }else if(cmd=='F'){
      Serial.println("Set to OFF"); // Send data to serial port
      enable=false;
    }
  }

   if(enable){
      Serial.println("System is running");
      delay(200);
    }
}

Si vous tapez O puis F dans la barre de commande du moniteur série, ceci devrait s’afficher:

Communication entre deux cartes Arduino

Il est aussi possible de communiquer entre différents systèmes en utilisant le port série. Un exemple intéressant est la communication entre deux cartes Arduino. Pour cela, il nous faut écrire deux programmes, un pour la carte « Maîtresse » (Master) et l’autre pour la carte « Esclave » (Slave).

Un port série est défini par deux pins et une vitesse de communication. La libraire SoftwareSerial.h permet de les définir simplement. Dans cet exemple nous choisissons les pins 2 et 3 (SoftwareSerial ArduinoSlave(2,3); )et une vitesse de communication de 9600bps (ArduinoSlave.begin(9600);).

ATTENTION : Nous utilisons ici, les broches 2 et 3 de l’Arduino UNO. Selon le microcontrôleur utilisé, les broches utilisables pour la communication série peuvent être différentes. Notamment pour les cartes Arduino Mega, Micro, Leonardo. Vérifiez la documentation.

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

Généralement, une carte va envoyer des informations et une autre va les recevoir. Dans notre exemple, pour vérifier que les deux cartes communiquent, la carte « Esclave » renvoie ce qu’elle a reçu de la carte « Maîtresse ».

Ce qui suit reste valable pour toute communication série! Il est possible de transformer cette communication filaire en une communication sans fil avec les modules Bluetooth HC-06 et HC-05.

Code de la carte « Maîtresse »

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(2,3);
char cmd="";
char old_cmd;
char answer="";
char old_answer;

void setup(){
  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoSlave.begin(9600);            
}

void loop(){
  old_cmd=cmd;
  old_answer=answer;
  //Read command from monitor
  if (Serial.available()){
    cmd=Serial.read();
  } 
  //Read answer from slave
  if (ArduinoSlave.available()){
    answer=ArduinoSlave.read();
  } 
  //Send data to slave
  if(cmd!=old_cmd){
    Serial.print("Master sent : ");
    Serial.println(cmd);
    ArduinoSlave.write(cmd);
  }
  //Send answer to monitor
  if(answer!=old_answer){
    Serial.print("Slave received : ");
    Serial.println(answer);
  }
}

Code de la carte « Esclave »

#include <SoftwareSerial.h>

SoftwareSerial ArduinoMaster(2,3);
char cmd="";
char old_cmd;

void setup(){
  ArduinoMaster.begin(9600);    
}

void loop(){
  old_cmd=cmd;
  // Read data from master
  if (ArduinoMaster.available()){
    cmd=ArduinoMaster.read();
  } 
  // Send answer to master
  if(cmd!=old_cmd){
    ArduinoMaster.write(cmd);
  }
}

Dans le moniteur série de la carte « Maîtresse », tapez la commande que vous voulez vous devriez voir la carte « Esclave » vous répondre comme dans l’image suivante.

Ce code permet d’envoyer un seul byte à la fois. Les caractères sont codé en ASCII. Vérifiez la table ASCII pour vérifier comment recevoir vos données. Certains caractères ne peuvent être envoyé comme le signe degré »° » ou les accents « é », etc.

Jouez avec les données

Dans certains cas, vous aurez besoin d’utiliser d’autres types de données qu’un entier ou un caractère et envoyer plusieurs informations. Une solution qui peut aider dans la plupart des cas est de convertir les données en chaîne de caractères et de les envoyer à l’aide de la fonction print().

Code de la carte Maîtresse

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(2,3);
String msg;

void setup(){

  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoSlave.begin(9600);
                
}

void loop(){
  //Read command from monitor
  readSerialPort();
  
  //Send data to slave
  if(msg!=""){
    Serial.print("Master sent : ");
    Serial.println(msg);
    ArduinoSlave.print(msg);
    msg="";
  }

}

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c; //add to String
   }
 }
 Serial.flush(); //clean buffer
}

Code de la carte Esclave

#include <SoftwareSerial.h>

SoftwareSerial ArduinoMaster(2,3);
String msg;

void setup(){
  Serial.begin(9600);
  ArduinoMaster.begin(9600);    
}

void loop(){
  readMasterPort();
  
  // Send answer to master
  if(msg!=""){
    ArduinoMaster.print(msg);
    Serial.print("Master sent : " );
    Serial.println(msg);
    msg=""; 
  }
}

void readMasterPort(){
 while (ArduinoMaster.available()) {
   delay(10); 
   if (ArduinoMaster.available() >0) {
     char c = ArduinoMaster.read();  //gets one byte from serial buffer
     msg += c; //makes the string readString
     Serial.println(b);
   }
 }
 ArduinoMaster.flush();
}

Ce code vous permet d’envoyer tout type ou chaine de caractères.

Une fois que la communication et en place et que les cartes peuvent échanger des données l’important est de savoir quoi en faire.

Envoyer et recevoir la valeur d’un capteur

Un bon exemple d’application est de modifier la luminosité d’une LED branchée sur la carte Esclave à l’aide d’un capteur branché sur la carte Maître. Comme nous récupérons les données sous la forme d’un String il nous faut le convertir en entier. La fonction pour effectuer conversion est atoi(). La valeur de PWM de la led est comprise entre 0 et 255, il faut donc convertir la valeur du capteur, comprise entre 0 et 1023 avec la fonction map().

Code de la carte Maitre pour lire et envoyer la valeur d’un capteur

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(2,3);
String answer;
String msg;
int intVal=0,oldIntVal=0;

void setup(){

  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoSlave.begin(9600);
                
}

void loop(){
  //Read sensor
  intVal=analogRead(A0);
  //Read answer from slave
  readSlavePort();
  
  //Send data to slave
  if(oldIntVal!=intVal){
    Serial.print("Master sent : ");
    Serial.println(intVal);
    ArduinoSlave.print(intVal);
    oldIntVal=intVal; 
  }
  //Send answer to monitor
  if(answer!=""){
    Serial.print("Slave LED PWM value : ");
    Serial.println(answer);
    answer="";
  }
  delay(1000);
}

void readSlavePort(){
  while (ArduinoSlave.available()) {
   delay(10);  
   if (ArduinoSlave.available() >0) {
     char c = ArduinoSlave.read();  //gets one byte from serial buffer
     answer += c; //makes the string readString
   }
 }
}

Code de la carte Esclave pour recevoir et utiliser la valeur d’un capteur

#include <SoftwareSerial.h>

SoftwareSerial ArduinoMaster(2,3);

#define ledPin 11

String msg="";
int ledVal=0;
int intVal=0,oldIntVal=0;

void setup(){
  Serial.begin(9600);
  ArduinoMaster.begin(9600);
  pinMode(ledPin,OUTPUT);    
}

void loop(){
  readMasterPort();
  convertMsgToCmd();
  // Send answer to master
  if(intVal!=oldIntVal){
    Serial.print("Master sent : " );
    Serial.println(intVal);

    ledVal=map(intVal,0,1023,0,255);
    Serial.print("led value : ");
    Serial.println(ledVal);
    ArduinoMaster.print(ledVal);
    analogWrite(ledPin,ledVal);
    oldIntVal=intVal; 
  }
}

void readMasterPort(){
 while (ArduinoMaster.available()) {
   delay(10); 
   if (ArduinoMaster.available() >0) {
     char c = ArduinoMaster.read();  //gets one byte from serial buffer
     
     msg += c; //makes the string readString
   }
 }
 ArduinoMaster.flush();
}

void convertMsgToCmd(){
   if (msg.length() >0) {
    Serial.print("message length : ");
    Serial.println(msg.length());
    
     char carray1[6]; //magic needed to convert string to a number
     msg.toCharArray(carray1, sizeof(carray1));
     intVal = atoi(carray1);
     
     msg="";
 }
}

Envoyer et recevoir deux valeurs de capteurs

Pour recevoir des valeurs différentes, l’idée est de définir un séparateur. Dans ce cas, nous utilisons le caractère « x ». Il suffit ensuite de repérer le caractère dans la chaine à l’aide de la fonction indexOf().

Code de la carte Maitre pour envoyer deux valeurs distinctes

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(2,3);
String answer;
String msg;
int intVal1=0,oldIntVal1=0;
int intVal2=0,oldIntVal2=0;


void setup(){
  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoSlave.begin(9600);
                
}

void loop(){
  //Read sensors
  intVal1=analogRead(A0);
  intVal2=analogRead(A1);
  
  //Send data to slave
  if(oldIntVal1!=intVal1 || oldIntVal2!=intVal2 ){
    Serial.print("Master sent : ");
    Serial.print(intVal1);
    Serial.print("x");
    Serial.println(intVal2);
    
    ArduinoSlave.print(intVal1);
    ArduinoSlave.print("x");
    ArduinoSlave.print(intVal2);
    oldIntVal1=intVal1;
    oldIntVal2=intVal2;
  }
  delay(1000);
  //Read answer from slave
  readSlavePort();
  
  //Send answer to monitor
  if(answer!=""){
    Serial.println("Slave received : ");
    Serial.println(answer);
    answer="";
  }
  
}

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c; //makes the string readString
   }
 }
 Serial.flush();
}

void readSlavePort(){
  while (ArduinoSlave.available()) {
   delay(10);  
   if (ArduinoSlave.available() >0) {
     char c = ArduinoSlave.read();  //gets one byte from serial buffer
     answer += c; //makes the string readString
   }
 }
}

Code de la carte Esclave pour recevoir deux valeurs distinctes

#include <SoftwareSerial.h>

SoftwareSerial ArduinoMaster(2,3);

String msg="",m1="",m2="";
int num1=-1,num2=-1;
int sep;

void setup(){
  Serial.begin(9600);
  ArduinoMaster.begin(9600);
}

void loop(){
  readMasterPort();
  convertMsgToMultiCmd();
  // Send answer to master
  if(num1!=-1 && num2!=-1){
    Serial.print("Sensor 1 : " );
    Serial.println(num1);

    Serial.print("Sensor 2 : " );
    Serial.println(num2);

    ArduinoMaster.print("Sensor 1 : " );
    ArduinoMaster.println(num1);

    ArduinoMaster.print("Sensor 2 : " );
    ArduinoMaster.println(num2);
    num1=-1;
    num2=-1;
  }
}

void readMasterPort(){
 while (ArduinoMaster.available()) {
   delay(10); 
   if (ArduinoMaster.available() >0) {
     char c = ArduinoMaster.read();  //gets one byte from serial buffer
     
     msg += c; //makes the string readString
   }
 }
 ArduinoMaster.flush();
}


void convertMsgToMultiCmd(){
   if (msg.length() >0) {
    Serial.print("message length : ");
    Serial.println(msg.length());
     sep = msg.indexOf('x');
     //Serial.println(sep);
     // expect a string like 0x0021 containing the two servo positions      
     m1 = msg.substring(0, sep); //get servo id
     m2 = msg.substring(sep+1, msg.length()); //get servo pos 
    
     char carray1[6]; //magic needed to convert string to a number
     m1.toCharArray(carray1, sizeof(carray1));
     num1 = atoi(carray1);
     
     char carray2[6];
     m2.toCharArray(carray2, sizeof(carray2));
     num2 = atoi(carray2);
      
     msg="";
 }
}

Envoyer et recevoir plusieurs données

Il est possible que dans votre application vous ayez besoin d’une multitude de données à échanger entre deux cartes. Dans ce cas il faut modifier un peu la fonction de conversion. Dans notre exemple, nous supposons que nous voulons piloter l’état d’une led, la vitesse et la direction d’un moteur.

Code de la carte Maitre pour envoyer plusieurs données

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(2,3);
String answer;
String msg;
int intVal1=0,oldIntVal1=0;
int intVal2=0,oldIntVal2=0;


void setup(){

  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoSlave.begin(9600);
                
}

void loop(){
  //Read command from monitor
  readSerialPort();
 
  //Read answer from slave
  readSlavePort();
  
  //Send data to slave
  if(msg!=""){
    Serial.print("Master sent : ");
    Serial.println(msg);
    ArduinoSlave.print(msg);
    msg="";
  }
  
  //Send answer to monitor
  if(answer!=""){
    Serial.print("Slave received : ");
    Serial.println(answer);
    answer="";
  }
  delay(1000);
}

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c; //makes the string readString
   }
 }
 Serial.flush();
}

void readSlavePort(){
  while (ArduinoSlave.available()) {
   delay(10);  
   if (ArduinoSlave.available() >0) {
     char c = ArduinoSlave.read();  //gets one byte from serial buffer
     answer += c; //makes the string readString
   }
 }
}

Code de la carte Esclave pour recevoir plusieurs données

#include <SoftwareSerial.h>
SoftwareSerial ArduinoMaster(2,3);
String msg="";
int ledVal=0;
int sep;
String data[3];
unsigned int data_count=0;
void setup(){
 Serial.begin(9600);
 ArduinoMaster.begin(9600);
}
void loop(){
 readMasterPort();
 convertMsgToMultiCmd();
 //Use data
 if(data_count==3){
   for(int i=0;i<(data_count+1);i++){
     switch(i){
       case 0: //led status
         if(data[0]=="ON"){
           Serial.println("Switch led ON");
         }else if(data[0]=="OFF"){
           Serial.println("Switch led OFF");
         }else{
           Serial.println("Led wrong command");
         }
       break;
       case 1: //motor PWM
          Serial.print("Set motor power to : ");
          Serial.println(stringToInt(data[1]));
       break;
       case 2: //motor direction
          if(data[2]=="forward"){
           Serial.println("Motor direction forward");
         }else if(data[2]=="backward"){
           Serial.println("Motor direction backward");
         }else{
           Serial.println("Motor wrong command");
         }
       break;
       
     }
   }
   data_count=0;
 }
 
}
void readMasterPort(){
while (ArduinoMaster.available()) {
  delay(10); 
  if (ArduinoMaster.available() >0) {
    char c = ArduinoMaster.read();  //gets one byte from serial buffer
    
    msg += c; //makes the string readString
  }
}
ArduinoMaster.flush();
}
void convertMsgToMultiCmd(){
 
  if (msg.length() >0) {
   data_count=0;
   Serial.print("Master sent : ");
   Serial.println(msg);//Serial.println(msg.length());
   do{
     sep = msg.indexOf('x');
      // expect a string like 0x0021 containing the two servo positions      
      String m1 = msg.substring(0, sep); //get servo id
      msg = msg.substring(sep+1, msg.length()); //get servo pos 
     data[data_count]=m1;
     data_count++;
   } while(sep!=-1);
   
   Serial.println("data received : "); 
   for(int i=0;i<(data_count+1);i++) Serial.println(data[i]); 
    msg="";
}
}
int stringToInt(String s){
    char carray1[6]; //magic needed to convert string to a number
    s.toCharArray(carray1, sizeof(carray1));
    return atoi(carray1);
}

Une fois que les données sont correctement échangées, il suffit de convertir en entier les grandeurs si besoin et effectuer les actions voulues en fonction des données reçues.

Avec ses connaissances de base, vous pouvez communiquer avec d’autres modules comme des contrôleurs série de servomoteurs, d’autres microcontrôleurs ou encore des modules Bluetooth.

Si vous avez un problème pour établir une communication série avec votre projet ou si vous souhaitez d’autres exemples dans ce tutoriel n’hésitez pas à laisser un commentaire ou à m’envoyer un message.

Applications

  • Communiquez avec Arduino et les modules Bluetooth HC-06 et HC-05.

Sources

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie