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
- Computador
- 2 x placas Arduino UNO
- 3 x cabos Jumper M/M
Comunicação via monitor serial
As funções de que vamos precisar são:
- Serial.begin() permite configurar a taxa de transferência da porta serial. Se esta função estiver mal configurada, a comunicação não funcionará.
- Serial.print() e Serial.println() permitem enviar cadeias de caracteres (String) para a porta serial. A segunda permite terminar o envio com uma quebra de linha.
- Serial.write() permite enviar um byte de cada vez.
- Serial.available() permite verificar se a porta serial recebeu os dados
- Serial.read() permite receber os dados da porta serial
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
- Arduino 1 Rx -> Arduino 2 Tx
- Arduino 1 Tx -> Arduino 2 Rx
- Arduino 1 GND -> Arduino 2 GND
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
- Comunique com Arduino e os módulos Bluetooth HC-06 e HC-05.
- Crie um monitor serial com Python
Fontes
Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie