Leitura de Botões e o Bounce
Botões são utilizados na criação de interfaces entre maquinas e humanos. Nesse tutorial veremos como fazer com que o Arduino reconheça o estado de um botão e entenderemos o que é o Bounce.
Esse tutorial requer conhecimento em assuntos explicados em posts anteriores. Caso tenha dificuldades em acompanhar os assuntos abordados aqui, confira alguns de nossos tutoriais anteriores:
[toc]
REALIZANDO A LEITURA DE UMA ENTRADA DIGITAL ACIONADA POR UM BOTÃO
Para que o Arduino consiga saber o estado na qual o botão se encontra devemos fazer com que cada um de seus estados defina um nível lógico diferente na entrada na qual ele esta conectado. Para isso podemos utilizar o pull up interno do Arduino como mostra o esquema da figura a seguir.
No esquema da Figura acima, quando o botão estiver pressionado o pino escolhido estará com 0V e quando não estiver pressionado estará com 5V.
A Figura abaixo mostra o esquema de ligação para os exemplos apresentados a seguir. O resistor de 1k é utilizado para proteger o pino 8 de um possível curto para a terra.
Exemplo 1: Utilizando o botão para controlar um led.
void setup() { pinMode(13, OUTPUT); // Configura o pino 13 (led interno) como saída; pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno; } void loop() { if (digitalRead(8) == LOW) { // Botão Pressionado; digitalWrite(13, HIGH); // Liga led. } else { // Botão Não Pressionado digitalWrite(13, LOW); // Desliga led. } }
Apesar do esquema acima funcionar perfeitamente um fenômeno indesejado está acontecendo. Vamos analisá-lo:
O QUE É O BOUNCE?
Ao pressionar um botão nós fazemos com que os seus contatos se choquem o que fará com que o botão trepide um pouco antes de se estabilizar. Esse fenômeno é similar ao que acontece com uma bolinha de ping pong quando jogada contra ao chão, que quica algumas vezes antes de se estabilizar. A figura abaixo mostra a forma de onda do sinal produzido pelo botão ao ser pressionado.
Podemos observar que o botão varia entre nível alto e baixo várias vezes antes de se estabilizar em 0V, ou seja, o led do exemplo 1 pisca várias vezes cada vez que o botão é pressionado, porém isso acontece tão rápido que nosso olho não consegue perceber.
Exemplo 2: Detectando o Bounce.
O código abaixo implementa um contador, que conta o número de vezes que o botão mudou de estado. Essa quantidade é impressa na porta serial a cada segundo. Ao apertar e soltar o botão esperamos que esse contador incremente de duas unidades.
bool last; // Guarda o último estado do botão; uint32_t print_timer; // Timer para a impressão na porta serial; uint8_t counter = 0; // Conta o número de mudança de estados no botão; void setup() { Serial.begin(9600); pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno; last = digitalRead(8); } void loop() { bool now = digitalRead(8); // Lê o estado atual do botão; if (now != last) { // Checa se houve uma mudança de estado; ++counter; last = now; // Atualiza o ultimo estado; } if (millis() - print_timer > 1000) { // Imprime a quantidade de mudanças a cada segundo; Serial.println(counter); print_timer = millis(); } }
Ao pressionarmos o botão com força uma única vez e soltarmos veremos que o contador será incrementado mais que duas vezes como mostra a Figura 4. Isso ocorre devido a trepidação do botão.
Exemplo 3: Tratamento simples para o Bounce.
Podemos tratar a trepidação de um botão utilizando um pequeno delay assim que uma mudança por detectada. Se ao final desse delay a mudança ainda persiste então realmente houve uma alteração no estado do botão. O tempo na qual um botão trepida dependerá de vários fatores e pode ser diferente até mesmo em botões similares. Experimente diferentes valores de delay.
bool last; // Guarda o último estado do botão uint32_t print_timer; uint8_t counter = 0; void setup() { Serial.begin(9600); pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno; last = digitalRead(8); } void loop() { bool now = digitalRead(8); // Lê o estado atual do botão; if (now != last) { // Checa se houve uma mudança de estado; delay(10); // Espera até que a trepidação pare; if (now == digitalRead(8)) { // Checa se a mudança ainda persiste; ++counter; last = now; // Atualiza o ultimo estado; } } if (millis() - print_timer > 1000) { Serial.println(counter); // Imprime um ponto para indicar a mudança; print_timer = millis(); } }
Por utilizar um delay essa abordagem faz com que o Arduino fique bloqueado/parado enquanto o botão trepida o que não é interessante.
Exemplo 4: Tratando Bounce sem bloquear o Arduino.
Criaremos uma variável para guardar o tempo em millisegundos. Sempre que uma mudança for detectada no botão essa variável receberá o tempo do millis atual. Caso a diferença entre o millis atual e essa variável seja maior que o tempo de bounce o botão parou de trepidar e então podemos checar se a mudança realmente aconteceu.
bool stable; // Guarda o último estado estável do botão; bool unstable; // Guarda o último estado instável do botão; uint32_t bounce_timer; uint8_t counter = 0; bool changed() { bool now = digitalRead(8); // Lê o estado atual do botão; if (unstable != now) { // Checa se houve mudança; bounce_timer = millis(); // Atualiza timer; unstable = now; // Atualiza estado instável; } else if (millis() - bounce_timer > 10) { // Checa o tempo de trepidação acabou; if (stable != now) { // Checa se a mudança ainda persiste; stable = now; // Atualiza estado estável; return 1; } } return 0; } void setup() { Serial.begin(9600); // Configura comunicação serial a uma taxa de 9600 bauds. pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno; stable = digitalRead(8); } void loop() { if (changed()) { ++counter; Serial.println(counter); } // Outras tarefas; }
Biblioteca auxiliar
A biblioteca Bounce2 possui uma implementação eficiente e de fácil uso para o tratamento de bounce. Confira!
#include <Bounce2.h> Bounce debouncer = Bounce(); void setup() { pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno; debouncer.attach(8); // Informa que o tratamento de debouce será feito no pino 8; debouncer.interval(10); // Seta o intervalo de trepidação; } void loop() { debouncer.update(); // Executa o algorítimo de tratamento; int value = debouncer.read(); // Lê o valor tratado do botão; if (value == HIGH) { Serial.println("Botao Nao Pressionado!"); } else { Serial.println("Botao Pressionado!"); } }
FINALIZANDO
Este conteúdo foi totalmente voltado para esclarecer alguns pontos à respeito da leitura de botões. Esperamos que você tenha gostado deste conteúdo, sinta-se à vontade para nos dar sugestões, críticas ou elogios. Lembre-se de deixar suas dúvidas nos comentários abaixo.
Olá! Alan! Se eu usar a biblioteca para ESP32 preciso saber os pinos que podem usar atach certo? E na função atach quando o botão for pressionado e ainda for mantido pressionado(pressionado = High e não pressionado = Low) e for preciso tem um tempo de 60 segundos tipo temporizador não tera problemas se eu fizer desta forma quando usar atach mesmo com um time?
Para utilizar mais de um botão, essa função Bounce2 serve? Como seria?
Olá:
Por acaso um capacitor de 100 nF em paralelo com o botão não surte o mesmo efeito?
Olá. Tenho um esquema com vários leds que ligam/desligam sequencialmente respeitando um intervalo entre cada um. ao final do void loop a sequência se repete.
Tenho um switcher que muda o tipo de sequência do liga/desliga dos leds.
O problema é que por causa do intervalo entre um led e outro fazendo com que a execução do código dentro do loop leve cerca de 10 segundos, este é o tempo que a leitura do estado do swicher demora.
Para fazer com que a detecção da mudança do estado do switcher não leve este tempo, tenho que repetir esta detecção pelo menos após cada liga/desliga de um led.
Gostaria de saber se há como deixar a detecção do estado do switcher paralelo a execução do código principal a fim de que ele possa ser detecado a qualquer tempo e não apenas no final da iteração do void loop.
nao conhecia pullup! muito obrigado!
millis() = fantástico!
Boa tarde
Se eu quiser usar vários botões tenho que mudar alguma coisa na declaração ?
“pinMode(8, INPUT_PULLUP); // Configura pino 8 como entrada e habilita pull up interno;
debouncer.attach(8); // Informa que o tratamento de debouce será feito no pino 8;”
Grato
Parabéns pela organização do espaço e pelo conteúdo de qualidade.
Obrigado pelo post e a explicação clara!
Boa, preciso de uma ajuda, o programa esta funcionando, mas esta contando no acionamento e na soltura do botão, já fiz muitos testes e não consigo identificar a falha, poderiam me ajudar?
Mas é esse mesmo o objetivo do programa, ele checa a mudança de estado; então realmente deve contar 2 quando você pressiona e solta o botão.
Muito bom, vai me salvar no meu projeto
Por que na função if devemos utilizar dois ==?
Obrigado
porque meu amigo, = é diferente de ==, = significa atribuição, ou seja, é usado para atribuir uma variável a um valor e == significa comparação, ou seja, você compara se um valor é igual ao outro.