Site icon AranaCorp

A comunicação serial com Arduino

A forma mais simples de comunicar com uma placa Arduino é através de um monitor serial, que é, aliás, a ferramenta mais potente para testar o seu programa e eliminar bugs. A comunicação serial também pode servir para trocar dados com outros dispositivos (Arduino, Raspberry ou outros).

Equipamento

Comunicação via monitor serial

As funções de que vamos precisar são:

Carregue o código a seguir para testar a comunicação entre o computador e a placa 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);
    }
}

Ao se digitar O e depois F na barra de comando do monitor serial, deverá aparecer isto:

Comunicação entre duas placas Arduino

É possível utilizar a porta de série (Rx0,Tx1) utilizada pela porta USB para comunicar entre duas placas Arduino. Para o fazer, basta ligar os pinos da seguinte forma

CUIDADO: Se utilizar placas diferentes, os níveis de tensão podem não ser compatíveis. Neste caso, é necessário utilizar um conversor de voltagem. Verificar a documentação de cada quadro.

Como dissemos, os pinos 0 e 1 são utilizados pela porta USB. Para evitar conflitos com o monitor de série, vamos analisar a biblioteca SoftwareSerial que utiliza outros pinos do microcontrolador. Os exemplos seguintes também são possíveis com a porta de série do hardware. Só tem de substituir “ArduinoSlave” e “ArduinoMaster” por “Serial” nos seguintes códigos.

Em geral, as técnicas de intercâmbio de dados discutidas abaixo são aplicáveis a todas as comunicações em série.

Comunicação entre duas placas Arduino com SoftwareSerial

Também é possível comunicar entre diferentes sistemas utilizando uma porta serial. Um exemplo interessante é a comunicação entre duas placas Arduino. Para isso, é preciso escrever dois programas: um para a placa mestre (Master) e outro para a placa escrava (Slave).

Uma porta serial é definida por dois pinos e uma taxa de transferência. A biblioteca SoftwareSerial.h permite defini-la com facilidade. No nosso exemplo, escolhemos os pinos 2 e 3 (SoftwareSerial ArduinoSlave(2,3); ) e uma taxa de transferência de 9600bps (ArduinoSlave.begin(9600);).

ATENÇÃO: Utilizamos aqui os pinos 2 e 3 do Arduino UNO. Os pinos utilizados para a comunicação serial podem ser diferentes em função dos microcontrolador utilizado, em especial para as placas Arduino Mega, Micro, Leonardo. Verifique a documentação.

Para que as duas placas comuniquem entre si, é preciso ligá-las corretamente e não esquecer de ligar as terras (GND) como indicado no esquema a seguir.

Em geral, uma placa envia as informações e a outra as recebe. No nosso exemplo, verificamos que as duas placas se comunicam quando a placa escrava reenvia o que recebeu da placa mestre.

O que vem a seguir vale para qualquer comunicação serial! É possível transformar esta comunicação com fio numa comunicação sem fio com os módulos Bluetooth HC-06 e HC-05.

Código da placa Mestre

#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);
  }
}

Código da placa Escrava

#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);
  }
}

Digite o comando que desejar no monitor serial da placa mestre. A placa escrava deverá responder como na imagem abaixo.

Este código permite enviar um único byte por vez. Os caracteres são codificados em ASCII. Consulte a tabela ASCII para verificar como receber os seus dados. Alguns caracteres não podem ser enviados, como o sinal de grau “°” ou os acentos “é”, etc.

Sirva-se dos dados

Em alguns casos, será necessário enviar diversas informações e utilizar outros tipos de dados que não um inteiro ou um caractere. Uma solução que pode ajudar na maior parte dos casos é converter os dados em cadeias de caracteres e enviá-los com o auxílio da função print().

Código da placa Mestre

#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
}

Código da placa Escrava

#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(msg);
   }
 }
 ArduinoMaster.flush();
}

O código a seguir permite enviar todo tipo de cadeia de caracteres.

Uma vez que a comunicação estiver estabelecida e as placas puderam trocar dados, é importante saber o que fazer em seguida.

Enviar e receber o valor de um sensor

Um bom exemplo de aplicação é variar o brilho de um LED conectado à placa escrava com o auxílio de um sensor conectado à placa mestre. Como captamos os dados na forma de String, é preciso convertê-los em inteiro. A função para realizar a conversão é atoi(). O valor de PWM do LED está compreendido entre 0 e 255, então é preciso converter o valor do sensor, compreendido entre 0 e 1023, com a função map().

Código da placa Mestre para ler e enviar o valor de um sensor

#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
   }
 }
}

Código da placa Escrava para receber e utilizar o valor de um sensor

#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="";
 }
}

Enviar e receber dois valores de sensores

Para receber diferentes valores, a ideia é definir um separador. Neste caso, iremos utilizar o caractere “x”. Em seguida, basta localizar o caractere na cadeia por meio da funcão indexOf().

Código da placa Mestre para enviar dois valores diferentes

#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
   }
 }
}

Código da placa Escrava para receber dois valores diferentes

#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="";
 }
}

Enviar e receber diversos dados

Pode ser que a sua aplicação exija trocar diversos dados entre duas placas. Nesse caso, será preciso alterar um pouco a função de conversão. No nosso exemplo, supomos que o objectivo seja controlar o estado de um LED e a velocidade e a direção de um motor.

Código da placa Mestre para enviar diversos dados

#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
   }
 }
}

Código da carta Escrava para receber diversos dados

#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);
}

Quando os dados se comunicarem corretamente, basta converter as grandezas em inteiro, se necessário, e realizar as ações desejadas em função dos dados recebidos.

Com essas informações básicas, poderá comunicar com outros módulos como controladores seriais de servomotores, outros microcontroladores ou ainda módulos Bluetooth.

Se tiver dificuldade em estabelecer uma comunicação serial no seu projecto ou se gostaria de ver outros exemplos neste tutorial, deixe-nos um comentário ou contacte-nos por mensagem.

Aplicações

Fontes

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

Exit mobile version