September 2017 – Eletrônica para artistas
Entradas e saídas do Arduino
Slides com um resumo ilustrando o funcionamento das instruções digitalWrite/digitalRead e analogWrite/analogRead do Arduino.
Experimento 36 – Arduino com sensor de distância
5 resistores (150 ou 220 ohms dependendo da cor do LED)
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:
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 ECHO = 4;
float distance = pulse();
Serial.println(distance); // imprime a distancia em cm
delay(500); // must be over 60ms
float pulse() {
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 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 luz(boolean acender, int led) {
if (acender) {
// 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
// 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);
// envia o pulso para TRIGGER, solicitando a medição
// espera até 50 segundos por um pulso de resposta
long duration = pulseIn(ECHO, HIGH, 50000);
// retorna a distância em cm (calculada)
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.
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] = {
{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,LOW, HIGH,HIGH}
void showNumber(int* disp, int dig) {
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
// Usando a referência interna de 1,1V para maior precisão
void exibir(int numero) {
showNumber(unidades, numero % 10);
showNumber(dezenas, numero / 10);
int leitura = analogRead(TERMOMETRO);
float volts = (leitura / 1024.0) * 1.1;
float celsius = (volts) * 100.0;
int temperatura = (int) round(celsius); // arredonda
exibir(temperatura);
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
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.