Curso Arduino – #3 – UART e Variáveis
O Arduino permite a utilização de inúmeras interfaces de comunicação. Graças a estas, os projetos desenvolvidos podem comunicar com outros sistemas, sensores ou com o computador.
Neste artigo, vamos abordar o UART, que é uma interface série simples e muito utilizada. Especialmente quando se trata de uma comunicação com o computador.
Como é que o UART funciona?
O seu princípio de funcionamento baseia-se no envio série de uma sequência de bits, que são transformados em informação. Um conjunto de dados (data frame) é transmitido da seguinte forma:
A transmissão começa com o start bit, marcado como BS na figura. É sempre um bit que é zero lógico. Depois, dependendo da configuração, existem 7, 8 ou 9 data bits (marcados de B0-B7) que são as informações a ser enviadas. O stop bit (indicado como BK) é um bit um lógico – finaliza a transmissão.
Ao usar o UART no Arduino, devemos ter em consideração dois pinos:
- Tx – envio de dados (pino 1);
- Rx – receção de dados (pino 0).
Para que a transmissão funcione corretamente, deve ser definida a mesma velocidade de transferência de dados em ambos os sistemas – conhecida como baud-rate ou taxa de transmissão. Esta especifica o número de bits transmitidos por segundo. Os valores mais utilizados são: 9.600 e 112.500.
O computador com o qual pretendemos estabelecer comunicação também deve estar equipado com a interface apropriada. Infelizmente, os fabricantes de PCs pararam de inserir a porta série RS-232, que há alguns anos fazia parte do equipamento básico da maioria dos computadores.
Resta-nos a comunicação USB. Infelizmente, esta é uma tarefa bem difícil. Desta forma, geralmente são usados conversores USB-UART, o que simplifica bastante o trabalho. A boa notícia é que não precisa de se preocupar com isso na utilização do Arduino, o conversor já vem integrado na placa.
Portanto, tudo o que precisa de fazer é ligar o Arduino ao computador através do cabo USB (o mesmo que é usado na programação).
Vamos passar para os exemplos práticos. Os dois primeiros apenas requerem ligar o Arduino via USB ao computador. Só iremos adicionar periféricos ao sistema mais tarde.
Exercício Prático – Comunicação UART
Material necessário:
O objetivo do programa abaixo é muito simples: enviar um texto para o computador:
void setup(){ Serial.begin(9600); //Configuração da velocidade de transmissão Serial.println("Bem-vindo ao blog ElectroFun!"); //Envio de texto único } void loop() { delay(5000); Serial.println("Passaram 5 segundos"); //Envio de texto em loop }
Após o upload do programa acima, aparentemente nada acontece. Para verificar o seu funcionamento, precisa de selecionar no menu do Arduino: Ferramentas -> Monitor Série. Depois disso, vai abrir uma nova janela. Aqui, podemos observar o que é enviado para/do Arduino através da porta COM, que é o nosso UART. Vejamos a operação em prática:
Vamos agora analisar o programa. A primeira coisa ser feita foi a definição da baud-rate. A função Serial.begin () é usada para este propósito, onde entre parênteses se encontra a velocidade de transmissão. Nesse caso, é 9600 baud/seg. Por outro lado, a função Serial.println () é usada para enviar uma informação (frase ou números).
O texto “Bem-vindo ao blog ElectroFun!” é exibido apenas uma vez, porque está inserido na função void setup e, como se deve lembrar do capítulo anterior do curso, as instruções inseridas nessa função são realizadas apenas uma vez.
A transmissão também pode ser observada nos LEDs integrados no Arduino (Tx e Rx)! Estes acendem quando os dados estão a ser transferidos para/da placa.
Trabalho de Casa nº3
Verifique o que acontece quando escolhe velocidades diferentes da configurada no Arduino, no monitor série. Quando é que aparecem erros? Como é que se evidenciam? Como poderá verificar, os erros são bastante característicos, vale a pena fazer a experiência.
Exercício Prático – Interação com o programa
É claro que as informações não precisam de ser sempre enviadas por UART, a transmissão e receção também podem ocorrer uma vez num momento escolhido. Isto é muito útil, por exemplo, para diagnosticar a operação do sistema ou sinalizar diferentes ocorrências.
Usando o conhecimento adquirido anteriormente, poderá escrever um sketch que ativa um LED quando uma janela está aberta. É claro que não vamos usar um sensor de abertura de janelas, vamos simular utilizado componentes mais simples. Um botão de pressão vai substituir o sensor e dois LEDs servirão para sinalização.
Material necessário:
- 1x Arduino UNO e cabo USB;
- 1x Breadboard;
- 1x LED vermelho;
- 1x LED verde;
- 1x Botão de pressão;
- 2x Resistências de 330Ω;
- 5x Cabos jumper.
Faça a ligação da seguinte forma:
Quando a janela está fechada (botão pressionado), o LED verde está aceso. Quando abrimos o circuito (paramos de carregar no botão) o LED vermelho acende e no monitor série vamos ler a mensagem “Atenção! Alarme! A janela está aberta!”.
void setup(){ Serial.begin(9600); //Configuração da velocidade de transmissão pinMode(8, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED verde pinMode(10, INPUT_PULLUP); //Botão digitalWrite(8, LOW); //Desligar LEDs digitalWrite(9, LOW); } void loop() { if (digitalRead(10) == LOW) { //Se o botão for pressionado digitalWrite(9, HIGH); //Liga o LED verde digitalWrite(8, LOW); //Desliga o LED vermelho } else { //Se o botão não for pressionado digitalWrite(9, LOW); //Desliga o LED verde digitalWrite(8, HIGH); //Liga o LED vermelho Serial.println("Atenção! Alarme! A janela está aberta!"); } }
Vamos verificar como é que o programa está a funcionar! Infelizmente, não está muito bem. A informação de alarme é enviada o tempo todo. Preferimos que seja enviada apenas uma vez. Sabe como mudar isso? Claro, inserindo a função while!
void setup(){ Serial.begin(9600); //Configuração da velocidade de transmissão pinMode(8, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED verde pinMode(10, INPUT_PULLUP); //Botão digitalWrite(8, LOW); //Desligar LEDs digitalWrite(9, LOW); } void loop() { if (digitalRead(10) == LOW) { //Se o botão for pressionado digitalWrite(9, HIGH); //Liga o LED verde digitalWrite(8, LOW); //Desliga o LED vermelho } else { //Se o botão não for pressionado digitalWrite(9, LOW); //Desliga o LED verde digitalWrite(8, HIGH); //Liga o LED vermelho Serial.println("Atenção! Alarme! A janela está aberta!"); while (digitalRead(10) == HIGH) { //Criação de um loop vazio para a janela voltar a fechar delay(25); //Atraso de 25ms dentro do loop para minimizar interferências } } }
Podemos melhorar alguma coisa neste código? Não, mas podemos torná-lo mais elegante!
Instrução #define
Com o tempo, os nossos programas vão aumentar consideravelmente. E se for necessário alterar a conexão física de, por exemplo, um LED ou um botão? Alterar o número do pino de todo o código seria bastante difícil.
A instrução #define ajuda nesse aspeto. Permite que defina um símbolo para um determinado pino, que será substituído pelo número deste antes da compilação, em qualquer local do código. Por exemplo:
#define ledPin 8 void setup() { pinMode(ledPin, OUTPUT); //Configuração do pino 8 como saída } void loop() { digitalWrite(ledPin, HIGH); //Liga o LED delay(1000); //Espera 1 segundo digitalWrite(ledPin, LOW); //Desliga o LED delay(1000); //Espera 1 segundo }
Colocando a linha: #define ledPin 8 no início do código, fazemos com que, antes da compilação, qualquer parte do programa que possua como pino o “ledPin”, seja automaticamente transformada no número definido para este, nomeadamente 8. É claro que o nome do pino pode ser diferente, o importante é estabelecer um nome único, que o ajude a escrever programas longos.
IMPORTANTE: Depois da instrução #define não se coloca ponto e vírgula (;).
De seguida, está a nova e melhorada versão do código do sensor de abertura de janelas:
#define LEDvermelho 8 #define LEDverde 9 #define Botão 10 void setup(){ Serial.begin(9600); //Configuração da velocidade de transmissão pinMode(LEDvermelho, OUTPUT); //LED vermelho como saída pinMode(LEDverde, OUTPUT); //LED verde como saída pinMode(Botão, INPUT_PULLUP); //Botão digitalWrite(LEDvermelho, LOW); //Desligar LEDs digitalWrite(LEDverde, LOW); } void loop() { if (digitalRead(Botão) == LOW) { //Se o botão for pressionado digitalWrite(LEDverde, HIGH); //Liga LED verde digitalWrite(LEDvermelho, LOW); //Desliga LED vermelho } else { //Se o botão não for pressionado digitalWrite(LEDverde, LOW); //Desliga LED verde digitalWrite(LEDvermelho, HIGH); //Liga LED vermelho Serial.println("Atenção! Alarme! A janela está aberta!"); while (digitalRead(Botão) == HIGH) { //Criação de um loop vazio para a janela voltar a fechar delay(25); //Atraso de 25ms dentro do loop para minimizar interferências } } }
De agora em diante, só precisará de fazer a alteração de mudança de pino uma vez em todo o código!
Variáveis
Antes de passarmos para outros programas (incluindo envio de informações para o Arduino via UART), temos que saber o que são variáveis, e como é que estas funcionam.
As variáveis, de forma geral, são declarações de algum tipo de informação necessária para o código. Podem ser caracteres, palavras ou números. Na maioria das vezes, vamo-nos deparar com variáveis numéricas.
Quando é que as variáveis são necessárias? Quando queremos guardar um valor e executar vários tipos de operações com ele. Uma variável, assim como uma função, pode ter um tipo específico de informação, acerca de que tipo de dados pode armazenar.
Abaixo pode encontrar uma lista dos tipos de variáveis mais importantes:
boolean logica = false; //Boolean - verdadeiro (true) ou falso (false) int numero = 30000; //Int - números inteiros no intervalo de -32768 a 32767 long numeroGrande = 2000000; //Long - números inteiros no intervalo de -2147483648 a 2147483647 float numeroRacional = 6.28; //Float - números racionais que ocupem até 4 bytes de memória char caractere = 'a'; //Char - caracteres String frase = "Bem-vindo ao blog ElectroFun!"; //String - sequência de caracteres
NOTA: Os valores máximos que podem ser gravados numa variável dependem da placa Arduino utilizada. Os valores acima são apropriados para o Arduino UNO.
De início, iremos usar mais frequentemente as seguintes variáveis:
- Boolean – como mencionado, é usado para armazenar valores verdadeiros ou falsos. Este tipo de variável, geralmente é usado para sinalizar ocorrências ou condições de controlo;
- Int – é a variável mais comum para o armazenamento de números inteiros. Pode armazenar informações como o número de toques no teclado, quantas vezes ocorreu uma determinada situação ou um valor dado pelo sensor de distância (será realizado no final do curso). E, claro, pode realizar operações matemáticas nas variáveis – mais nos exemplos práticos;
- String – é um conjunto de caracteres, ou seja, de forma simplificada, podemos guardar uma palavra/frase/legenda/mensagem/etc.
Declaração de Variáveis
De forma a usar uma variável, é necessário declará-la, isto é, informar o compilador sobre o seu tipo e o seu nome. O nome de cada variável começa sempre com uma letra, nunca com um número, e não pode conter espaços. A declaração de uma variável deve ser feita da seguinte forma:
tipo nome = 0;
IMPORTANTE: Note que o símbolo = é usado para atribuir um valor de variável, e o símbolo == serve para comparar a igualdade entre variáveis e valores.
Também é importante referir que se uma variável for colocada dentro de uma função, instrução ou subprograma, esta ficará invisível (não poderemos usá-la) noutras funções. Vejamos:
int variavel = 0; //Variável global - pode ser usada em qualquer parte do programa void setup() { int variavel2 = 0; //Variável local - só pode ser utilizada dentro da função setup() } void loop() { int variavel3 = 0; //Variável local - só pode ser utilizada dentro da função loop() }
É normal que, para já, esteja tudo ainda um pouco verde, mas com tempo aprenderá tudo na prática. De início, para facilitar a aprendizagem, usaremos apenas variáveis globais. Os programadores experientes podem não gostar, no entanto, chegaremos a tudo, pouco a pouco.
Há mais um aspeto importante a apontar – denominar as variáveis. Lembre-se de dar nomes às variáveis que determinem o seu propósito. Por exemplo, em vez de:
string xx = "Manuel";
Coloque antes um nome único que identifique a variável:
string nomePessoa = "Manuel";
O problema poderá surgir quando o propósito das variáveis for mais complicado de definir. Não tenha receio de criar nomes como:
int vMotorEsquerdo = 100; //Velocidade do motor esquerdo
O mais importante é tornar o código legível e inteligível!
Exercício Prático – Variáveis
A teoria já passou, agora é hora de praticar o que aprendeu. Vamos começar com algo muito simples. De início, deixe o nosso código escrever um valor de variável, que vai aumentando, em cada ciclo de loop.
int contador = 0; //Declaração da variável void setup() { Serial.begin(9600); //Configuração da velocidade de transmissão } void loop() { Serial.println(contador); //Enviar o valor da variável contador = contador + 1; //Somar 1 ao valor do contador delay(100); //Atraso para tornar o programa mais percetível }
Obviamente, a declaração de variável foi colocada no início, fora de qualquer função. Graças a isso, podemos ter acesso à variável em qualquer parte do programa.
Primeiro, a taxa de transmissão é definida e inicializada e, de seguida, a função loop () executa 3 ações:
- Invoca o local da memória, no qual declaramos uma variável denominada “contador”, e envia o valor encontrado via UART;
- Aumenta +1 no valor recebido do contador;
- Espera 100ms (para uma melhor perceção) e volta ao início do loop.
Passaremos a explicar o ponto 2, que é o aumento do valor da variável. A instrução utilizada foi a seguinte:
contador = contador + 1; //Somar 1 ao valor do contador
Do ponto de vista matemático, onde o sinal “=” significa igualdade, a linha acima não deveria funcionar. No entanto, na programação, o sinal “=” significa atribuição. Na prática, o código acima deve ser entendido como a seguinte operação:
- Ir buscar o valor da variável contador;
- Somar 1 ao valor recebido;
- Receber o resultado da operação e atribuí-lo à própria variável.
Faça o download do programa para o Arduino e confira se está tudo a funcionar corretamente. É claro que, para ver os resultados, deverá abrir o monitor série.
Trabalho de Casa nº4
O que é que acontece quando a declaração de variável é incluída na função loop ()?
Trabalho de Casa nº5
Como mencionado, as variáveis têm as suas limitações. Mude a declaração de variável do contador de int para byte (pode conter números entre 0 e 255). O que é que acontece quando esse valor é excedido?
Transmissão Bidirecional do Arduino
É claro que a comunicação, para ser útil, deve ocorrer de forma bidirecional. Até agora, tem sido o Arduino a enviar-nos informações. Está na hora de lhe respondermos!
O objetivo do primeiro programa é “ouvir” o nosso nome. Quando lhe enviarmos o nome, o Arduino deverá responder com a seguinte mensagem “Olá, Nome!”, onde, obviamente, o nome será o anteriormente enviado.
String dadosRecebidos = ""; //Conjunto vazio de dados recebidos void setup() { Serial.begin(9600); //Configuração da velocidade de transmissão } void loop() { if(Serial.available() > 0) { //Se o Arduino receber dados dadosRecebidos = Serial.readStringUntil('\n'); // Lê os dados recebidos e guarda na própria variável Serial.println("Bem-vindo " + dadosRecebidos + "!"); //Mostrar a mensagem } }
Primeiramente, declaramos a variável dadosRecebidos, para a qual o conjunto de caracteres recebido (nome) será copiado. De seguida, como de costume, definimos a taxa de transmissão e iniciá-mo-la. Depois introduzimos uma nova função: Serial.available (). Esta envia o número de bytes que foram recebidos e que estão a aguardar suporte do Arduino.
No caso dos dados já estarem disponíveis (maior que 0), são enviados para a variável dadosRecebidos. Isto é feito através da função .readStringUntil (término) que copia os dados do buffer até encontrar um caractere de finalização (neste caso, “\n”).
Exercício Prático de Interação com o Sistema – Controlo de LEDs via UART
Material necessário:
- 1x Arduino UNO e cabo USB;
- 1x Breadboard;
- 1x LED verde;
- 1x LED vermelho;
- 2x Resistências de 330Ω;
- 3x Cabos jumper.
Neste exercício vamos usar capacidade de enviar texto para o Arduino para controlar LEDs. Para isso, ligue dois LEDs de acordo com o esquema abaixo (LEDs nos pinos 8 e 9):
O objetivo do programa é ligar o LED verde ou vermelho por 1 segundo, quando é enviado um comando apropriado para o Arduino. O código final é o seguinte:
#define verde 8 #define vermelho 9 String dadosRecebidos = ""; //Conjunto vazio de dados recebidos void setup() { Serial.begin(9600); //Configuração da velocidade de transmissão pinMode(verde, OUTPUT); //Configuração dos LEDs como saídas pinMode(vermelho, OUTPUT); digitalWrite(verde, LOW); //Desligar LEDs digitalWrite(vermelho, LOW); } void loop() { if(Serial.available() > 0) { //Se o Arduino receber dados dadosRecebidos = Serial.readStringUntil('\n');//Lê os dados recebidos e guarda na própria variável if (dadosRecebidos == "verde") { //Se escrever a palavra "verde" digitalWrite(verde, HIGH); //Liga o LED verde delay(1000); //Espera 1 segundo digitalWrite(verde, LOW); //Desliga o LED verde } if (dadosRecebidos == "vermelho") { //Se escrever a palavra "vermelho" digitalWrite(vermelho, HIGH); //Liga o LED vermelho delay(1000); //Espera 1 segundo digitalWrite(vermelho, LOW); //Desliga o LED vermelho } } }
Vamos agora analisar o funcionamento do programa. Inicialmente, os números dos pinos com LEDs são definidos e a variável para a qual os dados recebidos são copiados é declarada. No loop, é verificado se o Arduino recebeu os dados. Se sim, é averiguado se esses dados correspondem a uma das cores. Depois disto, é ligado o LED da cor indicada.
Trabalho de Casa nº6
Edite o código acima, de forma a que, quando é inserida uma cor errada (ex. laranja, azul, preto,…), seja enviada uma mensagem apropriada através do monitor série. Envie-nos a sua solução nos comentários abaixo!
Trabalho de Casa nº7
Este TPC é mais complexo! Escreva um programa que, depois de enviar a cor do LED, mude o seu estado para o oposto. Se o LED estiver aceso, será desligado e vice-versa.
Sumário
Este artigo possui informações fundamentais para programar com Arduino. As variáveis são elementos de programação utilizados em praticamente todos os códigos. Por sua vez, a comunicação UART costuma ser utilizada para exibir informações reunidas e alterar configurações do programa. Também é muito útil para procurar erros nos nossos códigos!
Apesar da grande quantidade de informação, foi uma lição relativamente fácil, mas se ainda tem algumas dificuldades não se preocupe. Com o tempo irá perceber tudo muito melhor!
Para treinar e consolidar alguns conhecimentos, é imperativo que realize os trabalhos de casa propostos! Estamos à espera das suas soluções e dúvidas nos comentários!
___________
O que achou deste artigo? Deixe o seu comentário abaixo, e partilhe nas Redes Sociais que certamente será útil e interessante para os seus amigos!
Curso Arduino – #0 – Introdução
Curso Arduino – #1 – O Básico do Arduino e o Software de Programação
Curso Arduino – #2 – O Básico da Programação e as Portas I/O
Curso Arduino – #3 – UART e Variáveis
Curso Arduino – #4 – Conversor Analógico-Digital
Curso Arduino – #5 – PWM, Servomecanismos e Bibliotecas
Curso Arduino – #6 – UART (continuação) e Servos
Curso Arduino – #8 – Controlo de Motores DC
Curso Arduino – #9 – Sensor de Distância Ultrassónico HC-SR04 e Novas Funções
Curso Arduino – #10 – Gráficos, Números Aleatórios e Condições