Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
O que é o Arduíno?..........................................................................................................................................................2
Constituição do Arduíno UNO R3...................................................................................................................................2
O Básico da Programação do Arduíno.............................................................................................................................3
Funções – o que é que significam?..................................................................................................................................3
Arduíno UNO – pinagem.................................................................................................................................................4
Exercício Prático de Saídas – LED..................................................................................................................................4
Exercício Prático de Delays – LED a Piscar....................................................................................................................5
Exercício Prático de Entradas – Instrução Condicional (if).............................................................................................6
Exemplo – Interruptor de Luz com “Temporizador”.......................................................................................................7
Exemplo – Semáforo.......................................................................................................................................................7
Como é que o UART funciona?.....................................................................................................................................12
Exercício Prático – Comunicação UART......................................................................................................................13
Exercício Prático – Interação com o programa..............................................................................................................14
Instrução #define...........................................................................................................................................................15
Variáveis........................................................................................................................................................................ 16
Declaração de Variáveis................................................................................................................................................17
Exercício Prático – Variáveis.........................................................................................................................................17
Transmissão Bidirecional do Arduíno...........................................................................................................................18
Exercício Prático de Interação com o Sistema – Controlo de LEDs via UART.............................................................19
Conversor Analógico-Digital.........................................................................................................................................20
Exercício Prático com ADC – Seleção de LEDs............................................................................................................23
Exercício Prático com ADC – Ativar um LED quando está escuro...............................................................................25
Exercício Prático com ADC – Ativar um LED quando está escuro v2..........................................................................26
O que é o sinal PWM?...................................................................................................................................................27
Para que é que o sinal PWM é usado?...........................................................................................................................28
Exercício Prático de PWM – Controlo do brilho de um LED........................................................................................28
Servo Motor................................................................................................................................................................... 30
Como é que o servo funciona?.......................................................................................................................................30
Alimentação do servo....................................................................................................................................................30
Exercício Prático – Servomecanismo.............................................................................................................................30
Representação Numérica no Monitor Série...................................................................................................................32
Precisão dos Números Não Inteiros...............................................................................................................................32
Função Serial.print()......................................................................................................................................................33
Exercício Prático – Novas informações UART.............................................................................................................34
Função Switch().............................................................................................................................................................35
Porquê juntar motores ao Arduíno?...............................................................................................................................37
Novas Funções............................................................................................................................................................... 43
Sensor de Distância Ultrassónico HC-SR04..................................................................................................................50
Exercício Prático – Alarme............................................................................................................................................54
O que é o Arduíno?
De forma geral, o Arduíno é uma plataforma de prototipagem eletrónica, que integra o popular microcontrolador AVR, e
permite o desenvolvimento de controlo de sistemas interativos. É um dispositivo de baixo custo e acessível a todos.
Algumas características são:
No entanto, o verdadeiro poder do Arduíno está na sua linguagem de programação baseada em C/C++.
A programação é realizada através de livrarias, graças às quais até a criação de um programa complicado está ao alcance
de um programador iniciante.
O Arduíno pode ser alimentado de várias formas. Os métodos mais populares são:
Alimentação via cabo USB;
Alimentação através de uma fonte de alimentação externa (baterias ou plug-in).
Os constituintes mais importantes estão sinalizados na imagem seguinte:
O símbolo “//” indica um comentário. É uma informação, de uma linha, que ajuda as pessoas a entender o programa.
Durante a compilação, todos os comentários são omitidos. Se quiser escrever um comentário mais longo, deve colocá-lo
entre “*/“.
No Arduíno, há aspetos que estão simplificados. Existem duas funções: uma delas executa a instrução uma vez, a outra
executa a instrução definida em loop. Vejamos:
void setup() {
//Instruções que são executadas apenas uma vez
}
void loop() {
//Instruções que são executadas em loop
}
Na prática, a primeira função geralmente contém as configurações. Por exemplo: a definição dos pinos como entradas ou
saídas. Nesta função, depois de ligar a placa, serão realizadas ações que é suposto só acontecerem uma vez.
Na segunda função, coloca-se o código que pretende executar o tempo todo (em loop). Vai perceber isto muito melhor
nos exemplos práticos mais abaixo.
Existe um conceito de função na linguagem C. Uma função, nas linguagens de programação, é um bloco (lista) de certos
comandos extraídos do código principal, cujo desempenho fornece um resultado.
Cada função pode integrar vários argumentos e enviar um resultado. O programador pode determinar quais valores serão
o resultado e os dados de entrada. Cada função tem o seu próprio tipo de resultado enviado (prefixos ex.: int, string, etc.)
– pode ser um número, um sinal ou outra coisa. Há também um tipo específico de função – não envia qualquer valor
(prefixo void).
void setup() {
}
O procedimento setup, como já vimos, é indicado para executar as instruções definidas uma só vez. Como o nome
sugere, é destinado principalmente a configurações gerais. Neste, o processador é iniciado, os periféricos são
configurados, etc.
void loop() {
}
A função (procedimento) loop é, como o nome indica, um loop infinito. É indicado para instruções que devem ser
executadas o tempo todo.
Arduíno UNO – pinagem
Abaixo pode ver um esquema com os principais pinos do Arduíno UNO:
A verde escuro (#0 a #19) estão indicados os pinos digitais de entrada/saída (I/O). Quando usados como saídas, podemos
definir como sendo 0V (nível lógico 0, low) ou 5V (nível lógico 1, high). Quando são configurados como entradas, são
capazes de detetar se o pino em questão possui tensão de 0V ou 5V.
As entradas analógicas (A0-A5) estão destacadas a verde claro. Estes são pinos únicos que permitem medir a tensão (0-
5V). Como pode ver, a numeração destes pinos coincide com os pinos universais (#14 a #19). Trabalhar no modo
analógico é uma função adicional deles.
Em azul, foram destacados pinos com funções alternativas. Isto significa que, além de serem pinos I/O normais, podem
executar funções mais complexas. Vamos trabalhar com eles posteriormente, mas para já só precisamos de uma
explicação básica:
o SDA, SCL – barramentos I2C usados, por exemplo, para comunicação com sensores mais avançados. Existem dois
pinos SDA e dois pinos SCL, no canto inferior esquerdo e superior direito da placa;
o TX, RX – interface UART, usados principalmente para comunicação com o computador;
o PWM – saídas nas quais é possível gerar um sinal retangular (variável). É função muito útil, por exemplo, no
controlo de servos;
o LED – LED permanentemente instalado no Arduino, que está diretamente ligado ao pino #13.
A cor-de-laranja estão saídas que não são programáveis. Estas são responsáveis, principalmente, pela alimentação do
sistema. Serão discutidas mais detalhadamente quando chegar a hora de as usar.
Os conectores indicados na imagem como ICSP são usados para programação direta de dois microcontroladores, que
estão localizados na placa Arduíno UNO. Esses conectores são usados em casos muito específicos e, não há necessidade
de falarmos sobre eles para já.
Material necessário:
1x Placa Arduino UNO e cabo USB;
1x Breadboard;
1x LED;
1x Resistência 330Ω;
2x Cabos jumper macho-macho.
O sistema deve ser ligado de acordo com esquema apresentado abaixo. O LED é ligado em série com uma resistência
(330RΩ). De seguida, ligue o pino mais longo do LED (ânodo) na mesma coluna do jumper ligado ao pino #8. O segundo
pino, deverá estar na mesma coluna de uma das pernas da resistência. A outra perna deve estar na mesma coluna que o
jumper ligado ao pino terra (GND). Existem 3 pinos GND na placa, pode escolher qualquer um.
void setup() {
pinMode(8, OUTPUT);
digitalWrite(8, HIGH);
}
void loop() {
}
A programação da inclusão do LED é muito simples. Ligue o Arduíno ao computador com o cabo USB. Abra o Arduíno
IDE e escreva o código abaixo. De seguida, faça o upload para a placa.
A função pinMode (Pin, Mode) permite selecionar se o pino é uma entrada ou uma saída. O pino é um número inteiro
entre 0 e 13 e o modo pode ser:
o INPUT
o OUTPUT
o INPUT_PULLUP.
Graças a esta configuração, pode definir o estado lógico na saída e, assim, ativar o LED. A função digitalWrite (Pin,
Status) é usada para este propósito. O estado é um estado lógico que pode ser HIGH ou LOW (alto ou baixo).
Depois de definir o pino num único estado, o seu valor não será alterado até definir um valor diferente. Portanto, o
programa acima fará com que o LED permaneça ligado o tempo todo.
void setup() {
pinMode(8, OUTPUT); //Definir o pino 8 como saída
}
void loop() {
digitalWrite(8, HIGH); //Ligar o LED
delay(1000); //Esperar 1 segundo
digitalWrite(8, LOW); //Desligar o LED
delay(1000); //Esperar 1 segundo
}
Na função loop, o estado é constantemente alternado. Foram adicionados atrasos ao programa através da função delay.
Esta função assume um determinado número de milissegundos a atrasar.
Se não introduzisse os atrasos, o sistema mudaria de estado tão rapidamente que seria impossível ver a alternância a olho
nu. Pode realizar esta experiência ao colocar 0ms como delay.
Material necessário:
A ligação deverá ser realizada de acordo com o esquema abaixo. De um lado, o botão foi ligado à terra (menos) e do outro
lado ao pino 7.
O objetivo é criar um programa que ligue o LED quando
o botão é pressionado. A tarefa é muito simples, mas
vamos inserir algo novo – instruções condicionais.
Ao combinar o conhecimento, pode criar um programa que realize a tarefa proposta. Analise o código e faça o upload
para o Arduíno.
void setup() {
pinMode(8, OUTPUT); //LED como saída
pinMode(7, INPUT_PULLUP); //Botão como entrada
digitalWrite(8, LOW); //Desliga o LED
}
void loop()
{
if (digitalRead(7) == LOW) { //Se o botão for pressionado
digitalWrite(8, HIGH); //Ligar o LED
} else { //Se o botão não for pressionado
digitalWrite(8, LOW); //Desligar LED
}
}
Todavia, este programa é de pouca utilidade. Por que é que precisamos de um botão que só funciona quando o estamos a
pressionar? Não seria melhor se o LED estivesse aceso por um determinado período de tempo após o botão ser
pressionado?
void setup() {
pinMode(8, OUTPUT); //LED como saída
pinMode(7, INPUT_PULLUP); //Botão como entrada
digitalWrite(8, LOW); //Desliga o LED
}
void loop()
{
if (digitalRead(7) == LOW) { //Se o botão for pressionado
digitalWrite(8, HIGH); //Ligar o LED
delay(10000); //Esperar 10 segundos
digitalWrite(8, LOW); //Desligar LED
}
}
Exemplo – Semáforo
O próximo exemplo é um sistema de semáforo sequencial. O principal objetivo é escrever um programa que, depois do
botão ser pressionado, mostre uma sequência correta de luzes. Vamos assumir o seguinte ciclo:
Quando pressionar o botão, o sistema deve iniciar a sequência. Vamos fazê-lo por etapas. Primeiro, ligue os 3 LEDs
conforme o esquema abaixo:
Vamos preparar um programa que serve apenas para configurar as entradas e saídas. Agora que estão os LEDs e o botão
configurados, vamos escrever um programa que mude as luzes automaticamente, a cada 1 segundo. O sketch completo
deverá ficar assim:
void setup() {
pinMode(10, OUTPUT); //LED vermelho
pinMode(9, OUTPUT); //LED amarelo
pinMode(8, OUTPUT); //LED verde
void loop()
{
digitalWrite(10, LOW); //Vermelho
digitalWrite(9, LOW); //Amarelo
digitalWrite(8, HIGH); //Verde
Até agora, usamos apenas o loop principal e obrigatório (função void loop). Agora é hora de conhecer um loop que
podemos usar dentro dos nossos programas.
Agora vamos abordar o loop while (), que funciona enquanto uma certa condição for cumprida (verdadeira). O seu
funcionamento é apresentado no seguinte código:
Para uma maior clareza, a função while apenas executa o código que fica entre as suas chavetas {}. O restante código não
é executado naquele momento.
Vamos aproveitar o sistema de semáforo que foi montado anteriormente, para escrever um programa que fará piscar um
LED de cada vez quando o botão for pressionado. Este exercício é uma tarefa mais difícil do que a anterior.
void loop() {
Se entender o código acima, pode prosseguir e completar a tarefa original: comutação automática das luzes.
Desta vez, as sequências devem ser exibidas até pressionar o botão. Assumimos que o botão é pressionado e largado
muito rapidamente. O programa finalizado deve ter este aspeto:
void setup() {
pinMode(10, OUTPUT); //LED vermelho
pinMode(9, OUTPUT); //LED amarelo
pinMode(8, OUTPUT); //LED verde
void loop()
{
digitalWrite(10, LOW); //Vermelho
digitalWrite(9, LOW); //Amarelo
digitalWrite(8, HIGH); //Verde
Neste caso, o loop foi usado de uma maneira bastante estranha. Como pode verificar não há nada dentro das chavetas!
Então, como é que o programa está a funcionar? Isso ocorre porque o programa usa loops para parar.
O que é que está a acontecer? Está tudo a funcionar como suposto? Claro que não! Mesmo quando o botão é pressionado
por um curto período de tempo, às vezes o programa funciona corretamente, e outras vezes, salta algumas posições. Por
que é que isso acontece?
O processador, de forma simplificada, realiza cerca de 16 milhões de operações por segundo. Portanto, ao pressionar o
botão, o processador será capaz de ter acesso a todos os estados da nossa sinalização. Posto isto, depois de largar o botão
poderá haver uma escolha aleatória na sequência.
Como resolver este problema? Muito simples! É suficiente alterar o programa, para que a mudança de luz não ocorra com
mais frequência do que, por exemplo, a cada segundo. Para isso, podemos usar a função delay () já conhecida.
void setup() {
pinMode(10, OUTPUT); //LED vermelho
pinMode(9, OUTPUT); //LED amarelo
pinMode(8, OUTPUT); //LED verde
void loop(){
digitalWrite(10, LOW); //Vermelho
digitalWrite(9, LOW); //Amarelo
digitalWrite(8, HIGH); //Verde
É importante ressaltar que as condições na função while () podem ser combinadas e muito mais complexas, mas
voltaremos a este tópico quando conhecermos as variáveis.
Vamos abordar o UART, que é uma interface série simples e muito utilizada. Especialmente quando se trata de uma
comunicação com o computador.
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.
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 Arduíno, o conversor já vem integrado na placa.
Portanto, tudo o que precisa de fazer é ligar o Arduíno ao computador através do cabo USB (o mesmo que é usado na
programação).
void setup(){
Serial.begin(9600); //Configuração da velocidade de transmissão
Serial.println("Bem-vindo!"); //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 Arduíno: Ferramentas -> Monitor Série. Depois disso, vai abrir uma nova janela. Aqui, podemos
observar o que é enviado para/do Arduíno 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!” é 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 Arduíno (Tx e Rx)! Estes acendem quando os dados
estão a ser transferidos para/da placa.
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:
o 1x Arduíno UNO e cabo USB;
o 1x Breadboard;
o 1x LED vermelho;
o 1x LED verde;
o 1x Botão de pressão;
o 2x Resistências de 330Ω;
o 5x Cabos jumper.
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
void loop() {
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
}
}
}
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.
#define LEDvermelho 8
#define LEDverde 9
#define Botão 10
void setup(){
Serial.begin(9600); //Configuração da velocidade de transmissão
void loop() {
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 Arduíno 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:
float numeroRacional = 6.28; //Float - números racionais que ocupem até 4 bytes de memória
NOTA: Os valores máximos que podem ser gravados numa variável dependem da placa Arduíno utilizada. Os valores
acima são apropriados para o Arduíno UNO.
o 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;
o 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;
o 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()
}
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:
1. Invoca o local da memória, no qual declaramos uma variável denominada “contador”, e envia o valor encontrado via
UART;
2. Aumenta +1 no valor recebido do contador;
3. Espera 100ms (para uma melhor perceção) e volta ao início do loop.
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:
1. Ir buscar o valor da variável contador;
2. Somar 1 ao valor recebido;
3. Receber o resultado da operação e atribuí-lo à própria variável.
Faça o download do programa para o Arduíno e confira se está tudo a funcionar corretamente. É claro que, para ver os
resultados, deverá abrir o monitor série.
Transmissão Bidirecional do Arduíno
É claro que a comunicação, para ser útil, deve ocorrer de forma bidirecional. Até agora, tem sido o Arduíno 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 Arduíno 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 Arduíno.
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”).
O objetivo do programa é ligar o LED verde ou vermelho por 1 segundo, quando é enviado um comando apropriado para
o Arduíno. O código final é o seguinte:
#define verde 8
#define vermelho 9
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);
void loop() {
if(Serial.available() > 0) { //Se o Arduíno receber dados
dadosRecebidos = Serial.readStringUntil('\n'); //Lê os dados recebidos e guarda na própria variável
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 Arduíno recebeu os dados. Se
sim, é averiguado se esses dados correspondem a uma das cores. Depois disto, é ligado o LED da cor indicada.
Conversor Analógico-Digital
O mundo que nos rodeia não é só digital, e, às vezes, 2 estados (high e low) não são suficientes para realizar o projeto
pretendido.
Por isso é que, vamos falar do conversor analógico-digital, abreviado como ADC (analog-to-digital converter).
Introdução Teórica
A eletrónica divide-se em dois tipos de sinais: digital e analógico. Os sinais digitais estão limitados a dois estados: High
(1) ou Low (0). Na prática, significa que o Arduíno reconhecerá uma de duas tensões: 5V ou 0V, respetivamente.
Infelizmente, nem tudo em nosso redor pode ser descrito de uma forma tão simples. Por exemplo, ao usarmos um sensor
de distância, provavelmente pretendemos saber a distância exata de um obstáculo, e não apenas informações básicas
como: obstáculo detetado/sem obstáculo.
Tal sensor, na sua saída, pode fornecer uma tensão proporcional à distância do obstáculo. Então, a medição consiste em
ler a tensão numa faixa de 0 a 5V. Esta é a abordagem analógica. Portanto, nos exemplos seguintes, vamos usar os
periféricos apropriados do Arduíno, que permitem medir a tensão fornecida às entradas especiais do sistema.
ATENÇÃO: Lembre-se que as entradas do Arduíno só são compatíveis com tensões incluídas na seguinte faixa: 0-5V.
Outras tensões poderão danificar a placa e até o computador ao qual ligar o circuito.
Para o processamento de sinais analógicos, é utilizado o chamado ADC: conversor analógico-digital. Este é um dos
periféricos mais populares encontrados em microcontroladores. O seu trabalho é converter uma tensão aplicada numa
entrada do sistema num formato digital. Por exemplo (transdutor linear):
Neste caso, apresentamos um conversor de 8 bits, já que o valor lido poderia ter 256 combinações (começando em 0). Na
verdade, podemos encontrar vários transdutores, por exemplo de 12 ou 16 bits. O conversor que possui mais bits é o mais
preciso, porque seu o valor máximo é maior (funciona no mesmo intervalo, mas com uma resolução mais alta).
Como curiosidade, é interessante saber que os conversores analógico-digitais operam de forma relativamente lenta.
Naturalmente, não vai verificar isso nos seus programas, pois, mesmo sendo “lentos” são inacreditavelmente rápidos (até
10.000 medições por segundo). No entanto, comparando o ADC a outros periféricos do microcontrolador, é a “ovelha
negra”.
Os ADCs são, também, muito caros de produzir. Como se deve lembrar, na segunda lição do curso, verificamos que o
Arduíno UNO possui 6 entradas analógicas (A0-A5). De facto, na placa, existe apenas um conversor analógico-digital,
que consegue medir a tensão das seis entradas.
Material necessário:
É hora de verificar como é que o ADC funciona na prática. Para isso, faça a montagem do circuito de acordo com a figura
abaixo.
O uso de software do ADC é trivial e limitado ao uso de apenas uma função analogRead (canal ADC), onde o canal ADC
é o pino analógico utilizado (A0-A5).
int valorLido = 0;
void setup() {
Serial.begin(9600); //Configuração da velocidade de transmissão
}
void loop() {
valorLido = analogRead(A5); //Ler valor da tensão na entrada A5
Serial.println(valorLido); //Enviar sinal para o monitor série
delay(200);//Esperar 200ms
}
Ao girar o potenciómetro, obtemos valores de 0 a 1023 no monitor série. Isto significa que o conversor analógico-digital
integrado no Arduíno UNO, é de 10 bits.
Simplificando, qualquer aumento na tensão de entrada de ~0.005V aumentará a leitura do ADC em um. Assim, para obter
um resultado em volts basta adicionar uma linha:
void setup() {
Serial.begin(9600); //Configuração da taxa de transmissão
}
void loop() {
valorLido = analogRead(A5); //Ler valor da tensão na entrada A5
tensão = valorLido * (5.0/1023.0); //Converter valores em volts
Serial.println(tensão); //Enviar tensão medida para monitor série
delay(200); //Esperar 200ms
}
Note que a declaração de tensão foi feita através de uma variável float, para que seja possível armazenar valores/números
racionais.
Deverá conseguir ver no monitor série valores entre 0 e 5V. Acabamos de construir um voltímetro muito simples!
O exemplo anterior pretendia apenas demonstrar o funcionamento do conversor. Agora vamos usar o potenciómetro para
influenciar o funcionamento do programa.
Material necessário:
o 1x Arduíno UNO e cabo USB;
o 1x Breadboard;
o 1x Potenciómetro;
o 1x LED;
o 1x Resistência de 330Ω;
o 6x Cabos jumper.
O que é que será que acontece se o valor lido do ADC determinar o delay do programa? Este é o exemplo mais rápido
para construir um dispositivo com um LED, que pisca a uma frequência controlada por um potenciómetro:
void setup() {
pinMode(2, OUTPUT); //Configuração do LED como saída
}
void loop() {
valorLido = analogRead(A5); //Ler valor do ADC
digitalWrite(2, HIGH); //Ligar o LED
delay(valorLido); //Tempo de espera igual ao valor lido pelo ADC
digitalWrite(2, LOW); //Desligar o LED
delay(valorLido); //Tempo de espera igual ao valor lido pelo ADC
}
Material necessário:
Primeiro, necessita de ligar todo o circuito. O nosso está disposto da seguinte forma:
A tarefa é simples, basta dividir o valor máximo que podemos ler do ADC (1023) por 5 e, com base nisso, criar condições
para a ativação individual dos LEDs. Mas, para isso, podemos usar a função map (), que nos facilita muito o trabalho!
Como resultado da linha de código acima, obtém-se sempre um valor no intervalo compreendido entre 1 e 5. Vejamos o
código completo:
int valorLido = 0;
void setup() {
pinMode(8, OUTPUT); //Configuração dos LEDs como saídas
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
}
void loop() {
valorLido = analogRead(A5); //Ler valor do ADC
valorLido = map(valorLido, 0, 1023, 1, 5); //Valores para divisão
Verifique o que acontece aos LEDs quando roda o potenciómetro. Se tudo estiver a funcionar corretamente, e se estiver tudo
bem ligado, quando roda o potenciómetro deverá acender um LED diferente que estava aceso anteriormente.
Quando uma grande quantidade de luz incide sobre o LDR, a sua resistência é mínima e se estiver ligado ao Arduíno
possui uma tensão relativamente alta. Quando escurece, a resistência do LDR aumenta e a tensão é baixa.
Com este programa, será capaz de criar uma lâmpada que liga quando está escuro. O código é muito simples:
void setup() {
pinMode(8, OUTPUT); //Configuração do LED como saída
}
void loop() {
valorLido = analogRead(A5); //Ler valor do ADC
Existe uma condição que é responsável por ligar/desligar o LED. A questão é: como escolher o valor de mudança? O
melhor é fazê-lo à mão, mas é pouco prático… Digamos que quer mudar o projeto de local, para uma sala com condições
mínimas muito diferentes. Teremos de reconfigurar o código novamente? Sim, a menos que…
Em vez de tornar o LED dependente de uma faixa muito específica, o objetivo é ajustar suavemente o limite de
comutação com o potenciómetro. A alteração do código é muito fácil e consiste em editar 3 linhas de código:
void setup() {
pinMode(8, OUTPUT); //Configuração do LED como saída
}
void loop() {
valorLido = analogRead(A5); //Ler valor do ADC
valorComutacao = analogRead(A4); //Ler valor do ADC
if (valorLido < valorComutacao) { //Se for mais escuro do que o limite encontrado
digitalWrite(8, HIGH); //Liga o LED
} else {
digitalWrite(8, LOW); //Desliga o LED
}
delay(50);
}
void setup() {
pinMode(3, OUTPUT); //Configuração do LED como saída
}
void loop() {
digitalWrite(3, HIGH); //Liga o LED
delay(1000); //Espera 1 segundo
digitalWrite(3, LOW); //Desliga o LED
delay(1000); //Espera 1 segundo
}
Se desenhássemos um gráfico que demonstrasse a mudança de tensão em função do tempo do pino 3, obteríamos a
seguinte onda:
O valor marcado como x é o tempo em que o LED está ligado. De forma oposta, T é o período de tempo em que o LED
está desligado. Por sua vez, o seu inverso, isto é, 1/T, indica a frequência. A relação entre o tempo que o LED está ligado
e o tempo que o LED está desligado é 1:1. Por outras palavras, o LED está ativo durante 50% da operação do programa.
Este aspeto é denominado duty cycle.
Agora outro exercício semelhante. No entanto, com um duty cycle diferente, ainda que mantendo o período. Como fazê-
lo? Basta prolongar o tempo de operação do LED, reduzindo o tempo em que desligado. Por exemplo:
void setup() {
pinMode(3, OUTPUT); //Configuração do LED como saída
}
void loop() {
digitalWrite(3, HIGH); //Ligar o LED
delay(1667);
digitalWrite(3, LOW); //Desligar o LED
delay(333);
}
Desta vez, o LED fica ligado cerca de 5/6 do tempo. Então o duty cycle é cerca de 83%. Apresentando a situação num
gráfico obtemos:
De forma oposta, se trocarmos os delays, o duty cycle do sinal fica cerca de 17%. Vejamos:
Dê uma vista de olhos nos exemplos acima. Qual dos parâmetros mudou em cada exemplo? A resposta é fácil: o duty
cycle. A frequência permaneceu igual.
Agora imagine que os delays inseridos nos códigos acima eram muito mais pequenos, graças aos quais a frequência do
sinal é muito maior… Parabéns! Acabou de entender o princípio do PWM. É um método de modular um sinal retangular
através do ajuste da largura de pulso.
Material necessário:
1x Arduino UNO e cabo USB;
1x Breadboard;
1x LED;
1x Resistência de 330Ω;
2x Cabos jumper.
Como já vimos no segundo artigo do curso, o Arduíno está equipado com 6 canais PWM. Cada saída, na qual podemos
obter o sinal PWM, foi marcada na placa com um til “~”.
Cada canal PWM disponível no Arduíno UNO é de 8 bits. Isto significa que, o sinal que queremos receber na sua saída,
pode ser definido por um número de 0 a 255, onde 255 significa 100% de duty cycle.
Para realizar o primeiro exercício, é necessário ligar o LED ao pino 3 da seguinte forma:
#define pinoLED 3
int dutyCycle = 0;
int mudanca = 5;
void setup() {
pinMode(pinoLED, OUTPUT); //Configuração do LED como saída
}
void loop() {
analogWrite(pinoLED, dutyCycle); //Gerar um sinal com determinado duty cycle
if (dutyCycle < 255) { //Se o duty cycle for menor do que 100%
dutyCycle = dutyCycle + mudanca; //Aumenta o duty cycle
} else {
dutyCycle = 0; //Se o duty cycle for igual a 100%, volta ao início
}
Esperamos que tenha ficado claro. Podemos agora discutir a nova função inserida: analogWrite (pino, duty cycle). O seu
objetivo é gerar o sinal PWM no pino selecionado com o duty cycle indicado.
O programa acima pretende aumentar periodicamente o duty cycle de zero para o momento em que o seu valor é
imediatamente menor do que 255 (100%). Quando é alcançado o duty cycle máximo, o LED desliga e o processo é
repetido.
Servo Motor
Um servomecanismo é um motor, caixa de velocidades e controlador num só dispositivo. No entanto, estes motores não
são projetados para executar rotações completas. Na maioria das vezes, os servos possuem um ângulo de rotação de 0-
180º. É importante saber que eles conhecem a sua posição atual, por isso não precisa de se preocupar com erros de
posição.
1. Não se deve girar manualmente a posição do eixo, sem necessidade. Pode danificar as delicadas engrenagens de
plástico;
2. Não se deve alimentar o servo diretamente da fonte de alimentação usada no restante sistema. Cada motor recebe
uma corrente relativamente alta, especialmente no início do movimento. Isso pode perturbar o funcionamento dos
outros dispositivos e, em casos extremos, danificá-los.
Um padrão aceitável é o envio de um sinal com um período de 20ms para o servo. O duty cycle é interpretado como a
posição para a qual o servo deve ser movido. O duty cycle do sinal gerado deve encontrar-se entre 5 e 10%. Estes valores
serão convertidos em duas posições extremas no servo (máximo esquerdo e máximo direito).
o Vermelho – alimentação;
o Amarelo ou laranja – controlo do sinal;
o Preto ou castanho – GND.
Dependendo do fabricante, as cores dos fios podem variar. No entanto, dois serão definitivamente preto/castanho e
vermelho. O restante será o fio de sinal.
Alimentação do servo
Como foi referido anteriormente, não deve alimentar o servo diretamente da mesma fonte que alimenta o
microcontrolador. Portanto, devido ao fato de o motor consumir uma grande corrente, deve ser utilizada uma fonte
adequada para alimentação do sistema.
Infelizmente, a alimentação a partir da porta USB, como havíamos feito até agora, não é suficiente. Portanto, pela
primeira vez, vamos alimentar a placa com uma bateria de 9V!
Material necessário:
o 1x Arduino UNO e cabo USB;
o 1x Breadboard;
o 1x Servo Motor SG90;
o 1x Regulador de tensão LM7805;
o 1x Bateria de 9V;
o 1x Ligador para bateria de 9V;
o 7x Cabos jumper.
Em primeiro lugar, é necessário ligar a bateria. Em segundo, é preciso incluir um regulador de tensão LM7805.
void setup(){
servomecanismo.attach(9); //Servo ligado ao pino 9
}
void loop(){
if (posicao < 180) { //Se a posição for inferior a 180 graus
servomecanismo.write(posicao); //Move-se
} else { //Caso contrário, volta ao início
posicao = 0;
}
posicao = posicao + mudanca; //Aumentar a posição atual do servo
delay(200); //Atraso para melhor efeito
}
Desta vez, adicionámos uma biblioteca que vai expandir as capacidades do programa com as nossas funções. O comando
usado é:
#include Servo.h
Neste caso, adicionamos o arquivo Servo.h, que contém instruções adicionais do servo. Graças a este, não precisámos de
ser nós a controlar o sinal PWM. É suficiente indicar as posições (ângulo) para as quais queremos que o servo gire.
Para controlar o servo, é necessário declará-lo:
Servo servomecanismo;
A função attach (pino) – para o objeto Servo – funciona de maneira semelhante ao pinMode. A partir desta instrução, será
gerado, na saída indicada (neste caso 9), um sinal PWM.
Depois de iniciar o programa, o servo deve mover-se suavemente de uma posição extrema para a outra em loop. A
instrução chave é:
servomecanismo.write(posição);
[...]
int numero = 2345;
Como pode perceber, nos exemplos anteriores, a representação numérica foi realizada, por padrão, na forma decimal. No
entanto, pode escolher o sistema pretendido de entre quatro: hexadecimal, octal, binário e, claro, decimal. Na prática,
provavelmente só usará o sistema decimal e o binário.
Serial.println(PI); //Mistério
}
void loop() {
}
Ao enviar o programa para o Arduíno e abrir o monitor série, deverá aparecer a seguinte sequência:
Como pode ver, existe um parâmetro adicional para definir a precisão dos valores enviados (número de casas decimais).
Lembre-se que as variáveis float podem ser representadas com um máximo de 7 dígitos. Por exemplo:
float numero1 = 0.123456 √
float numero2 = 12345.6 √
float numero3 = 123.456 √
float numero4 = 1234567.8 ×
Vamos voltar ao código acima por um momento, especificamente para a seguinte linha misteriosa:
Serial.println(PI); //Mistério
Por que é que este comando enviou o número 3,14? Nós não declaramos tal variável em lugar nenhum. A verdade é que o
valor do número Pi é tão usado que, em muitas linguagens pode encontrar constantes “prontas”. Neste caso, qualquer
local do código com “PI” escrito, será transformado no valor apropriado da constante.
Se precisar de uma maior precisão no seu programa, pode sempre escrever a seguinte função:
Serial.print(PI, 25);
void loop() {
Serial.print("Bem-vindo! "); //Enviar texto
delay(1000); //Atraso de 1 segundo para melhor efeito
}
O resultado não é impressionante, mas mostra o que é pretendido: não há linhas novas.
Como é que pode criar uma nova linha quando pretendido? Existem 3 formas:
Serial.print("Primeira linha");
Serial.println();
Serial.print("Segunda linha");
OU
Serial.println("Primeira linha");
Serial.print("Segunda linha");
OU
Serial.print("Primeira linha \n Segunda linha");
A forma mais interessante é, definitivamente, a última. É incluído um novo símbolo “\n“. Não é único do Arduíno e
significa transição para uma nova linha. Como pode ver, é muito conveniente porque permite passar a uma nova linha em
qualquer momento.
Existem outros símbolos úteis como este? Sim! Para formatar o texto, também é útil usar o Tab. Se quiser mover o texto
para a direita, deve usar o \t – é muito mais prático do que inserir uma quantidade absurda de espaços.
Material necessário:
1x Arduino UNO e cabo USB;
1x Breadboard;
1x Potenciómetro;
5x Cabos jumper.
De início, faça todas as conexões necessárias. Vamos usar o potenciómetro para regular a tensão.
Esperamos que não tenha sido demasiada informação de uma vez, e que tenha percebido tudo. De qualquer das formas,
preparamos o código para o ajudar. O que é pretendido é que analise e compreenda o programa e, depois, escreva um
semelhante!
void setup() {
Serial.begin(9600); //Configuração da taxa de transmissão
}
void loop() {
int potenciometro = analogRead(A5); //Ler valores do ADC
Serial.print("Ler: ");
Serial.print(potenciometro, DEC);
Serial.print("[DEC]\t");
Serial.print(potenciometro, HEX);
Serial.print("[HEX]\t");
Serial.print(potenciometro, OCT);
Serial.print("[OCT]\t");
Serial.print(potenciometro, BIN);
Serial.print("[BIN]\n");
Depois de executar o programa, no monitor série, deve observar os valores dados pelo ADC bem formatados e em
diferentes sistemas numéricos:
Função Switch()
Vamos agora abordar a função de controlo switch, usada muito frequentemente. É aplicada em situações em que, com
base numa variável, são realizadas várias ações diferentes, dependendo do valor fornecido.
Para entender esta função, vamos usar um exemplo, que será resolvido de duas maneiras – de forma tradicional e através
de um novo método. Portanto, vamos supor que queremos escrever um programa que leia o valor ADC e, de seguida, o
envie para o monitor série na forma de números decimais, hexadecimais, octais ou binários. Tudo depende da nossa
escolha.
void setup() {
Serial.begin(9600); //Configuração da taxa de transmissão
}
void loop() {
int potenciometro = analogRead(A5); //Ler valores do ADC
if (dadosRecebidos == "d") {
Serial.println(potenciometro, DEC);
} else if (dadosRecebidos == "h") {
Serial.println(potenciometro, HEX);
} else if (dadosRecebidos == "o") {
Serial.println(potenciometro, OCT);
} else if (dadosRecebidos == "b") {
Serial.println(potenciometro, BIN);
}
delay(1000); //Atraso de 1 segundo para melhor efeito
}
Exequível? Sim. Prático? Mais ou menos. Não seria ideal se, por exemplo, houvesse muito mais condições, ou se fosse
necessário mudá-las. É aqui que a nova função switch entra para ajudar! Possui este aspeto:
switch (ValoraVerificar) {
case valor_1:
//O código é executado se a função for atendida
break;
case valor_2:
//O código é executado se a função for atendida
break;
[...]
default:
//Código a ser executado se a condição não foi satisfeita
break;
}
No início, escrevemos a palavra-chave switch e, dentro dos parênteses, indicamos a variável que queremos verificar. Em
seguida, abrimos chavetas. Dentro delas, pode inserir qualquer número de condições que serão verificadas
sucessivamente. Fazemos isso ao escrever a palavra case. Depois do espaço colocamos um valor, que deve ser igual à
variável que pretendemos verificar. Terminámos com dois pontos “:”.
Se a condição for atendida, o código será executado até ao break seguinte. Quando a condição não é atendida, parte do
código é ignorada e o microcontrolador passa a verificar a próxima condição (case).
No final, opcionalmente, podemos colocar um código entre default e break. Este será executado quando nenhuma das
condições anteriores tiver sido verificada.
Percebemos que possa parecer complicado, e é por isso que vamos passar para o exemplo prático e vamos transformar o
programa anterior.
int dadosRecebidos = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
int potenciometro = analogRead(A5); //Ler valores do ADC
switch (dadosRecebidos) {
case 'd':
Serial.println(potenciometro, DEC);
break;
case 'h':
Serial.println(potenciometro, HEX);
break;
case 'o':
Serial.println(potenciometro, OCT);
break;
case 'b':
Serial.println(potenciometro, BIN);
break;
}
delay(1000);
}
Uma pequena nota, a função switch funciona somente com base na comparação de números. Portanto, neste exemplo, as
letras que estamos a controlar: d, h, o, b devem ser tratadas não como letras, mas como códigos ASCII. Escrever uma
letra dentro de apóstrofos, depois de case, faz com que estas sejam tratadas como códigos ASCII.
dadosRecebidos = Serial.readStringUntil('\n');
Foi usada uma função mais simples, que apenas lê o primeiro byte (sinal) dos dados:
O Arduíno, mais especificamente o microcontrolador integrado, controla os sinais. A eficiência de cada saída é
relativamente pequena (cerca de 20mA). É fácil supor que, 99,999% dos motores que encontrar, vão precisar de muito
mais corrente. Posto isto, ao ligar um motor ao Arduíno corre o risco de danificar irreversivelmente a placa.
Introdução às Pontes H
Portanto, nos exercícios seguintes, os motores vão ser substituídos por LEDs. Será na mesma capaz de observar a
mudança de direção da rotação (através do LED que acender) e a mudança da velocidade (através do brilho do LED).
São necessários componentes intermédios entre o Arduíno e os motores. Estes são frequentemente denominadas pontes
H. Estes drivers podem ser construídos a partir de vários transístores ou pode simplesmente usar um circuito integrado
ponte H. Como é iniciante, o melhor é recorrer a um chip já pronto a utilizar.
O principal objetivo das pontes H é ler e converter os sinais enviados pelo microcontrolador em tamanhos compatíveis
com o controlo do motor. Por exemplo, o Arduíno, cujos sinais podem funcionar a um máximo de 5V/20mA, após a
aplicação da ponte H, pode facilmente controlar um motor que requer 12V/1A para funcionar.
Para parar ou mudar a direção de rotação do motor, deverá definir os sinais de acordo com o diagrama seguinte,
denominada tabela verdade:
Vamos agora programar, tendo por base o esquema ligado anteriormente. No início, vamos abordar o controlo da rotação
do motor. Deixaremos a regulação da velocidade para mais tarde. Como pode verificar, os pinos do Arduíno responsáveis
pelo motor são os seguintes:
o 6 (PWM) – regulação da velocidade;
o 7, 8 – Controlo do sentido de rotação.
Se não quisermos controlar a velocidade do motor, precisamos de definir o pino 6 como high. Também podíamos ligar o
cabo diretamente a 5V. No entanto, como já temos as ligações feitas, vamos usar o Arduíno:
void setup() {
pinMode(6, OUTPUT); //Sinal PWM do motor para controlo da velocidade
digitalWrite(6, HIGH); //Definir permanentemente o estado high no pino 6 - velocidade máxima
void loop() {
//Restante programa
}
Se o sistema foi ligado corretamente, depois de fazer o upload do programa não deverá acontecer nada! Agora é hora de
adicionar o restante código à função loop.
Vamos supor que gostaríamos de rodar o motor 3 segundos num sentido e 3 segundos no sentido oposto (velocidade
máxima). Para este fim, devemos adicionar uma parte muito simples ao programa:
void setup() {
pinMode(6, OUTPUT); //Sinal PWM do motor para controlo da velocidade
digitalWrite(6, HIGH); //Definir permanentemente o estado high no pino 6 - velocidade máxima
De acordo com a tabela verdade apresentada anteriormente, sabemos que o sistema funciona se uma das entradas tiver no
estado low “0” e a outra no estado high “1”. O sentido da rotação depende do sinal de entrada.
Depois de fazer o upload do programa para o Arduino, os dois LEDs devem começar a piscar alternadamente. Concentre-
se e entenda por que é que isso está a acontecer. Os LEDs brilham alternadamente porque, a corrente flui da saída 1 para a
saída 2, e vice-versa. O piscar é possível porque estes foram inseridos inversamente.
Para poder continuar, é necessário dominar os conteúdos abordados no ponto PWM, Servomecanismos e Bibliotecas.
Como pode adivinhar, basta ligar um sinal PWM ao pino ENABLE1 (circuito L293D) para controlar a velocidade do
motor!
Vamos agora escrever um programa simples, que rode os motores alternadamente, de forma mais lenta ou mais rápida.
Lembre-se que o brilho do LED apenas é visível acima de um certo limite de duty cycle do sinal PWM. O mesmo
acontece com os motores. Toda a gente terá um valor diferente.
void setup() {
pinMode(6, OUTPUT); //Sinal PWM do motor para controlo da velocidade
Deve verificar que um LED muda claramente o seu brilho – mas faz isso rapidamente! Agora inserir uma mudança suave
na velocidade do motor.
Função for()
Vamos agora discutir uma função loop extremamente útil. A função for() permite executar uma determinada instrução,
um determinado número de vezes. Vamos usá-la quando, por exemplo, pretendermos que escreva 10 números naturais
consecutivos, pisque um LED 5 vezes, etc. No entanto, antes passarmos ao exemplo, vamos mostrar a sintaxe:
Parece complicado, mas felizmente é muito simples. Depois da palavra for, abrimos parênteses e declaramos três aspetos
muito importantes. O primeiro é a declaração da variável. Como o código vai ser executado um determinado número de
vezes, deverá ser guardado num local o número de vezes que já foi realizado.
Depois do ponto e vírgula, indicamos a condição. O loop é executado até que a condição deixe de ser verificada. Após o
ponto e vírgula seguinte, inserimos a operação a ser executada na variável durante cada ciclo. Por fim, fechamos
parênteses.
Vamos utilizar o loop for para escrever uma saudação, 25 vezes, através de UART.
void setup() {
Serial.begin(9600); //Configuração da velocidade de transmissão
void loop() {
}
O int i = 0 significa que declaramos uma nova variável local denominada “i“, do tipo int, em que o seu valor inicial é 0.
Se tiver dificuldades em perceber esta declaração, volte novamente ao artigo #3 do curso.
Neste caso, a condição é i < 25. Ou seja, enquanto a variável for menor do que 25, a instrução será repetida.
O último elemento, i++, é a soma de 1 à variável, de forma a que esta tenha registo do número de vezes que o ciclo foi
realizado. Este registo também pode ser substituído por i = i + 1.
void setup() {
Serial.begin(9600); //Configuração da velocidade de transmissão
Para além desta disposição, existem outras que possuem exatamente o mesmo efeito:
ou
ou
for (int i = 1; i < 26; i++) { //Executar ciclo 25 vezes
ou
ou
Como pode ver, existem muitas possibilidades! Mas é claro que, a que nós utilizamos no exemplo é a mais frequente.
Agora podemos voltar à questão dos motores, isto é, à regulação suave da velocidade.
Como já conhecemos o ciclo for, podemos usá-lo para acelerar suavemente o motor. É suficiente para alterar o sinal
PWM suavemente, por exemplo, a cada 25 ms.
void setup() {
pinMode(6, OUTPUT); //Sinal PWM do motor para controlo da velocidade
void loop() {
for (int i = 0; i <= 255; i++) {
analogWrite(6, i); //Aceleração suave do motor
delay(25);
}
}
Muitas vezes, os iniciantes têm problemas porque o robot não quer andar. Na maioria das vezes, verifica-se que, ou a
fonte de alimentação utilizada é muito fraca, ou o sinal PWM tem pouco duty cycle. Se algum dia tiver algum problema,
lembre-se de verificar estes dois aspetos!
Novas Funções
O Sensor de Distância Ultrassónico HC-SR04, que é muito comum nos projetos Arduíno. Também, está na hora de
começar a escrever suas próprias funções. No final, vamos apresentar um outro elemento – buzzer.
Esperamos que esta parte seja interessante para os construtores de robots e para todos aqueles que gostariam de usar um
sensor de distância nos seus projetos. Porém, antes de introduzirmos o sensor, vamos abordar novos truques de
programação, que vão tornar os seus programas ainda melhores!
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
}
void loop() {
digitalWrite(13, HIGH); //Liga o LED
delay(1000); //Espera 1 segundo
digitalWrite(13, LOW); //Desliga o LED
delay(1000); //Espera 1 segundo
}
Imagine uma situação em que o seu programa é muito extenso e quer usar este piscar como confirmação de operações
selecionadas. Vai rapidamente reparar que a repetição do código responsável por ligar/desligar o LED consome muito do
seu tempo. O que é pior é que também torna todo o programa muito mais difícil de analisar.
Ao declarar uma variável, podemos utilizá-la inúmeras vezes de uma forma muito simples. Se pudéssemos escrever
sequências de operações sob um nome fácil, os programas seriam muito mais legíveis. Quaisquer alterações que
pretendêssemos fazer, também seriam mais fáceis de realizar. As funções ajudam neste aspeto, já que podemos criá-las
nós próprios.
Vamos voltar ao exemplo anterior do LED a piscar. O fragmento de código seguinte é responsável por essa operação:
Podemos retirar este fragmento da função loop e criar uma função própria com ele. Como é que o fazemos? No início, à
semelhança de uma variável, é necessário declarar o tipo e o nome da função. Fazemo-lo depois da função loop () {}.
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
}
void loop() {
}
void piscarLED() {
//Conteúdo da função
}
Como pode ver, antes do nome da função (piscarLED) vem o tipo de função. Não é nada mais do que informação acerca
da função, se envia algum valor/mensagem/etc. após o término da atividade. Se fosse esse o caso, então deveria ser
colocado o tipo apropriado de função (de acordo com os tipos de variáveis). Por exemplo, um inteiro (int), um caracter
(char), um número não inteiro (float), etc. Neste caso, o tipo de função é void. Isto significa que a função não retorna
qualquer valor/mensagem/etc. Porque é que escolhemos este tipo? O objetivo da função é piscar o LED, esta operação
não envia nenhum resultado, além do que é visível.
Após o nome da função deverá aparecer parênteses. Para já, vamos assumir que não há nada dentro deles. Depois abrimos
chavetas e colocámos o código pretendido. Fechámos chavetas e está pronto. Na prática será assim:
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
}
void loop() {
piscarLED();
}
void piscarLED() {
digitalWrite(13, HIGH); //Liga o LED
delay(500); //Espera meio segundo
digitalWrite(13, LOW); //Desliga o LED
delay(500); //Espera meio segundo
}
Por favor, note que na função loop () {} inserimos o nome da nossa função (sem o prefixo void). Isto é intitulado
chamada de função. Faça o upload do programa e verifique se o LED da porta 13 está a pisca.
Graças a esta função, se pretendermos alterar algum aspeto no código em questão, só o precisamos de fazer uma vez e
num só sítio.
Vamos agora descobrir novos segredos das funções. Lembra-se de uma das funções mais comuns que utilizamos na
programação com Arduíno? É, provavelmente, a mudança de estado num determinado pino, por exemplo:
digitalWrite(13, HIGH);
Em contraste com a nossa função piscarLED (); , existem dados dentro dos parênteses. Podem ser um número de pino,
um estado ou duty cycle do sinal PWM. Essas informações são usadas posteriormente na função, na execução de
determinadas operações.
Agora vamos escrever a nossa própria função, com um argumento. Vamos supor que queremos editar a função piscarLED
de tal forma que, durante a chamada, seja possível alterar a velocidade do piscar. Na declaração da função, devemos a
informação pretendida para o argumento. Fazemo-lo dentro dos parênteses da função:
//Versão original
void piscarLED(){
//Nova versão
void piscarLED(int tempo){
Vamos agora inserir um último elemento. Não é nada surpreendente. Simplesmente colocámos um número entre
parênteses, que vai indicar o tempo que o LED vai estar ligado e desligado. Vejamos o código:
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
}
void loop() {
piscarLED(50);
}
void piscarLED(int tempo){
digitalWrite(13, HIGH); //Ligar o LED
delay(tempo); //Esperar determinado tempo
digitalWrite(13, LOW); //Desligar LED
delay(tempo); //Esperar determinado tempo
}
Verifique como consegue alterar a frequência do piscar, ao alterar o valor entre parênteses.
Exemplo nº2
Até agora, o LED piscava constantemente, porque fizemos a chamada da função na função loop. Se transferimos a
chamada da função para a função setup, veremos apenas um flash após o início do programa.
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
piscarLED(50);
}
void loop() {
É hora de transformar a função de tal forma que, além de alterar o tempo que o LED pisca, também é possível influenciar
o número de piscas. Para isso, temos de alterar a declaração da função:
//Versão anterior
void piscarLED(int tempo){
//Nova versão
void piscarLED(int tempo, int quantidade){
Adicionamos o argumento “quantidade”, que será responsável pelo número de piscas. Como pode verificar, foi inserido
após a vírgula, precedido, obviamente, pelo seu tipo (int). Este parâmetro pode ser de um tipo diferente, não há qualquer
problema. O importante é que a declaração seja apropriada.
Esperamos que já saiba qual loop usar para controlar a quantidade de piscas. Se não, aconselhamo-lo a visitar o nosso
artigo #8 que aborda o assunto. No entanto, vamos presumir que já domina este conteúdo e apresentar a nova função
piscarLED:
void loop() {
}
Tudo deverá funcionar na perfeição. Agora, após o início do programa, o LED deve piscar exatamente 5 vezes. Mas como
é que o Arduíno sabe distinguir, das informações contidas entre parênteses, qual o tempo e qual a quantidade de piscas?
Bem, é muito simples – valores subsequentes separados por vírgulas, são atribuídos aos argumentos seguintes descritos na
declaração da função. No caso acima, o primeiro número será sempre tratado como tempo e o segundo como a quantidade
de vezes que o LED pisca.
O pino ao qual o LED está ligado é um número no código. Costumamos usá-lo em dois sítios: na declaração do pino
como saída/entrada e na definição do estado high/low. Portanto, esse número pode ser um outro argumento da nossa
função!
Material necessário:
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
piscarLED(100, 5, 13);
piscarLED(150, 4, 8);
}
void loop() {
}
Funciona? Nem por isso, certo? Apenas o LED do pino 13 pisca. O LED do pino 8 não quer dar sinal de vida. Mas,
porquê? Bem, cometemos um erro muito simples, que é fácil de fazer ao escrever este tipo de funções. Não indicamos em
lugar nenhum que, além do pino 13, podem existir outras saídas. Verifique a função setup:
void setup() {
pinMode(13, OUTPUT); //Configuração do pino 13 como saída
piscarLED(100, 5, 13);
piscarLED(150, 4, 8);
}
Podemos corrigir este problema facilmente. Basta mover a configuração do pino para dentro da nossa função, como pode
ver abaixo:
void setup() {
piscarLED(100, 5, 13);
piscarLED(150, 4, 8);
}
void loop() {
}
Agora tudo deve funcionar como pretendido. No entanto, devemos admitir que esta não é uma solução propriamente
elegante. Portanto, não aconselhamos a utilização deste tipo de solução em projetos profissionais. Como se trata de um
projeto simples, não há qualquer problema, até nos facilita a vida.
Até aqui, apenas usamos funções que realizavam algumas operações, sem qualquer retorno de resultado. Agora vamos
introduzir uma nova função que nos envia um valor. O objetivo da função será calcular a área de um quadrado, através do
valor de um dos lados, e enviar o resultado. Para começar, é necessário declarar a função:
int area(int a) {
Como pode ver, existe a expressão int antes do nome da função. Isto significa que o resultado enviado será um número
(área do quadrado).
A função deverá ficar desta forma:
int area(int a) {
int resultado = 0;
resultado = a * a;
return resultado;
}
Deve estar a perguntar-se para onde é enviado o resultado. Vamos verificar o exemplo seguinte:
void setup() {
Serial.begin(9600);
}
void loop() {
int resultado = area(4);
Serial.println(resultado); //Enviar resultado para monitor série
delay(500);
}
int area(int a) {
int resultado = 0;
resultado = a * a;
return resultado;
}
À semelhança do que está no exemplo acima, podemos estabelecer o envio do resultado para o monitor série de uma outra
forma:
//Versão anterior
int resultado = area(4);
Serial.println(resultado); //Enviar resultado para monitor série
//Nova versão
Serial.println(area(4)); //Enviar resultado para monitor série
A cereja no topo do bolo será enviar o valor do lado do quadrado para o Arduíno via computador. Se já não se lembra
como se faz, reveja o artigo do curso sobre comunicação via UART. O programa ficará assim:
void loop() {
if(Serial.available() > 0) { //Se o Arduino receber dados
dadosRecebidos = Serial.readStringUntil('\n'); //Ler dados e salvá-los na variável
int resultado = area(dadosRecebidos.toInt());
Serial.println(resultado); //Enviar resultado para monitor série
}
}
int area(int a) {
int resultado = 0;
resultado = a * a;
return resultado;
}
Como se deve lembrar, durante a comunicação UART, todos os caracteres são enviados na forma de códigos ASCII.
Assim, não podemos simplesmente enviar números para o Arduíno. Terá de haver um conversor que transforme o número
enviado como texto, num número tipo int. É exatamente isto que a linha anterior faz.
Mas vamos voltar ao Arduino e ao sensor de distância. Para começar, vamos concentrar-nos nos seus quatro pinos. Dois
deles são usados para alimentação (Vcc e GND) e os outros dois (trigger e echo) para realizar medições.
O Trigger é o pino que desencadeia a ação. Quando lhe atribuímos um estado high (por pelo menos 10 microssegundos),
a medição da distância começa. No entanto, é a partir do pino Echo que vamos obter a distância medida. O alcance
máximo deste sistema é 4 metros.
Material necessário:
A descrição dos pinos do sensor está indicada no próprio diagrama. No entanto, para que não haja dúvidas, criamos uma
tabela com a ordem dos pinos e com as conexões com o Arduíno:
Como podemos verificar na tabela, o trigger está conectado ao pino 12 e o echo ao pino 11 do Arduino. Isso deve estar
bem claro no código. Portanto, sugerimos que defina os pinos:
#define trigPin 12
#define echoPin 11
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Definição do pino 12 como saída
pinMode(echoPin, INPUT); //Definição do pino 11 como entrada
}
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
//Medição feita
}
Observe a nova função delayMicroseconds (). Esta é equivalente à função delay (). A diferença é óbvia: a nova função
conta o tempo em microssegundos. A função mais comum, conta o tempo em milissegundos.
A sequência que inicia a medição é muito simples. No início, definimos o estado do pino ligado ao trigger como low.
Colocamos um atraso de dois microssegundos, que são suficientes, e definimos o estado high durante 10 microssegundos.
O sensor realiza a medição e envia os resultados através do pino echo.
A questão é como é que vamos ler esse valor? Existe UART ou outra interface de comunicação para isso? Não,
felizmente este sensor é muito simples e a distância medida é representada pelo pulso (estado high) no pino echo. O seu
tamanho é proporcional à distância, ou seja, quanto maior for o pulso, maior é a distância medida.
Felizmente, no Arduino existe uma função muito simples que consegue medir a duração do pulso em qualquer entrada. A
sua duração deve ser compreendida entre 10 microssegundos e 3 minutos. O início da medição ocorre quando é detetada
uma mudança de estado no pino.
int resultado = 0;
resultado = pulseIn(11, HIGH);
A função apenas aceita dois argumentos muito simples: o número do pino a ser verificado e o nível lógico (high/low) a
ser medido.
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Definição do pino 12 como saída
pinMode(echoPin, INPUT); //Definição do pino 11 como entrada
}
void loop() {
long tempo;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Ao executar este programa, vão aparecer números no monitor série. Quanto mais perto de um obstáculo o sensor estiver,
menor será o valor que aparece. No entanto, são impercetíveis para nós. Para que a medição seja legível, o resultado deve
ser dividido por um “número mágico”.
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Definição do pino 12 como saída
pinMode(echoPin, INPUT); //Definição do pino 11 como entrada
}
void loop() {
long tempo, distancia;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Serial.print(distancia);
Serial.println(" cm");
delay(500);
}
Agora, o resultado deve estar apresentado em centímetros. É claro que o número (58) pelo qual dividimos o valor não é
“mágico”. Resulta do tempo que o som viaja numa distância de 1cm e da distância padrão indicada pelo fabricante.
int medicaoDistancia() {
long tempo, distancia;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
return distancia;
}
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Definição do pino 12 como saída
pinMode(echoPin, INPUT); //Definição do pino 11 como entrada
}
void loop() {
Serial.print(medicaoDistancia());
Serial.println(" cm");
delay(500);
}
int medicaoDistancia() {
long tempo, distancia;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
return distancia;
}
Este elemento possui duas entradas: VCC e GND. Quando ligamos as duas, o buzzer emite um som alto, não muito
agradável.
Material necessário:
Agora vamos escrever código cujo objetivo será verificar se um objeto se encontra a uma certa distância do sensor. Se se
verificar, o buzzer irá emitir som.
Para tal, utilizamos o código anteriormente utilizado para medir a distância e acrescentamos a seguinte função:
Como argumento da função, indicamos dois valores inteiros. De seguida, verificamos se a distância medida é maior do
que o valor inicial da nossa faixa (a) e menor que o valor máximo (b).
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Definição do pino 12 como saída
pinMode(echoPin, INPUT); //Definição do pino 11 como entrada
pinMode(3, OUTPUT); //Definição do pino 3, ligado ao buzzer, como saída
}
void loop() {
alcance(10, 25); //Ligar o alarme se houver um objeto a uma distância de 10 a 25cm do sensor
delay(100);
}
int medicaoDistancia() {
long tempo, distancia;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
return distancia;
}
Verifique como é que o programa funciona na prática. Na nossa opinião, este é um ótimo primeiro passo na criação de um
sistema de alarme.
https://www.electrofun.pt/blog/curso-arduino-3-uart-e-variaveis/