Sensores analógicos no Arduino: uma experiência inicial, incluindo funções
Um sensor que custa alguns centavos pode detectar se está escuro, e a partir daí podemos comandar de forma coordenada o funcionamento de 2 leds, fazendo uso de funções em C e de um timer sem delay.
Como primeira experiência prática com sensores analógicos, montei um circuito com um sensor de iluminação.
Para dar um pouco mais de funcionalidade, fiz a montagem com 2 leds independentes, e programei o Arduino para funcionar assim: se o sensor detecta que escureceu (por exemplo, ao apagar a luz do quarto), ele acende 2 leds, e os mantém acesos por um timer.
Na hora de apagar, entretanto, ele não os apaga juntos: apaga primeiro um, aí espera um tempo antes de apagar o outro, para dar tempo de você apertar um botão se quiser que ambos voltem a ficar acesos por mais um ciclo do timer.
Descrevendo o circuito
O circuito é simples, envolvendo 2 leds (L1 e L2) e seus resistores associados (R1 e R2 – e 330Ω é um valor genérico bem seguro para cada um deles), um botão (S1 – chave de 2 terminais 'normalmente aberta'), o sensor (L3 – um simples e barato fotoresistor ou resistor fotossensível), e um resistor comum de 10KΩ (R3) associado a ele, mais o Arduino, uma breadboard, e os jumpers que forem necessários para as conexões (eu usei 6, mas poderia ter usado bem menos).
O esquema simplificado acima mostra os detalhes lógicos da montagem, inclusive em qual pino do Arduino cada componente é montado. Os únicos componentes para os quais você precisa prestar atenção à polaridade são os leds, cuja perna maior precisa ficar conectada ao pino, e a outra perna ao Terra. Os demais componentes podem ser montados de qualquer lado.
A inteligência básica do circuito é dada pelo fotoresistor L3. Note que um lado dele está conectado ao pino de 5V e o outro está conectado simultaneamente ao pino analógico A3 do Arduino e a um resistor de 10KΩ ligado ao terra para fazer o papel de pull-down.
O que o fotoresistor faz é reduzir a sua resistência conforme aumenta a luminosidade que incide sobre ele. Como ele está conectado ao pino A3 e recebendo continuamente a corrente que sai do pino 5V do Arduino, isso significa que quando ele estiver bem iluminado, a resistência dele vai baixar, e mais corrente chegará ao pino A3. Inversamente, quanto mais escuro o ambiente, mais resistência, e menos corrente chegará ao pino A3.
O restante do circuito são conexões bem simples: 2 leds conectados, de um lado, a pinos digitais do Arduino e, do outro, a resistores de 330Ω conectados ao terra, e um botão conectado a um pino digital do Arduino e ao terra.
Descrevendo o programa
O programa para o Arduino precisa tomar várias decisões a partir das 2 fontes de entrada possíveis no circuito acima: o fotoresistor e o botão.
Como o fotoresistor está ligado a uma porta analógica do Arduino, sabemos que a corrente que ele deixar passar será convertida pelo Arduino em um número inteiro na faixa de 0 a 1023. Para facilitar a minha vida, vou usar a função map
para sempre converter esse inteiro em um número entre 1 e 10, diminuindo a precisão e passando a ter uma "nota" de 1 a 10 para a luminosidade detectada. A sintaxe dessa conversão é assim: valorConvertido=map(valorOriginal,0,1023,1,10);
.
A outra entrada possível é o botão, que está conectado a um pino digital e assim sabemos que ele vai gerar um valor HIGH ou LOW quando pressionado, dependendo do que eu conectar à sua outra ponta. Como eu o conectei a outra ponta ao terra, o valor que ele vai gerar no pino ao ser pressionado é um LOW.
Para garantir que o pino de entrada estará sempre HIGH exceto quando o botão for pressionado, eu não vou inicializá-lo da forma tradicional (que seria pinMode(9, INPUT);
), e sim garantindo o uso do pull-up interno do Arduino, com o comando pinMode(chave, INPUT_PULLUP);
. E se você ainda não teve curiosidade de ir pesquisar o que é um resistor pull-up, agora seria um bom momento de fazê-lo ;-)
Com todas as informações acima, já dá de imaginar a lógica básica do programa, que é: se o botão for pressionado ou a luz do ambiente apagar (ou seja, se o valor lido do fotoresistor passar a estar abaixo de certo limite), os leds devem acender e o timer para apagá-los deve iniciar.
Se o timer iniciar, a contagem deve ocorrer considerando os limites diferentes para ambos os leds (porque um apaga antes do outro). Como ela não pode impedir a reação ao eventual pressionamento de um botão, o timer não poderá ser um simples delay()
, mas sim ocorrer em paralelo ao funcionamento do loop()
do Arduino.
Na animação acima você vê eu usar uma lanterna para trapacear o nível de iluminação do ambiente. No momento em que eu desligo, os 2 leds acendem e, em seguida, apagam de forma escalonada.
O restante do programa são as burocracias: declarar variáveis e constantes, acender e apagar os leds, etc. – mas, para aumentar a clareza às custas do aumento da contagem dos elementos do programa, movi para 2 funções o "filé mignon" do programa: acender os leds (iniciando o timer) e controlar o timer.
Criando o programa para o Arduino
Antes de explicar, vejamos uma versão colorizada do código completo do programa que descrevi acima:
/* Sensor-acende-led: acende 2 leds quando fica escuro, e os apaga separadamente
(c) Augusto Campos 28.11.2014 - BR-Arduino.org
Usage of the works is permitted provided that this instrument is retained
with the works, so that any entity that uses the works is notified of
this instrument. DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. */
const int limite_claridade=7; // sensor igual ou abaixo deste valor indica escuro
const int iluminador1=4; // pino do primeiro led
const int iluminador2=5; // pino do segundo led
const int chave=9; // pino onde esta a chave/botao
const int lim_timer1=10000; // ciclos para apagar o primeiro led
const int lim_timer2=20000; // ciclos para apagar o segundo led
int contador=0;
int contando=0;
bool estavaClaro=false;
void setup() {
pinMode(iluminador1, OUTPUT);
pinMode(iluminador2, OUTPUT);
pinMode(chave, INPUT_PULLUP);
}
void loop() {
int sensor=analogRead(3);
int luz10=map(sensor,0,1023,1,10);
if (luz10<=limite_claridade && estavaClaro && contando==0) {
acendeIluminadores();
}
if (digitalRead(chave)==LOW) {
acendeIluminadores();
}
if (contando!= 0) controlaContador();
estavaClaro=(luz10>limite_claridade);
}
void acendeIluminadores() {
digitalWrite(iluminador1,HIGH);
digitalWrite(iluminador2,HIGH);
contador=0;
contando=1;
}
void controlaContador() {
contador++;
if (contador==lim_timer1) digitalWrite(iluminador1,LOW);
if (contador==lim_timer2) {
digitalWrite(iluminador2,LOW);
contando=0;
}
}
Os trechos em cinza são apenas comentários, que podem ser importantes para a compreensão do programa mas não interferem na sua funcionalidade, nem no tamanho do que vai ser transferido para o Arduino após a compilação.
Em marrom temos, primeiro, 6 linhas que começam com a palavra const
. Elas definem constantes, que servirão como parâmetro para o funcionamento do programa. A primeira delas, limite_claridade
, foi obtida após várias tentativas e erros, até que eu verifiquei que no meu ambiente e sensor, o número 7 corresponde ao valor, na escala de 7 a 10, que indica a escuridão diurna quando fecho a cortina. Num teste noturno, eu reduziria este valor. A seguir temos a definição dos pinos digitais em que estão conectados os componentes, e a duração dos timers para cada led (os números mencionados correspondem a poucos segundos, numa aplicação real eles precisariam ser maiores e, possivelmente, de outro tipo numérico1).
Logo abaixo temos 3 variáveis2, também em marrom, mas eu as diferenciei usando itálico. As duas primeiras são para controle do timer, e a última delas (com o nome de estavaClaro
) serve para armazenar um estado anterior do sensor, de modo a ser possível verificar se "já estava escuro" ou se "estava claro e ficou escuro" – só o segundo caso justifica acender os leds.
Aí vem em verde a função setup()
, obrigatória, que inicializa o Arduino. No nosso caso, ela se limita a definir os papeis de cada pino digital. Note o uso do papel INPUT_PULLUP – já comentamos sobre ele, acima.
Em seguida, em vermelho, vem o loop()
, também obrigatório, que é a parte que o Arduino repetirá constantemente. Conjugue a informação a seguir com o que já comentamos na descrição do programa, acima:
- o loop lê o sensor e em seguida o converte (na variável
luz10
) em um número de 1 a 10 indicando a iluminação detectada. - Se
luz10
estiver abaixo do limite de claridade definido nos parâmetros3 e se antes estava claro4 e se já não estivermos com os leds acesos5, a funçãoacendeIluminadores()
será executada. - Da mesma forma, se o estado do pino a que está conectado nosso botão indicar que ele está pressionado, a função
acendeIluminadores()
também será executada. - Se a variável
contando
contiver um valor diferente de zero, significa que estamos com um timer em andamento, e aí chamaremos a funçãocontrolaContador()
para... controlar o contador. - Encerrando o loop, a variável
estavaClaro
recebe um valor verdadeiro caso neste momento a iluminação no ambiente esteja acima do nosso limite de claridade, e falso no caso contrário. Isso serve para que possamos detectar o momento em que a luz se apaga, que é aquele no qual a variável estavaClaro indica que na volta anterior do loop ainda estava claro, mas na volta atual do loop o sensor informa que não está mais – como você pode perceber revendo o item 2 desta lista, acima.
Para fechar, vem o filé mignon, em preto: as duas funções responsáveis por acender os leds (e iniciar o timer) e por controlar o timer.
Veremos primeiro a acendeIluminadores()
. O que ela faz é bem simples: acende os 2 leds, zera o contador
e define a variável contando
com o valor 1 – lembra que acima, no item 4 do loop, a presença de um valor diferente de zero nessa variável era usada para ver se estávamos com um contador em andamento?
Completando, a função controlaContador() é a responsável por fazer andar o nosso timer. Para que o programa se mantenha permanentemente responsivo ao pressionamento do botão, optei por não usar um timer baseado na função delay()
(que interrompe o processamento6), e sim na contagem de vezes que o loop()
é executado (o que não interrompe o processamento).
Note que o funcionamento dela é simples: ela soma 1 ao valor do contador
(que foi zerado quando os leds acenderam, na acendeIluminadores()
), e compara o novo valor do contador aos limites dos timers de cada um dos leds: se tiver sido alcançado o primeiro deles, o led correspondente é simplesmente apagado. Quando chegar ao limite do segundo led, o outro led será apagado e o valor zero será atribuído à variável contando
, indicando que não estamos mais com um timer em andamento.
Um ponto interessante para você observar: o botão tem o poder de acender os 2 leds e zerar o contador até mesmo se os leds já estiverem acesos, e independentemente do estado anterior da variável estavaClaro
.
Outro ponto interessante, que eu mesmo preciso observar: este circuito parece bem adequado para quando eu for fazer algum experimento com PWM (para controlar a intensidade dos leds) e com um sensor de presença. E também com os timers internos do Arduino. E com outras interrupções. Creio que voltarei a ele algumas vezes ;-)
- Ou eu precisaria implementar outro tipo de timer. Chegarei a isto, mas é uma experiência futura. ↩
- As variáveis estão definidas no cabeçalho porque serão usadas em várias funções – são variáveis globais. Não são a melhor prática de programação aplicável aqui, mas as alternativas prejudicariam a explicação a não-programadores, então preferi recorrer a elas e manter mais clareza. ↩
- O limite de claridade é definido na constante limite_claridade, explicada acima. ↩
- O funcionamento da variável
estavaClaro
é explicado adiante, no texto. ↩ - Quando os leds estão acesos, haverá um timer em andamento, e por isso a variável
contando
será diferente de zero, como veremos. ↩ - Uma alternativa seria usar interrupções, mas é cedo para isso, no meu aprendizado. ↩
Comentar