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:
Índice
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.
Estudante de Engenharia Elétrica da UFES e Fundador do Vida de Silício. Sonhador com uma única pretensão, fazer a diferença.


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.