Experimento 36 – Arduino com sensor de distância

Material necessário:

  • Módulo sensor de distância ultrassônico HC-SR04
  • 5 LEDs
  • 5 resistores (150 ou 220 ohms dependendo da cor do LED)
  • 1 Arduino Nano
  • 1 Alto-falante de 8 ohms

Neste experimento usaremos um sensor de distância ultrassônico para reagir à distância de objetos colocados diante do sensor. O HC-SR04 consegue detectar distância de um centímetro a alguns metros. O objetivo será fazer LEDs diferentes acenderem e soar um som diferente no alto-falante à medida em que a distância mudar. O esquema está abaixo:

Esta é uma possível montagem com o protoboard.

Antes de acionar LEDs ou alto-falantes, vamos testar o HC-SR04 imprimindo os valores lidos no monitor serial. O HC-SR04 tem 4 terminais. Dois são usados para fornecimento de energia (VCC e GND), e devem ser ligados em 5V e GND. Os outros dois são a entrada TRIGGER, usado para solicitar que o sensor faça uma medição, e a saída ECHO, que contém a resposta da medição. O sensor é controlado através do envio de pulsos, que devem ter uma duração especificada, e a resposta é recebida também como um pulso, cuja duração varia conforme a distância medida. O pulso é uma onda quadrada, com estado LOW em 0 e HIGH em 5V.

Para solicitar uma medição, o Arduino precisa enviar um pulso (HIGH) de 10 microssegundos para a entrada TRIGGER do sensor. Quando o sensor recebe esse pulso, ele devolve um pulso (também HIGH) de duração variável na saída ECHO. O programa precisa então medir a duração deste pulso, e multiplicar por um valor fixo para obter a distância em centímetros.

O gráfico abaixo ilustra graficamente o pulso que precisamos enviar para a entrada TRIGGER que irá disparar uma medição:

Este outro gráfico mostra um pulso de resposta recebido em ECHO. A duração deste pulso é o tempo em que ele permanece na posição HIGH.

A linguagem do Arduino possui uma função especial para medir a largura de um pulso. A função pulseIn() recebe dois (ou três) argumentos: o primeiro é o pino onde o pulso será recebido e o segundo é o nível do pulso  (HIGH ou LOW). Por exemplo, a instrução abaixo interrompe o código até que um pulso de valor HIGH seja recebido no pino 8. Quando ele terminar (ou seja, quando o sinal voltar para LOW), a duração será gravada na variável duracao:

long duracao = pulseIn(8, HIGH);

Se o pulso nunca chegar, o programa fica esperando para sempre. Uma maneira de evitar isto é estipular um timeout, que é o terceiro argumento. A instrução abaixo espera até 1 minuto (60 segundos) pelo pulso:

long duracao = pulseIn(8, HIGH, 60000);

Para enviar o pulso de disparo, não podemos usar delay() pois a sua resolução mínima é de apenas 1 milissegundo. Neste caso precisamos usar a função delayMicrosseconds(). O pulso de disparo dura 10 microssegundos no estado HIGH, portanto precisamos começar com o estado LOW, esperar um tempo (pode ser também uns 5 a 10 microssegundos) e depois mudar o estado para HIGH, esperar 10 microssegundos, e baixar para LOW:

 digitalWrite(TRIGGER, LOW);
 delayMicroseconds(5);
 digitalWrite(TRIGGER, HIGH);
 delayMicroseconds(10);
 digitalWrite(TRIGGER, LOW);

Por fim, precisamos converter a duração do pulso recebido em ECHO na distância em centímetros. De acordo com a especificação do HC-SR04 é preciso apenas dividir por 58.

O programa abaixo pode ser usado para testar o funcionamento do sensor, imprimindo os dados recebidos no monitor serial. Faça o upload, abra o monitor serial e aproxime uma superfície plana do sensor e veja os valores recebidos na tela.

const int TRIGGER = 3;
const int ECHO = 4;

void setup() {
   pinMode(TRIGGER, OUTPUT);
   pinMode(ECHO, INPUT);
   Serial.begin(9600);
}

void loop() {
   float distance = pulse();
   Serial.println(distance); // imprime a distancia em cm
   delay(500); // must be over 60ms
}

float pulse() {
   digitalWrite(TRIGGER, LOW);
   delayMicroseconds(10);
   digitalWrite(TRIGGER, HIGH);
   delayMicroseconds(10);
   digitalWrite(TRIGGER, LOW);

   long duration = pulseIn(ECHO, HIGH);
   return duration / 58.0;
}

Agora que testamos o sensor, podemos usar esses dados para  controlar dispositivos de saída. No circuito temos LEDs e um alto-falante. O programa abaixo utiliza o arquivo do experimento 32, que define frequências de notas musicais, e seleciona notas de frequências mais altas à medida em que a distância aumenta. O aumento também causa o acendimento de mais LEDs. Aproximar um objeto a uma distância menor que 5 centímetros deve desligar o som e os LEDs:

#include "notas.h"

const int TRIGGER = 3;
const int ECHO = 4;
const int SPEAKER = 5;
const int LED_R = 6,
          LED_Y = 7,
          LED_G = 8,
          LED_B = 9,
          LED_W = 10;

const int notas[] = {
   ZZ, C4, D4, E4, F4, G4
};

void setup() {
   pinMode(TRIGGER, OUTPUT);
   pinMode(SPEAKER, OUTPUT);
   pinMode(LED_R, OUTPUT);
   pinMode(LED_Y, OUTPUT);
   pinMode(LED_G, OUTPUT);
   pinMode(LED_B, OUTPUT);
   pinMode(LED_W, OUTPUT);
   pinMode(ECHO, INPUT);
}

void luz(boolean acender, int led) {
   if (acender) {
      digitalWrite(led, HIGH);
   } else {
      digitalWrite(led, LOW);
   }
}

void loop() {
   float distance = pulse();

   // Som: idx = indice do array notas[] (0 a 5)
   int idx = (int)(distance / 5);
   if(idx > 5) { // se idx resultar em numero > 5, faça idx = 5
      idx = 5;
   }
   // soar um tom na nota correspondente por 500ms
   tone(SPEAKER, notas[ idx ], 500);

   // Luz(condição para acender, led)
   luz( distance >= 5, LED_R);
   luz( distance >= 10, LED_Y);
   luz( distance >= 15, LED_G);
   luz( distance >= 20, LED_B);
   luz( distance >= 25, LED_W);

   delay(200);
}

float pulse() {
   // envia o pulso para TRIGGER, solicitando a medição
   digitalWrite(TRIGGER, LOW);
   delayMicroseconds(10);
   digitalWrite(TRIGGER, HIGH);
   delayMicroseconds(10);
   digitalWrite(TRIGGER, LOW);

   // espera até 50 segundos por um pulso de resposta
   long duration = pulseIn(ECHO, HIGH, 50000);
   // retorna a distância em cm (calculada)
   return duration / 58.0;
}

O sensor é muito sensível e interferências são frequentes. Neste exemplo o próprio som do alto-falante pode interferir no sinal. Há várias maneiras de contorná-las e algumas usando software. Em vez de construir você mesmo os pulsos como fizemos neste exemplo, você pode usar uma biblioteca criada especificamente para facilitar o uso do sensor. Uma boa bibliioteca como a NewPing não apenas oferece funções como também lida com diferentes sensores de outros fabricantes e busca otimizar a medição, reduzindo interferências e melhorando a precisão.

 

Alteração 35.1 – Termômetro com display de 7-segmentos

Este experimento é uma mistura do experimento anterior, que demonstra o uso de um display de 7 segmentos com Arduino, e do experimento 29, que implementa um termômetro com o LM35DZ.

Material adicional:

  • Circuito integrado LM35DZ (termômetro digital analógico).

O esquema consiste apenas em acrescentar o LM35, que será conectado a uma entrada analógica. Mudamos para a entrada A3, já que o display está usando as primeiras três entradas analógicas como saídas digitais:

Uma possível implementação com o protoboard está ilustrada abaixo:

Para simplificar o programa e facilitar o reuso, transferimos o mapeamento dos dígitos e segmentos para uma biblioteca local (chamamos de seven_segment_display.h) contendo o código abaixo:

static int digit[10][7] = {
    {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,LOW},
    {LOW, HIGH,HIGH,LOW, LOW, LOW, LOW},
    {HIGH,HIGH,LOW, HIGH,HIGH,LOW, HIGH},
    {HIGH,HIGH,HIGH,HIGH,LOW, LOW, HIGH},
    {LOW, HIGH,HIGH,LOW, LOW, HIGH,HIGH},
    {HIGH,LOW, HIGH,HIGH,LOW, HIGH,HIGH},
    {HIGH,LOW, HIGH,HIGH,HIGH,HIGH,HIGH}, 
    {HIGH,HIGH,HIGH,LOW, LOW, LOW, LOW}, 
    {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}, 
    {HIGH,HIGH,HIGH,HIGH,LOW, HIGH,HIGH}
};

const int DISPLAY_LEDS = 7;
const int DISPLAY_DIGITS = 10;

void showNumber(int* disp, int dig) {
    for(int i = 0; i < DISPLAY_LEDS; i++) {
        digitalWrite(disp[i], digit[dig][i]);
    }
}

Para criar o arquivo, clique na seta que fica abaixo do ícone do monitor serial(canto superior direito do IDE do Arduino) e selecione “New Tab”, depois escolha o nome (seven_segment_display.h) e grave.

Para usar é preciso apenas incluir o arquivo no código-fonte principal usando #include, e chamar a função showNumber() passando como argumento o display e o dígito a exibir. No programa abaixo criamos uma função exibir que recebe o número e mostra nos dois displays. Chamamos a função exibir assim que lemos a temperatura a cada 2 segundos:

#include "seven_segment_display.h"

#define TERMOMETRO A3

int unidades[] = {14, 15, 2, 3, 4, 5, 6};
int dezenas[] = {7, 8, 9, 10, 11, 12, 16};
 
void setup() {
    for(int i = 0; i < DISPLAY_LEDS; i++) {
        pinMode(dezenas[i], OUTPUT);
        pinMode(unidades[i], OUTPUT);
    }
    // Usando a referência interna de 1,1V para maior precisão
    analogReference(INTERNAL);
}

void exibir(int numero) {
    showNumber(unidades, numero % 10);
    showNumber(dezenas, numero / 10);
}

void loop() {
    int leitura = analogRead(TERMOMETRO);
    float volts = (leitura / 1024.0) * 1.1;
    float celsius = (volts) * 100.0;
 
    int temperatura = (int) round(celsius); // arredonda
    exibir(temperatura);
 
    delay(2000);
}

Este programa possui uma diferença em relação ao utilizado no experimento 29. Em vez de usar a referência padrão de 5V para leituras analógicas (via analogRead), usamos a referência interna calibrada do Arduino através do comando

analogReference(INTERNAL);

Isto significa que o valor 0 a 1024 não corresponde mais a 0 a 5V, mas a 0 a 1,1V. Isto permite maior precisão na leitura. Assim, para obter a tensão entre o terminal central e GND, dividimos o valor lido por 1024 e depois multiplicamos por 1,1V. Cada grau Celsius corresponde a 0,01 V de diferença, então multiplicamos por 100 para obter a temperatura.

Experimento 35 (extra) – Usando displays de 7 segmentos

Material:

  • 2 displays de 7 segmentos de catodo comum (HS 5101A). Pode-se usar displays de anodo comum também (HS5101B), neste caso é preciso inverter a lógica dos pinos no Arduino e ligar o anodo comum no positivo (5V).
  • 14 resistores de 220 ohms
  • 1 Arduino Nano
  • Protoboard, fios e jumpers

Podemos exibir as informações numéricas obtidas por sensores usando um display LCD ou de LEDs. Existem vários tipos. Entre os mais populares em projetos Arduino estão displays OLED (exibem desenhos e cores) displays Nokia 5110, displays LCD com 2 ou 4 linhas e 16 colunas, e displays de LED com 7 segmentos (foto).

Para os displays mais simples, como o de 7 segmentos, a conexão ao Arduino é geralmente feita em paralelo, individualmente para cada LED e requer muitos fios (usa quase todos os pinos digitais). A solução é usar um circuito intermediário (multiplexador, registrador de deslocamento, etc.) para codificar os dados de forma serial, diminuindo o uso dos pinos.

Neste experimento vamos simplesmente conectar dois displays de LED de sete segmentos ao Arduino e fazê-los exibir números. Para isto, acessaremos cada LED do display individualmente e usaremos 14 pinos. O Arduino possui 14 pinos digitais, mas três deles devem ser evitados:

  • O pino 0, porque é também usado para receber dados (RX). Ele pode ser usado, mas para que o upload seja possível ele precisa ser desligado (apenas durante o upload). Este pino também possui um LED ligado diretamente a ele, que irá roubar parte da corrente que alimenta o LED do display.
  • O pino 1, porque é usado para transmitir dados (TX). Ele pode ser usado, mas se houver transmissão de dados (por exemplo, uso do monitor Serial) ele também irá roubar corrente do display (fazendo o LED acender mais fraco ou piscar durante a transmissão). Ele também tem um LED na placa que acende durante a transmissão.
  • O pino 13, porque ele tem um LED na placa diretamente ligado a ele que precisará compartilhar corrente com o LED do display.

Uma vez programado o Arduino, e desligado do computador, os pinos 0 e 1 podem ser usados normalmente, mas para simplificar este projeto, iremos usar outros pinos.

O Arduino Nano possui 8 pinos analógicos (A0 a A7), 6 dos quais podem ser usados como pinos digitais (A0 a A5) referenciados pelos números 14 a 19. Portanto, neste projeto, em vez de usar os pinos 0 e 1, usaremos 14 e 15, respectivamente, e trocaremos o pino 13 pelo pino 16.

Os pinos a-g do display correspondem ao anodo de cada LED, que é ligado ao positivo (ligamos aos pinos de saída do Arduino, que acenderão cada LED com um nível lógico HIGH). O último pino do display é o ponto decimal. Os catodos de todos os LEDs estão interligados e podem ser ligados ao negativo através do pino central em qualquer um dos lados do display. Experimente acender LEDs individuais ligando uma mini bateria CR2032 (3V) entre o catodo comum e outro terminal do display.

Neste experimento não usaremos o LED do ponto decimal. O esquema das conexões está ilustrado abaixo, usando resistores de 220 ohms para ligar cada LED.

O display usado (HS-5101) consome no máximo 80mA (isto varia bastante e depende do tamanho do display – displays gigantes consomem vários amperes), mas consideramos os valores nominais de cada LED para calcular os resistores (a corrente recomendada é de 12mA, e máxima de 20mA). Assim, para displays de LEDs vermelhos ou amarelos (HS5101AS ou AY), a tensão direta em cada LED é de 2,0V e valor seria 250 ohms. Para displays de LEDs verdes ou azuis (HS5101AG ou AB) a tensão é 3,0V e usaríamos 170 ohms. Usamos um valor médio (220 ohms) que garante uma corrente abaixo da máxima e próxima da recomendada independente do modelo usado.

Pode-se calcular apenas um resistor para o catodo comum, mas isto irá fazer causar alterações de brilho entre dígitos diferentes (1, que usa apenas 2 LEDs e 8 que usa 2 LEDs, por exemplo). Isto foi feito no experimento 23.

Uma possível implementação com o protoboard está ilustrada abaixo. A maior complexidade deste circuito são a grande quantidade de fios. Verifique as conexões com cuidado, e se preferir, posicione os displays mais afastados. Use o esquema para verificar as ligações.

Teste das conexões

Para testar cada LED digite o programa abaixo no IDE do Arduino e faça upload. Ele configura 14 pinos como OUTPUT e depois faz com que cada LED acenda em sequência.

void setup() {
   // Define cada pino (2 a 16, exceto 13 )como saida
   for(int i = 2; i < 17; i++) {
      if(i != 13) {
          pinMode(i, OUTPUT);
      }
   }
   teste();  // roda o teste abaixo uma vez
}

// testa os leds acendendo-os e depois apagando-os
void teste() {
   for(int i = 2; i < 17; i++) {
       if(i != 13) {
           digitalWrite(i, HIGH);
           delay(500);
       }
   }
   for(int i = 2; i < 17; i++) {
       if(i != 13) {
           digitalWrite(i, LOW);
           delay(500);
       }
   }
}

void loop() {}

O próximo passo é configurar os LEDs de maneira a desenhar números.

Contando até 99

Para desenhar os números precisamos acender determinados LEDs ao mesmo tempo. O Arduino precisa fornecer nível HIGH para cada LED que deve acender. A tabela abaixo relaciona o estado (HIGH ou LOW) nos pinos a-g para desenhar cada dígito de 0-9:

dígito a b c d e f g
0 HIGH HIGH HIGH HIGH HIGH HIGH LOW
1 LOW HIGH HIGH LOW LOW LOW LOW
2 HIGH HIGH LOW HIGH HIGH LOW HIGH
3 HIGH HIGH HIGH HIGH LOW LOW HIGH
4 LOW HIGH HIGH LOW LOW HIGH HIGH
5 HIGH LOW HIGH HIGH LOW HIGH HIGH
6 HIGH LOW HIGH HIGH HIGH HIGH HIGH
7 HIGH HIGH HIGH LOW LOW LOW LOW
8 HIGH HIGH HIGH HIGH HIGH HIGH HIGH
9 HIGH HIGH HIGH HIGH LOW HIGH HIGH

O programa abaixo declara uma matriz 10 x 7 (vetor bidimensional) contendo cada um dos 10 dígitos associados a uma lista de segmentos contendo seu estado (HIGH ou LOW). Através dela poderemos mapear pinos do Arduino a cada LED individualmente. Contém também duas listas de 7 elementos (vetores) contendo os valores dos pinos Arduino usados em cada display.

int unidades[] = {14, 15, 2, 3, 4, 5, 6};
int dezenas[] = {7, 8, 9, 10, 11, 12, 16};

static int digito[10][7] = {
   {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,LOW},
   {LOW,HIGH,HIGH,LOW,LOW,LOW,LOW},
   {HIGH,HIGH,LOW,HIGH,HIGH,LOW,HIGH},
   {HIGH,HIGH,HIGH,HIGH,LOW,LOW,HIGH},
   {LOW,HIGH,HIGH,LOW,LOW,HIGH,HIGH},
   {HIGH,LOW,HIGH,HIGH,LOW,HIGH,HIGH},
   {HIGH,LOW,HIGH,HIGH,HIGH,HIGH,HIGH}, 
   {HIGH,HIGH,HIGH,LOW,LOW,LOW,LOW}, 
   {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}, 
   {HIGH,HIGH,HIGH,HIGH,LOW,HIGH,HIGH}
};

const int DISPLAY_LEDS = 7;
const int DISPLAY_DIGITS = 10;
 
void setup() {
    for(int i = 0; i < DISPLAY_LEDS; i++) {
       pinMode(dezenas[i], OUTPUT);
       pinMode(unidades[i], OUTPUT);
    }
}

void acende(int* disp, int dig) {
    for(int i = 0; i < DISPLAY_LEDS; i++) {
       digitalWrite(disp[i], digito[dig][i]);
    }
}

void loop() {
    for(int j = 0; j < DISPLAY_DIGITS; j++) {
       acende(dezenas, j);
       for(int i = 0; i < DISPLAY_DIGITS; i++) {
          acende(unidades, i);
          delay(500);
       } 
    }
}

O setup() configura cada pino de cada display como OUTPUT, para que o Arduino possa fornecer saída de 0 a 5V.

A função acende() recebe como argumentos um display (array unidades ou dezenas) e um dígito e passa por cada LED gravando HIGH ou LOW, dependendo da linha da matriz 10×7 correspondente ao dígito a desenhar. O loop() chama acende para o display dezenas e unidades, contando de 00 a 99.

Para reiniciar a contagem, aperte o botão reset do Arduino.

Experimento 34 – Controle de um servo-motor

Servo-motores respondem a um comando e giram até no máximo um ângulo especificado (geralmente 180 graus, mas alguns servos podem ser construídos para ângulos maiores ou menores). São otimizados para torque e são ideais para programar movimentos de braços e alavancas, como em robôs. O controle é feito via PWM. Este experimento mostra como controlar os movimentos básicos de um servo-motor usando uma biblioteca do Arduino.

Material necessário

  • Um servo-motor 9g ou similar
  • Um potenciômetro (10 ou 50k)
  • Um Arduino

Para usar um servo-motor podemos usar a biblioteca Servo.h, fornecida pela distribuição oficial do Arduino. Monte o circuito abaixo:

Ligue o potenciômetro como divisor de tensão com o pino central em A0 (ou outro pino com entrada analógica) e os outros dois respectivamente no positivo e no negativo (como ilustrado). Conecte os fios vermelho e preto (do servo) respectivamente aos polos positivo e negativo do circuito, e o fio restante (laranja ou amarelo) em D3 (ou outro pino com suporte PWM).

Uma possível implementação no protoboard:

Digite o programa abaixo (ou baixe o código-fonte no GitHub):

#include <Servo.h>

#define SERVO     3
#define DIRECAO   A0

Servo servo;

void setup() {
    servo.attach(SERVO);
    servo.write(90);
}

void loop() {
    int pot = analogRead(DIRECAO); // 0 a 1023
    int posicao = map(pot, 0, 1023, 0, 180);
    servo.write(posicao);
    delay(100);
}

Ligue o Arduino e faça upload do programa. O Arduino irá mover o servo-motor de um lado para o outro de acordo com a posição do giro do potenciômetro.

A instrução map() realiza um mapeamento de valores (no exemplo acima a escala do potenciômetro 0 a 1023 – é mapeado à escala do servo 0 a 180). A instrução servo.write(angulo) gira o servo no angulo especificado (90 é o centro, valores < 90 gira para a esquerda, > 90 gira para a direita).

Este circuito de demonstração está sendo alimentado pelo Arduino, que está obtendo sua energia da entrada USB, que estabelece um limite máximo à corrente fornecida (geralmente 200mA, no máximo 500mA). Em projetos reais, o motor deve ser alimentado com uma fonte externa.

Caso seja necessário controlar vários servos, pode-se usar um ou mais registradores de deslocamento (shift-registers).

Baixe o livro

Todo o conteúdo dos posts deste site, mais as referências e guias da apostila do curso Introdução à Eletrônica para Artistas foi disponibilizado para download em um e-book em PDF.

 

Arduino 12: Usando LEDs endereçáveis

Neste último experimento iremos programar o Arduino para que ele execute uma sequência luminosa em um LED endereçável WS2812. Baixaremos as bibliotecas e os programas para executar as sequências da Internet através da IDE do Arduino.

Experimento 33 – Usando LEDs RGB endereçáveis

LEDs WS2812 são pixels RGB frequentemente usados em painéis digitais coloridos de alta-definição. Eles também são muito populares em produtos de wearables (eletrônica para vestir), usados em roupas, jóias e calçados. Geralmente eles são distribuídos em conjuntos contendo vários LEDs, organizados em matrizes quadradas, sequências circulares, fitas e painéis flexíveis.

A foto abaixo mostra um circuito usando dois conjuntos de LEDs WS2812 (1 + 16) piscando em uma sequência programada no Arduino. No kit foi incluído um LED WS2812.

Material necessário

  • Arduino Nano + cabo USB + computador
  • LED RGB endereçável WS2812
  • Resistor de 470 ohms
  • Capacitor de 100uF
  • Protoboard, fios e jumpers

Monte o circuito abaixo. É necessário soldar terminais no LED WS2812 distribuído no kit. Veja como fazer isto no tutorial de soldagem no final da apostila. Você também pode adquirir LEDs WS2812 montados em placas com vários LEDs e adaptados para uso em projetos de eletrônica para vestir.

Existem várias bibliotecas para usar LEDs endereçáveis. As mais populares são as bibliotecas NeoPixel e FastLED. Todas têm diversos programas de exemplo que você pode usar imediatamente, fazendo poucas alterações. Vamos instalar uma delas e rodar seus exemplos.

Selecione o menu Sketch/Include Library/Manage Libraries no IDE do Arduino. Você verá a janela abaixo. No campo de pesquisa digite FastLED, e a janela filtrará a biblioteca FastLED que usaremos para programar os LEDs:

Clique no botão Install, que aparece do lado direito. Quando a instalação terminar, você poderá abrir os exemplos que usam a biblioteca selecionando o menu File/Examples. No final há uma seção “Examples from Custom Libraries” e você encontrará FastLED.

Abra o exemplo Blink. Verifique que a variável NUM_LEDS contém o valor 1 (um LED), e altere DATA_PIN para 4 (que é o pino onde conectamos o LED). Depois faça upload para o Arduino. O LED deverá piscar na cor vermelha. Você pode mudar a cor alterando o código em loop(), por exemplo, troque por:

leds[0] = CRGB::Blue;

para piscar na cor azul.

Abra o exemplo ColorPalette. Este programa realiza uma série de animações passando por várias cores. Como temos apenas um LED, mude NUM_LEDS para 1, e LED_PIN para 4. Faça upload para o Arduino e observe as variações de cor e pulsos que o LED vai fazer.

É muito mais interessante usar esse programa com vários LEDs WS2812. Se você adquirir um outro LED endereçável, ou melhor ainda, uma placa com diversos LEDs WS2812 montados, você pode conectá-los em sequência ligando o pino Dout de uma placa à entrada Din da seguinte. Você então deve alterar os programas que fazem o sequenciamento de LEDs e informar a quantidade total de LEDs que serão controlados.

Por exemplo, no circuito abaixo adicionamos um anel de 16 LEDs em série com o WS2812 que já estava ligado ao circuito, resultando no total de 17 LEDs endereçáveis. Altere a variável NUM_LEDS novamente (para 17) e faça upload para ver os LEDs piscarem e mudarem de cor sequencialmente.

O circuito acima só deve ser usado em Arduino estabelecendo um limite de brilho para os LEDs (controlado pelo programa), já que o uso de 17 LEDs em brilho máximo exige muita corrente da saída 5V do Arduino Nano chinês (limite máximo de 500mA via USB e 800mA via fonte externa conectada a Vin). O LED com brilho máximo consome 60mA, portanto 17 LEDs com brilho máximo podem ultrapassar 1 ampere. O ColorPalette usa apenas ¼ do brilho (64) máximo. Há um fusível nos reguladores de tensão que desliga temporariamente o fornecimento de energia se ela ultrapassar esses limites.

O ideal é alimentar circuitos que tenham 7 ou mais LEDs WS2812 usando uma fonte externa. Nessa configuração, o Arduino fornece apenas o sinal de controle, através de seu pino de saída. A fonte externa pode até ser a mesma bateria, se ela fornecer até 5V.

O esquema abaixo mostra como construir o mesmo circuito acima de maneira mais segura, usando uma fonte externa. Com essa configuração, como a corrente não passará por dentro do Arduino, podemos acender os LEDs na intensidade máxima e alimentar muito mais LEDs:

No esquema acima o Arduino pode estar sendo alimentado por USB ou por outra fonte a partir da sua entrada Vin. Mas é possível também usar a mesma fonte que fornece energia para os pixels WS2812, se ela tiver capacidade de fornecimento de corrente suficiente. Por exemplo, 3 pilhas AAA de 1,5V cada poderiam alimentar o Arduino e os LEDs.

Com 4,5V não seria possível alimentar o Arduino pelo pino Vin, já que o limite mínimo é de 6V. A alimentação do Arduino teria que ser feita diretamente via pino 5V. É preciso tomar cuidado ao utilizar esse pino como entrada, já que ele está ligado diretamente ao microcontrolador (sem fusível), e poderá queimar o Arduino se a tensão passar de 5,5V.

Arduino 11: Música com bibliotecas

Neste experimento usaremos uma mini-biblioteca (arquivo .h) de notas musicais para fazer o Arduino executar uma música.

Experimento 32 – Usando bibliotecas para produzir notas musicais

Material necessário

  • Arduino Nano + cabo USB + computador com conexão à internet
  • Alto-falante
  • Protoboard, fios e jumpers

O comando tone() gera pulsos de onda quadrada na frequência passada como parâmetro. Por exemplo, pra produzir um lá central usado para afinação, podemos mandar para um pino PWM que esteja ligado a um alto-falante um comando:

tone(440);

Para tocar uma música precisamos saber a frequência de cada nota, mas o ideal seria poder simplesmente chamar as notas pelo nome.

Existe um arquivo .h para isto. Ele atribui nomes de variáveis para cada uma das 88 notas do piano, guardando a sua frequência. É um programa de domínio público que faz parte dos exemplos da IDE do Arduino. Este é um trecho:

...
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
...

Ou seja, usando-o podemos mandar comandos:

tone(NOTE_A4);

e fazer soar um lá sem precisar lembrar da sua frequência.

NOTE_C3 corresponde ao terceiro Dó do piano. NOTE_A4 ao quarto lá, e assim por diante. O comando noTone(PINO) desliga o sinal no pino (aplica valor LOW, 0V).

Criaremos o arquivo .h localmente. Abra um sketch novo, e em seguida abra um tab (como mostrado acima no exemplo sobre arquivos .h), e cole nele conteúdo do arquivo abaixo:

https://www.arduino.cc/en/Tutorial/ToneMelody?action=sourceblock&num=2

Grave o arquivo como notas.h.

Vamos alterar o nome das variáveis para que fiquem mais curtos e seja mais fácil digitar notas. Com o tab notas.h aberto, digite Control-F (Command-F no Mac) ou selecione o item de menu Edit/Find. Abrirá a janela abaixo.

No campo “Find:” digite NOTE_ e clique no botão Replace All. Isto removerá o prefixo NOTE_ de todas as notas. O resultado deve ficar da forma abaixo:

#define B0 31
#define C1 33
#define CS1 35
#define D1 37
#define DS1 39
#define E1 41
...

Agora acrescente mais uma definição, em uma linha antes de B0:

#define ZZ 0

Esta será a nota de pausa (frequência zero) que não produzirá som algum.

Digite o programa abaixo. Ele foi adaptado dos exemplos do Arduino (“toneMelody” criado por Tom Igoe). Ele usa essa biblioteca de notas musicais para tocar uma pequena música. Nesta versão tocamos uma música diferente. As notas da música estão dentro do bloco melodia[], e os tempos correspondentes de cada nota em durações[]:

#include "notas.h"

int ALTO_FALANTE = 5; // coloque alto-falante ou buzzer entre pino 5 e GND
int NUM_NOTAS = 44;   // número de notas incluídas em melodia[]

int melodia[] = {
    G4, G4, G4, DS4, AS4, G4, DS4, AS4, G4,
    D5, D5, D5, DS5, AS4, FS4, DS4, AS5, G4,
    G5, G4, G4, G5, FS5, F5, E5, DS5, E5, ZZ,
    GS4, CS5, C5, B5, AS5, A5, AS5, ZZ,
    DS4, FS4, DS4, AS4, G4, DS4, AS5, G4};

// duração: 1 = semibreve, 2 = mínima, 4 = seminima, 8 = colcheia, etc.:
int duracoes[] = {
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 4, 4, 6, 8, 4, 6, 8, 2,
    4, 6, 8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 16, 16, 8, 8,
    8, 4, 6, 8, 4, 6, 8, 2
};

void setup() {
    for (int nota = 0; nota < NUM_NOTAS; nota++) {
        int duracao = 1000 / duracoes[nota]; // divide 1000 por cada duração
        tone(ALTO_FALANTE, melodia[nota], duracao);
        int pausa = duracao * 1.30;
        delay(pausa);
        noTone(ALTO_FALANTE);
    }
}
void loop() {}

O programa roda apenas uma vez no bloco setup(). Se quiser que ela toque sem parar, você pode transferir todo o código para o bloco loop(), incluindo um delay(1000) no final para que haja um intervalo entre as execuções.

O programa usa um bloco de repetição for() para executar cada nota (será explicado mais adiante.)

Construa o circuito abaixo (que talvez seja o circuito mais simples criado no curso), ligando um dos fios do alto-falante no pino 5, e o outro terminal em GND.

Logo após a transferência, a música começará a tocar. Experimente criar outras músicas usando essa biblioteca.

O código-fonte deste experimento (o sketch e arquivo notas.h já alterado) também está disponível para download em repositório GitHub (veja link na introdução da apostila).

Listas

O código do programa anterior possui várias estruturas que ainda não vimos. Uma delas é a lista (também chamado de array, ou vetor). Há duas listas no código do experimento anterior, representadas pelas variáveis durações[] e melodia[]. Listas são declaradas com seus itens entre chaves, separados por vírgula. O tipo de dados se refere ao tipo de cada elemento da lista:

float precos = {23.56, 9.99, 11.99, 25.49};

Cada item da lista é referenciado por um numero que é seu índice. A contagem começa em zero, portanto, na lista acima, os índices são 0, 1, 2 e 3. Eles são usados para referenciar itens da lista. Por exemplo, precos[1] é 9.99 e precos[3] é 25.49.

As duas listas do programa do experimento anterior têm 8 elementos, e seus índices variam de 0 a 7.

Repetição com for

O bloco for é uma estrutura de repetição. Consiste de uma declaração entre parênteses e um conteúdo (a ser repetido) entre chaves:

for( declaração ) { /* conteúdo a ser repetido */ }

A declaração tem três partes (separadas por ponto e vírgula):

  • O valor inicial de uma variável
  • A condição envolvendo a variável que deve ser verdadeira enquanto o bloco é repetido
  • Uma instrução para mudar o valor da a variável

Blocos de repetição for() são frequentemente usados com listas. Para imprimir no monitor serial cada um dos itens da lista precos (de 4 elementos) acima, pode-se usar o seguinte for():

for(int i = 0; i < 4; i = i + 1) {
    Serial.println( precos[i] );
}

A cada passada no for(), o valor de i será diferente. Iniciará em zero, e será 1, depois 2, depois 3. Na próxima passada i é 4, a condição se tornará falsa, e o bloco será encerrado.

No código do experimento anterior, o for() incrementa o índice (variável nota) usando nota++. Isto é o mesmo que fazer:

nota = nota + 1;

O for() executa apenas uma vez a primeira parte da declaração (inicialização da variável que controla a repetição), mas a cada repetição a condição da segunda parte é testada, o bloco entre chaves é executado, e por fim a expressão da terceira parte é executada, nesta ordem.

É importante que o valor da variável mude de forma que faça o for() terminar em algum momento (a menos que você queira um loop infinito – mas para isto você já tem a função loop() que é mais simples). Normalmente as expressões usadas na terceira parte do for() são apenas para incrementar (somar um) ou decrementar (subtrair um) da variável de controle.

Arduino 10: funções, listas, bibliotecas

Com o que vimos de programação do Arduino até aqui, que foi basicamente como usar suas entradas analógicas e digitais, já podemos construir todos os circuitos que vimos nas seções anteriores usando transistores e circuitos integrados 555, de forma muito mais simples e sem precisar calcular circuitos RC (resistor-capacitor), e nem mesmo medir tensões e correntes. Não será sempre assim. O conhecimento de eletrônica básica ainda será importante para fazer projetos mais interessantes, porém o Arduino de fato simplifica o uso da eletrônica.

Esta é uma seção opcional. Aqui exploramos alguns tópicos mais avançados de programação com o Arduino que permitirão que você use programas escritos por outras pessoas e faça alterações neles sem necessariamente entender tudo o que fazem. Se você nunca programou antes os conceitos podem parecer complexos. Mas não é preciso entender tudo para fazer os experimentos. Monte os circuitos (que são muito simples) e copie os códigos. Você pode deixar para ler a teoria depois, quando já tiver mais familiaridade com programas em Arduino.

Declarando funções

Os comandos digitalWrite, analogRead, etc. que chamamos dentro dos blocos setup() e loop() são chamadas de funções. Elas foram definidas nas bibliotecas do Arduino. Essas bibliotecas são automaticamente incluídas em todos os programas.

Uma função, portanto, para que possa ser chamada, precisa ser definida em algum lugar. A chamada poderá ser feita dentro do bloco loop() ou setup(). A definição de novas funções poderá ser feita no próprio sketch, ao lado dos blocos loop() e setup() que são definições de funções (chamadas automaticamente pelo Arduino).

Portanto, para definir uma função, escolha qualquer lugar antes ou depois dos blocos loop() e setup(), e crie um novo bloco com a estrutura abaixo:

void nomeDaFuncao() {
    // coloque aqui as instruções que sua função irá executar quando chamada
}

Vejamos um exemplo. Abaixo definimos uma função chamada piscar():

void piscar() {
    digitalWrite(8, HIGH);
    delay(500);
    digitalWrite(8, LOW);
    delay(500);
}

Ela tem o mesmo código dentro de loop() do circuito que pisca o LED. Agora podemos chamar a função que acabamos de definir como se fosse um comando (terminando em ponto-e-vírgula):

void loop() {
    piscar();   // chama a função piscar()
}

O código acima funciona igual ao código original que pisca o LED. A vantagem de definir funções é que podemos chamá-las várias vezes, sem precisar repetir o código que ela contém. Isto é mais fácil de perceber se definirmos funções com parâmetros.

Quando definimos uma função, os parâmetros são declarados como variáveis. Como toda variável, o cada parâmetro tem um tipo de dados que faz parte da declaração. Essas variáveis declaradas irão receber valores quando a função for chamada, e podem usar esses valores dentro da definição da função. No exemplo abaixo definimos uma função piscar() contendo dois parâmetros inteiros:

void piscar(int pino, int tempo) {
    digitalWrite(pino, HIGH);
    delay(tempo);
    digitalWrite(pino, LOW);
    delay(tempo);
}

Observe que as variáveis são usadas pelos comandos que estão dentro da função. Para chamar a função acima precisamos passar para ela dois parâmetros inteiros. O valor do primeiro parâmetro será copiado (atribuído) à variável pino, e o valor do segundo será copiado à variável tempo. Por exemplo, veja a chamada abaixo dentro de loop():

void loop() {
    piscar(8, 500);
}

Isto irá copiar o valor 8 para pino, e o valor 500 para tempo. E dentro da função, esses valores serão novamente copiados para funções do Arduino (digitalWrite e delay). Qual a vantagem disso? Agora podemos chamar a mesma função várias vezes, alterando os parâmetros que passamos para ela. Por exemplo, podemos fazer um loop() que pisca LEDs em pinos diferentes e em tempos diferentes sem precisar escrever 12 linhas de código:

void loop() {
    piscar(8, 500);
    piscar(9, 250);
    piscar(8, 1000);
}

Experimento 31 (extra) – Definindo funções para controlar um LED RGB

Material necessário:

  • Arduino Nano + cabo USB + computador
  • LED RGB de anodo comum (ou três LEDs: um vermelho, um verde e um azul)
  • Resistor de 220 ohms
  • Protoboard, fios e jumpers

O programa abaixo define uma função cor(r,g,b) que permite declarar cores usando comandos RGB que são traduzidos em cores de um LED RGB cujos terminais estão conectados a saídas PWM do Arduino. O circuito pode ser usado com 3 LEDs ou um LED RGB. Como o Led RGB usado tem o anodo comum, ele foi ligado em 5V e analogWrite(pino, 0) provocará o brilho máximo, já que a diferença de potencial nesse valor é máxima. Para compensar isto sem modificar o circuito podemos subtrair 255 do valor de cada componente de cor. Isto é feito dentro da função cor();

#define LED_VERMELHO 9
#define LED_VERDE 10
#define LED_AZUL 11

void setup() {}

void cor(int r, int g, int b) {
    analogWrite(LED_VERMELHO, 255 - r);
    analogWrite(LED_VERDE, 255 - g);
    analogWrite(LED_AZUL, 255 - b);
}

void acender(int r, int g, int b, int intervalo) {
    cor(r, g, b);
    delay(intervalo);
}

void loop() {
    acender(255,0,0,1000);   // vermelho
    acender(0,255,0,1000);    // verde
    acender(0,0,255,1000);    // azul
    acender(190,255,0,1000);  // amarelo
    acender(190,0,255,1000);  // magenta

    acender(0,255,255,1000);  // ciano
    acender(255,255,255,1000); // branco
    acender(0,0,0,1000);      // apagado

    acender(63,0,0,1000); // vermelho com 1/4 da intensidade
    acender(0,63,0,1000); // verde com 1/4 da intensidade
    acender(0,0,63,1000); // azul com 1/4 da intensidade
}

Além da função cor(), o programa acima também define uma função acender(r,g,b,duracao), que estabelece a cor (chamando a função cor()) e o tempo em que ela ficará acesa. O loop() chama várias vezes a função acender() com diferentes parâmetros, para que o circuito exiba uma sequência de cores diferentes, mantendo cada uma acesa por um segundo.

Bibliotecas e arquivos .h (arquivos de cabeçalho)

Suponha que você pretenda usar LEDs RGB em vários circuitos diferentes e queira reusar as funções criadas no experimento anterior. Uma maneira de fazer isto é recortar e colar o texto no outro programa. Mas há alternativas melhores. Uma delas é armazená-las em um arquivo separado que possa ser incluído em outros programas: um arquivo de cabeçalho, que tem a extensão .h

Para criar um arquivo de cabeçalho no IDE do Arduino, clique no menu que aparece logo abaixo do ícone do monitor serial, e depois selecione “New Tab”:

Depois escolha um nome para o arquivo. Por exemplo, “ledsrgb.h”, e grave-o.

Agora vamos transferir as funções cor() e acender() para o arquivo ledsrgb.h. Recorte-as do sketch, e cole as duas funções abaixo no arquivo .h:

void cor(int r, int g, int b) {
    analogWrite(LED_VERMELHO, 255 - r);
    analogWrite(LED_VERDE, 255 - g);
    analogWrite(LED_AZUL, 255 - b);
}

void acender(int r, int g, int b, int intervalo) {
    cor(r, g, b);
    delay(intervalo);
}

Como a função cor() depende de variáveis que ainda estão no arquivo original, precisamos redefini-las localmente (no arquivo .h) e depois copiar os valores. Acrescente o seguinte código:

int pino_R; // foram declaradas sem valor inicial
int pino_G;
int pino_B;

void configurarRGB(int pr, int pg, int pb) {
    pino_R = pr; // o valor inicial passado quando esta função for chamada
    pino_G = pg;
    pino_B = pb;
}

E altere a função cor() para que ela use os novos valores:

void cor(int r, int g, int b) {
    analogWrite(pino_R, 255 - r);
    analogWrite(pino_G, 255 - g);
    analogWrite(pino_B, 255 - b);
}

A função configurarRGB() será chamada a partir do sketch original, para passar os valores dos pinos ao arquivo .h. Para incluir o arquivo .h no seu sketch, use uma expressão #include:

#include "piscaled.h"

(observe que ela não tem ponto e vírgula no final – é uma diretiva como #define).

Dentro do setup() do seu sketch, chame a função configurarRGB() passando as variáveis que contém os números dos pinos. Desta forma, os valores serão copiados para as variáveis do arquivo .h:

void setup() {
    configurarRGB(LED_VERMELHO, LED_VERDE, LED_AZUL);
}

Agora é possível rodar o programa alterado, que consiste de dois arquivos, e ele funcionará igual, mesmo não contendo a definição de acender(). Se você quiser usar piscaled.h em outro sketch, precisa apenas copiá-lo para a pasta onde está o arquivo .ino do sketch, declarar o #include e chamar o configurarRGB(). Depois disso você pode chamar as funções cor() e acender() como se elas tivessem sido definidas localmente.

Se você achar mais fácil, pode baixar o projeto contendo os dois arquivos usados neste exemplo a partir do repositório GitHub disponível na Internet. Veja o link na introdução da apostila.

Arduino 9: Usando entradas e saídas analógicas

Variando a tensão média aplicada no motor podemos fazê-lo acelerar ou desacelerar. Já fizemos isto usando o circuito integrado 555. Com Arduino o circuito é bem mais simples.

Experimento 30 – Acelerando e desacelerando o motor com luz

Material necessário:

  • Arduino Nano + cabo USB + computador
  • Motor de 3V
  • 1 diodo de propósito geral (1N4148 ou equivalente)
  • 2 resistores de 10k ohms
  • LDR
  • 1 transistor MOSFET IRL540
  • Protoboard, fios e jumpers

Monte o circuito abaixo:

O circuito requer uma entrada analógica que irá selecionar a velocidade desejada para o motor, e uma saída, também analógica (PWM), para variar a intensidade da tensão aplicada no motor e assim fazê-lo girar mais rápido ou mais devagar.

A leitura dos dados de entrada para controlar a velocidade do motor poderia ser feita variando um potenciômetro, mas é bem mais interessante usar um sensor de intensidade luminosa como o LDR. O circuito pode ser usado para construir um carrinho que anda quando o ambiente está iluminado, e que para quando entra em um ambiente escuro.

Como um motor pode demandar muito mais corrente que o pino do Arduino é capaz de fornecer, precisamos isolá-lo. Uma forma de fazer isto é através de um transistor. Usaremos neste exemplo um transistor MOSFET de potência, que consome pouca energia mas suporta bem mais corrente direta que o motor será capaz de exigir. Motores também causam pulsos reversos de corrente quando o motor liga ou desliga. Protegeremos o circuito desses pulsos com um diodo em paralelo com o motor.

Este MOSFET que usamos só irá conduzir (entre os seus terminais D e S) quando a tensão no terminal de controle (terminal G) tiver mais de 4,5V. Como usamos PWM, o pino 3 irá gerar pulsos de onda quadrada em valores absolutos de 0 ou 5V (os valores intermediários são simulados através da largura dos pulsos, ou seja, através de PWM). Assim o MOSFET irá ligar e desligar muito rapidamente. Quando o tempo ligado aumentar, o motor irá acelerar. Quando o tempo desligado aumentar, o motor irá desacelerar.

O programa é simples. Consiste na leitura do valor do LDR (0 a 1023) dividido por quatro para que possa ser enviado para a saída PWM (0 a 255):

#define PINO_MOTOR 3;
#define PINO_LDR A0;

void setup() {
    pinMode(PINO_MOTOR, OUTPUT);
}

void loop() {
    int velocidade = analogRead(PINO_LDR) / 4;
    analogWrite(PINO_MOTOR, velocidade);
}

Alteração 30.1 – Usando uma fonte externa para alimentar o motor

Este experimento funciona adequadamente com o motor distribuído no kit, que consome muito pouca corrente, mas se você usar um outro motor ele poderá exigir demais do Arduino. Normalmente um motor não deve ser alimentado com a fonte interna de 5V do Arduino. Ele deve usar uma fonte externa (a fonte pode até ser a mesma usada pelo Arduino, se o motor suportar). Isto não impede que o Arduino continue a controlá-lo. O circuito abaixo ilustra esta configuração (o Arduino pode ser alimentado via USB ou pelo pino Vin):