Trilha de aprendizado · Nível 10 · Tutorial 2

Eventos de botão com tratamento de rebote

Ler um botão próprio sem bloquear o programa e produzir um único evento por pressionamento confirmado, mesmo diante do rebote dos contatos.

  • Nível: Intermediário
  • Duração: 16 min
  • 8 passos
Eventos de botão com tratamento de rebote

O que você vai percorrer

  1. Por que um acionamento pode parecer vários Reconheça o rebote como uma oscilação breve dos contatos e entenda por que uma mudança de leitura não deve virar comando imediatamente. 1 min
  2. Separe leitura, memória e estado estável Separe a leitura atual do botão das memórias necessárias para reconhecer uma mudança confiável. 2 min
  3. Confirme a mudança após um intervalo estável 3 min
  4. Transforme mudanças estáveis em eventos Converta cada mudança já confirmada do estado estável do botão em um evento único de pressionamento ou soltura. 2 min
  5. Alterne o LED apenas no pressionamento Use o evento de pressionamento confirmado para inverter o LED integrado apenas uma vez por acionamento. 2 min
  6. Atenda o botão sem parar a outra tarefa Integre o botão com tratamento de rebote a uma segunda tarefa temporizada, sem bloquear o loop. 3 min
  7. Ajuste a janela e reconheça os limites Ajuste o intervalo de estabilidade com base em ensaios e diferencie uma transição perdida de uma transição rejeitada. 2 min
  8. Valide o comportamento completo Valide a aplicação completa: um evento por pressionamento confirmado, sem repetição durante a pressão e com a tarefa periódica ainda ativa. 3 min

O que você vai aprender

  • Implementar a confirmação temporal de uma mudança no botão sem usar delay.
  • Distinguir leitura instantânea, estado estável e evento de pressionamento.
  • Alternar um LED apenas uma vez por acionamento confirmado.
  • Verificar que manter o botão pressionado não impede outra sequência temporizada.

Antes de começar

  • Leitura de botões nas entradas do Uno R3
  • Entradas definidas com pull-up e pull-down
  • Temporização não bloqueante com millis

Passo 1 de 8

Por que um acionamento pode parecer vários

Reconheça o rebote como uma oscilação breve dos contatos e entenda por que uma mudança de leitura não deve virar comando imediatamente.

Um contato mecânico não é instantâneo

A leitura elétrica pode oscilar

Um botão não muda de nível de forma ideal

Na montagem já conhecida, o botão fica entre D2 e GND e o pino usa INPUT_PULLUP. Portanto:

  • solto → leitura HIGH;
  • pressionado → leitura LOW.

Ao apertar uma vez, esperamos uma única mudança de HIGH para LOW. Porém, os contatos mecânicos podem encostar, separar e encostar novamente por um breve instante. O pino então pode fornecer várias leituras alternadas antes de se firmar em LOW.

Esse efeito também pode ocorrer ao soltar o botão, antes de a leitura se firmar em HIGH.

Um comando mecânico, várias leituras possíveis

Diagrama de um botão ligado entre D2 e GND com pull-up, um único pressionamento e uma forma de onda que oscila brevemente antes de permanecer em nível baixo.

Um único pressionamento pode gerar uma sequência curta de níveis HIGH e LOW antes de a leitura se estabilizar.

Da oscilação à confirmação

O que o programa pode enxergar

Rebote não são vários apertos

Imagine que o programa leia, em passagens consecutivas pelo loop(), esta sequência durante um único pressionamento:

HIGH → LOW → HIGH → LOW → LOW → LOW

Os primeiros níveis alternados são o rebote: mudanças elétricas rápidas causadas pelos contatos. Só a parte final, que permanece em LOW, representa uma condição que parece estável.

Se cada mudança para LOW fosse tratada imediatamente como um comando, o mesmo apertar poderia produzir mais de uma ação. A solução será confirmar que uma mudança deixou de oscilar antes de aceitá-la como válida.

Dica

Ideia central

Uma leitura que mudou agora ainda não deve ser interpretada automaticamente como um novo comando. Primeiro, ela precisa mostrar que permaneceu sem novas mudanças por algum tempo.

Verifique sua interpretação

Interprete a sequência

Durante um único pressionamento, a leitura foi HIGH → LOW → HIGH → LOW → LOW. Qual interpretação é correta?

Passo 2 de 8

Separe leitura, memória e estado estável

Separe a leitura atual do botão das memórias necessárias para reconhecer uma mudança confiável.

Leitura atual não é decisão

A leitura é apenas uma amostra

Em cada passagem por loop(), digitalRead(BOTAO_PIN) fornece uma leitura instantânea: a amostra elétrica disponível naquele momento. Com INPUT_PULLUP, HIGH significa botão solto e LOW significa botão pressionado.

Durante o rebote, essa leitura pode mudar várias vezes, embora a intenção mecânica seja uma só. Por isso, a aplicação não deve tratar toda leitura instantânea como um estado já aceito.

Uma amostra pode oscilar

Diagrama com leituras sucessivas de um botão oscilando durante o rebote e trilhas separadas para leitura, memória e estado aceito.

A leitura atual pode variar antes de a aplicação aceitar qualquer mudança.

Dê um papel a cada variável

Quatro informações, quatro responsabilidades

Para tratar o botão, mantenha informações com papéis diferentes. Elas podem coincidir em um instante, mas não significam a mesma coisa:

  • Leitura instantânea: o valor obtido agora com digitalRead().
  • Último nível observado: a memória da amostra anterior, usada para perceber que a leitura mudou.
  • Estado estável: o nível que a aplicação já aceitou como confiável.
  • Marca da última mudança observada: o instante, em unsigned long, registrado quando a leitura observada mudou.

Enquanto o contato oscila, a leitura e o último nível observado podem mudar; o estado estável pode continuar com o valor anterior.

Dica

Memória entre passagens

Use esta separação para evitar uma confusão comum: o nível lido agora descreve o pino; o estado estável descreve o que o restante do programa pode usar com confiança. A marca de tempo pertence à observação das mudanças, não ao LED nem à ação da aplicação.

Papéis no programa

Função de cada informação no tratamento de rebote.

Leitura e memória não devem ser tratadas como sinônimos.
InformaçãoDuraçãoFunção
Leitura instantâneaPassagem atualAmostrar o nível presente no pino
Último nível observadoPersistenteDetectar diferença entre amostras
Estado estávelPersistenteGuardar o nível aceito pela aplicação
Instante da mudançaPersistenteMarcar quando a leitura mudou por último

Inicialize sem criar comando

Comece com uma condição conhecida

No início do programa, leia o botão uma vez e use essa leitura para inicializar as duas memórias de nível. Essa é a condição de partida: não representa um novo pressionamento nem uma nova soltura.

A marca de tempo também precisa começar com um valor persistente. Aqui, millis() registra o instante inicial. A confirmação temporal será acrescentada no próximo passo.

Memórias iniciais do botão

Declarações e inicialização após configurar o pino do botão como INPUT_PULLUP.

cpp
const uint8_t BOTAO_PIN = 2;

int ultimoNivelObservado;
int estadoEstavel;
unsigned long instanteUltimaMudanca;

void setup() {
  pinMode(BOTAO_PIN, INPUT_PULLUP);

  int leituraInicial = digitalRead(BOTAO_PIN);
  ultimoNivelObservado = leituraInicial;
  estadoEstavel = leituraInicial;
  instanteUltimaMudanca = millis();
}

void loop() {
  int leituraAtual = digitalRead(BOTAO_PIN);

  // A confirmação temporal será incluída no próximo passo.
}

Prática de identificação

Confira a separação

Associe cada informação à sua função principal.

Qual é o papel de cada dado?

Faça as associações corretas.

Toque em um item e depois no par correspondente.

Passo 3 de 8

Confirme a mudança após um intervalo estável

Observe e reinicie

Reinicie a janela a cada mudança

Em cada passagem por loop(), compare a leitura instantânea com ultimoNivelObservado antes de avaliar o tempo. Se forem diferentes, uma mudança acabou de ser observada: atualize essa memória e registre agora em instanteUltimaMudanca.

Toda nova mudança reinicia a janela. Portanto, se o sinal oscilar durante o pressionamento ou durante a soltura, ainda não há estabilidade suficiente para aceitar o novo nível em estadoEstavel.

Uma oscilação adia a confirmação

Diagrama temporal de um sinal de botão que oscila entre alto e baixo; cada mudança reinicia a janela de estabilidade, e somente o trecho baixo final é aceito.

A janela recomeça a cada mudança observada, inclusive em oscilações que ocorrem ao soltar o botão.

Confirme sem esperar parado

Atualize a entrada sem bloquear

Junto das declarações persistentes do step anterior, acrescente a constante abaixo. Depois, substitua integralmente o loop() provisório daquele step por este loop(). Assim, haverá apenas uma definição de loop() no sketch.

cpp
const unsigned long INTERVALO_ESTAVEL = 30;

void loop() {
  unsigned long agora = millis();
  int leituraAtual = digitalRead(BOTAO_PIN);

  if (leituraAtual != ultimoNivelObservado) {
    ultimoNivelObservado = leituraAtual;
    instanteUltimaMudanca = agora;
  }

  if ((leituraAtual != estadoEstavel) &&
      (agora - instanteUltimaMudanca >= INTERVALO_ESTAVEL)) {
    estadoEstavel = leituraAtual;
  }

  // Outras tarefas podem ser atendidas nesta passagem.
}

Aceite somente após estabilidade

Um novo nível é aceito somente quando as duas condições são verdadeiras:

  1. leituraAtual é diferente de estadoEstavel; há uma mudança ainda não aceita.
  2. O tempo desde instanteUltimaMudanca alcançou INTERVALO_ESTAVEL; a leitura permaneceu sem mudança durante a janela.

Neste exemplo, 30 ms é um valor inicial de ensaio, não uma duração universal de rebote. Não há delay() nem laço de espera: enquanto o intervalo não termina, a condição temporal é falsa e o loop() continua executando suas demais tarefas.

Dica

A marca registra mudança, não consulta

Não atualize instanteUltimaMudanca em toda passagem pelo loop(). Se a marca fosse renovada continuamente, a diferença agora - instanteUltimaMudanca ficaria sempre pequena e o estado novo jamais seria confirmado.

Pratique as duas condições

Complete a lógica da janela

Complete as condições que reiniciam a janela e que permitem confirmar a leitura. As variáveis mostradas já existem no sketch.

Reinício e confirmação

Preencha as duas condições do tratamento de rebote.

cpp
if () {
  ultimoNivelObservado = leituraAtual;
  instanteUltimaMudanca = agora;
}

if ((leituraAtual != estadoEstavel) &&
    ()) {
  estadoEstavel = leituraAtual;
}

Verifique o instante de aceitação

Localize a confirmação

O estado estável atual é HIGH, e INTERVALO_ESTAVEL vale 30 ms. Considere estas consultas:

  • 10 ms: LOW
  • 18 ms: HIGH
  • 25 ms: LOW
  • 40 ms: LOW
  • 54 ms: LOW
  • 55 ms: LOW

Em qual consulta o programa pode aceitar LOW como novo estado estável?

Linha do tempo das consultas

A última mudança observada ocorre quando a leitura passa de HIGH para LOW em 25 ms.
ConsultaLeituraEfeito na janela
10 msLOWReinicia
18 msHIGHReinicia
25 msLOWReinicia
40 msLOWContinua
54 msLOWAinda não completou 30 ms
55 msLOWCompleta 30 ms

Quando LOW pode ser aceito?

Escolha a primeira consulta que satisfaz tanto o intervalo de estabilidade quanto a diferença em relação ao estado estável.

Passo 4 de 8

Transforme mudanças estáveis em eventos

Converta cada mudança já confirmada do estado estável do botão em um evento único de pressionamento ou soltura.

Estado mantido e pulso único

Estado contínuo não é evento

Um estado estável informa a condição aceita neste momento: enquanto o botão fica pressionado, ele continua em LOW.

Um evento informa que algo acabou de acontecer nesta passagem por loop(). Assim, pressionou deve ser true somente quando o estado estável muda de HIGH para LOW.

Com INPUT_PULLUP:

  • HIGH → LOW: pressionamento confirmado;
  • LOW → HIGH: soltura confirmada.

A permanência em LOW não cria novos pressionamentos. Ela apenas mantém o estado estável como pressionado.

Um nível mantido, um pulso único

Diagrama mostra um nível estável alto que muda para baixo e permanece baixo, enquanto um pulso único aparece apenas no instante da mudança.

O estado permanece em nível baixo; o evento existe apenas na passagem que confirma a transição.

A sequência correta na passagem pelo loop

Ciclo de atualização

No início de toda passagem por loop(), zere os indicadores de evento. Isso impede que um evento confirmado em uma passagem continue verdadeiro na próxima.

A atualização segue esta ordem:

  1. obter a leitura atual;
  2. atualizar ultimoNivelObservado e reiniciar a janela se a leitura mudou;
  3. confirmar um novo estadoEstavel quando a janela terminou;
  4. guardar o estado estável anterior e comparar a direção da transição;
  5. ativar pressionou ou soltou;
  6. disponibilizar esses eventos para a aplicação nesta mesma passagem.

A confirmação temporal é a mesma do passo anterior. Agora, a transição confirmada ganha um significado pontual: pressionar ou soltar.

Qual é a ordem?

Organize as etapas de uma passagem pelo loop() na ordem correta.

  1. Usar o evento nesta mesma passagem
  2. Confirmar um novo estado estável após o intervalo
  3. Ativar o evento de pressionamento ou soltura
  4. Comparar o estado estável anterior com o novo
  5. Ler o botão e reiniciar a janela se a leitura mudou

Código da transição confirmada

Gere os eventos após confirmar

Acrescente este conteúdo ao loop() do step anterior, substituindo o loop() provisório inteiro. Ele reutiliza exatamente BOTAO_PIN, ultimoNivelObservado, estadoEstavel, instanteUltimaMudanca e INTERVALO_ESTAVEL já declarados.

pressionou e soltou são variáveis locais: elas começam como false em cada passagem. Já as memórias do tratamento de rebote continuam persistentes, como antes.

Atualização e eventos

Substitua integralmente o loop() do step anterior por este código. As declarações globais e o setup() permanecem os mesmos.

cpp
void loop() {
  bool pressionou = false;
  bool soltou = false;

  unsigned long agora = millis();
  int leituraAtual = digitalRead(BOTAO_PIN);

  if (leituraAtual != ultimoNivelObservado) {
    ultimoNivelObservado = leituraAtual;
    instanteUltimaMudanca = agora;
  }

  if ((leituraAtual != estadoEstavel) &&
      (agora - instanteUltimaMudanca >= INTERVALO_ESTAVEL)) {
    int estadoEstavelAnterior = estadoEstavel;
    estadoEstavel = leituraAtual;

    if ((estadoEstavelAnterior == HIGH) &&
        (estadoEstavel == LOW)) {
      pressionou = true;
    } else if ((estadoEstavelAnterior == LOW) &&
               (estadoEstavel == HIGH)) {
      soltou = true;
    }
  }

  // Nesta mesma passagem, a aplicação pode consultar
  // pressionou e soltou.
}

Dica

Por que o evento não se repete

estadoEstavelAnterior é criado somente ao confirmar a mudança e recebe o valor antigo antes da atualização. É a comparação entre ele e o novo estadoEstavel que revela a direção.

Se o botão continuar pressionado, leituraAtual e estadoEstavel serão ambos LOW. A condição de confirmação será falsa e nenhum evento será recriado.

Reconheça pressionamento e soltura

Pratique: direção da transição

Complete as condições que reconhecem as duas transições confirmadas. Considere a ligação com INPUT_PULLUP: pressionado corresponde a LOW.

As duas variáveis comparadas representam estados estáveis, não leituras momentâneas.

Pressionamento e soltura

Preencha as condições das transições.

cpp
if () {
  pressionou = true;
} else if () {
  soltou = true;
}

Passo 5 de 8

Alterne o LED apenas no pressionamento

Use o evento de pressionamento confirmado para inverter o LED integrado apenas uma vez por acionamento.

Consuma o evento, não o nível

Três informações, três papéis

O tratamento anterior produz pressionou e soltou somente na passagem atual por loop(). Agora, o comando da saída precisa de outra memória, persistente:

  • estadoEstavel descreve o botão aceito pelo programa.
  • pressionou e soltou descrevem transições confirmadas nesta passagem.
  • ledIntegradoLigado registra o estado que o LED deve manter até surgir outro pressionamento confirmado.

O LED não deve usar o nível do botão como sua memória. Ele muda quando consome o evento pressionou.

Estado não é evento

Diagrama temporal com um nível de botão mantido, um pulso único de evento e um estado de LED persistente.

O evento é momentâneo; o comando do LED permanece até o próximo evento.

Dica

Consuma o evento correto

Com o botão entre D2 e GND usando INPUT_PULLUP, a transição estável de HIGH para LOW gera pressionou. Somente esse evento deve inverter o LED.

A soltura confirmada gera soltou, mas não muda a saída. Ela encerra a condição pressionada e permite que um próximo pressionamento gere outro evento.

Inverta apenas quando pressionou

Ação única sobre o LED integrado

Acrescente a variável global junto das demais declarações persistentes. No setup() já existente, acrescente a configuração e o comando inicial do LED. Por fim, coloque o último bloco dentro do loop() do step anterior, depois do código que pode definir pressionou e soltou.

cpp
// Variável global, junto das demais declarações
bool ledIntegradoLigado = false;

// Acrescente ao setup() existente
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, ledIntegradoLigado);

// Acrescente ao final do loop(), depois de gerar os eventos
if (pressionou) {
  ledIntegradoLigado = !ledIntegradoLigado;
  digitalWrite(LED_BUILTIN, ledIntegradoLigado);
}

Por que não repete

A condição é avaliada muitas vezes, mas pressionou só vale true na passagem que confirmou a transição para LOW. Nas passagens seguintes, mesmo com o botão mantido pressionado, ele volta a false; portanto, não há outra inversão.

Não use if (estadoEstavel == LOW) para alternar. Enquanto o botão estiver pressionado, essa condição continuará verdadeira em todas as passagens pelo loop() e inverteria o LED repetidamente.

Nível mantido versus evento

Comparação entre alternar repetidamente ao testar o nível pressionado e alternar uma vez ao usar um evento.

Um nível pode durar várias passagens pelo loop; um evento de pressionamento ocorre uma única vez.

Use a condição de evento

Pratique

Complete a condição que consome o evento correto. O LED deve alternar ao confirmar o pressionamento, não durante a manutenção do botão nem na soltura.

Aplique o evento de pressionamento

Preencha a condição do if.

cpp
if () {
  ledIntegradoLigado = !ledIntegradoLigado;
  digitalWrite(LED_BUILTIN, ledIntegradoLigado);
}

Verifique o comportamento

Preveja a sequência

Partindo com ledIntegradoLigado = false, considere que cada transição abaixo já foi confirmada pelo intervalo de estabilidade. Qual comportamento o LED integrado terá ao pressionar, manter pressionado, soltar e pressionar novamente?

Uma alternância por acionamento

Qual é o comportamento correto?

Passo 6 de 8

Atenda o botão sem parar a outra tarefa

Integre o botão com tratamento de rebote a uma segunda tarefa temporizada, sem bloquear o loop.

Monte a aplicação com duas tarefas

Monte entrada e saída externa

Com a alimentação desconectada, mantenha o botão entre D2 e GND; o sketch usará INPUT_PULLUP. Para a segunda tarefa, monte o LED externo: D7 → resistor de 1 kΩ → ânodo do LED vermelho. Ligue o cátodo do LED ao GND.

Confira a polaridade e os jumpers antes de alimentar. Use somente uma alimentação USB compatível no Uno R3. Não conecte fonte externa a VIN nem ao pino 5V durante esta prática.

Conexões da aplicação integrada

Diagrama da montagem com botão entre D2 e GND e LED externo ligado a D7 por um resistor de 1 kΩ, retornando ao GND.

O LED integrado responde ao evento de pressionamento; o LED externo em D7 representa a tarefa periódica.

Dica

O que observar

A alternância do LED externo não depende de o botão estar solto ou pressionado. Ela deve continuar enquanto a janela de estabilidade é verificada e também durante uma pressão mantida.

Prepare memórias e referências de tempo

1. Declarações persistentes

Crie um novo sketch na IDE Arduino local e copie primeiro estas declarações. Elas mantêm as memórias do botão já usadas nos steps anteriores e acrescentam uma marca de tempo e um estado próprios para o LED externo.

cpp
const uint8_t BOTAO_PIN = 2;
const uint8_t LED_EXTERNO_PIN = 7;

const unsigned long INTERVALO_ESTAVEL = 30;
const unsigned long INTERVALO_EXTERNO = 500;

int ultimoNivelObservado;
int estadoEstavel;
unsigned long instanteUltimaMudanca;

bool ledIntegradoLigado = false;
bool ledExternoLigado = false;
unsigned long instanteUltimaTrocaExterna;

2. Configuração inicial

Em seguida, copie o setup(). A leitura inicial é adotada como condição de partida: ela inicializa as memórias do botão, mas não cria um evento de pressionamento.

cpp
void setup() {
  pinMode(BOTAO_PIN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_EXTERNO_PIN, OUTPUT);

  digitalWrite(LED_BUILTIN, ledIntegradoLigado);
  digitalWrite(LED_EXTERNO_PIN, ledExternoLigado);

  int leituraInicial = digitalRead(BOTAO_PIN);
  ultimoNivelObservado = leituraInicial;
  estadoEstavel = leituraInicial;

  unsigned long agora = millis();
  instanteUltimaMudanca = agora;
  instanteUltimaTrocaExterna = agora;
}

Execute as verificações no mesmo loop

3. Loop: botão e sinalização periódica

Complete o sketch com este loop(), grave-o no Uno R3 e observe a montagem. As duas verificações temporais usam referências diferentes: uma é reiniciada por mudança observada no botão; a outra somente quando o LED externo alterna.

cpp
void loop() {
  bool pressionou = false;

  unsigned long agora = millis();
  int leituraAtual = digitalRead(BOTAO_PIN);

  if (leituraAtual != ultimoNivelObservado) {
    ultimoNivelObservado = leituraAtual;
    instanteUltimaMudanca = agora;
  }

  if ((leituraAtual != estadoEstavel) &&
      (agora - instanteUltimaMudanca >= INTERVALO_ESTAVEL)) {
    int estadoEstavelAnterior = estadoEstavel;
    estadoEstavel = leituraAtual;

    if ((estadoEstavelAnterior == HIGH) &&
        (estadoEstavel == LOW)) {
      pressionou = true;
    }
  }

  if (pressionou) {
    ledIntegradoLigado = !ledIntegradoLigado;
    digitalWrite(LED_BUILTIN, ledIntegradoLigado);
  }

  if (agora - instanteUltimaTrocaExterna >= INTERVALO_EXTERNO) {
    instanteUltimaTrocaExterna = agora;
    ledExternoLigado = !ledExternoLigado;
    digitalWrite(LED_EXTERNO_PIN, ledExternoLigado);
  }
}
Saída esperada
Resultado esperado na montagem:
- A cada pressionamento confirmado, o LED integrado alterna uma única vez.
- Manter o botão pressionado não produz novas alternâncias no LED integrado.
- O LED externo ligado a D7 alterna aproximadamente a cada 500 ms.
- O LED externo continua alternando durante o rebote e enquanto o botão permanece pressionado.

A alternância não é um texto no Monitor Serial: ela é observada fisicamente nos LEDs. O instante exato pode variar alguns milissegundos conforme a execução do loop.

Por que as duas tarefas continuam ativas

Não há delay() nem laço de espera. Em toda passagem pelo loop(), o programa lê o botão, verifica se ele ficou estável, eventualmente consome o evento e também verifica se já é hora de alternar D7. Uma tarefa não precisa esperar a outra terminar.

Confira a independência das tarefas

Duas marcas, duas responsabilidades

Complete cada referência de tempo com a variável que pertence à tarefa indicada. As duas condições são avaliadas repetidamente no mesmo loop().

Identifique as referências

Preencha as referências temporais independentes.

cpp
if (agora -  >= INTERVALO_ESTAVEL) {
  // a leitura do botão pode ser aceita como estável
}

if (agora -  >= INTERVALO_EXTERNO) {
  // o LED externo deve alternar
}

Verifique a pressão mantida

Com o sketch gravado, mantenha o botão pressionado por pelo menos 3 segundos. Relate o que aconteceu com o LED integrado e diga explicitamente se o LED externo em D7 continuou alternando.

Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 8

Ajuste a janela e reconheça os limites

Ajuste o intervalo de estabilidade com base em ensaios e diferencie uma transição perdida de uma transição rejeitada.

Entenda os dois limites

Janela: um compromisso

O intervalo de estabilidade não é uma duração universal de rebote. Ele é um parâmetro do seu ensaio: deve ser grande o bastante para não aceitar oscilações do botão como comandos e pequeno o bastante para a resposta continuar adequada.

Uma janela curta pode confirmar uma oscilação indesejada. Uma janela longa aumenta a demora entre pressionar e gerar o evento — e pode rejeitar uma pressão muito breve. O programa continua atendendo o restante do loop() nos dois casos; o que muda é o critério para aceitar a transição.

O que a leitura consegue distinguir

Diagrama com três linhas do tempo: pulso perdido entre leituras, pulso observado mas curto demais e nível baixo mantido tempo suficiente para confirmação.

Há dois motivos diferentes para não haver evento: a mudança pode não ter sido lida, ou pode ter sido lida mas não ter permanecido estável pelo intervalo exigido.

Teste um parâmetro de cada vez

Compare sem mudar o restante

Faça ensaios no seu Uno R3 com a mesma sequência: dê alguns toques breves, faça uma pressão mantida por cerca de um segundo, solte e pressione novamente. Altere somente o intervalo de estabilidade usado no sketch; experimente, por exemplo, 10 ms, 30 ms e 60 ms.

Observe o LED integrado, que muda pelo evento de pressionamento, e o LED externo periódico. Eles servem para verificar o comportamento funcional. Não permitem medir com precisão o rebote nem provar que toda falha possível seria detectada.

Roteiro de comparação

Use a mesma sequência de acionamentos em cada ensaio. O resultado depende do botão, da forma de pressionar e da frequência com que o loop() é executado.

Possíveis consequências ao alterar apenas a janela de estabilidade.
JanelaVantagem possívelRisco ou custo
10 msResposta mais rápidaAceitar oscilações do contato
30 msPonto inicial para ensaioPode não servir a todo botão
60 msMaior rejeição de oscilaçõesResposta tardia; toque breve rejeitado

Verifique sua interpretação

Diferencie perda e rejeição

O intervalo de estabilidade é 30 ms. Em uma linha do tempo, o botão fica em LOW e volta a HIGH inteiramente entre duas passagens consecutivas pelo loop(). Em outra, uma passagem lê LOW, mas 12 ms depois a leitura volta a HIGH. Qual interpretação está correta?

Conclua o ensaio

Registre sua decisão

No seu ensaio, qual intervalo você usou e o que ocorreu com toques breves, pressão mantida e o LED periódico? Diga se você manteria ou alteraria a janela e justifique pelo que observou.

Escreva pelo menos 80 caracteres (0/80).

Passo 8 de 8

Valide o comportamento completo

Valide a aplicação completa: um evento por pressionamento confirmado, sem repetição durante a pressão e com a tarefa periódica ainda ativa.

Enxergue estado e evento na sequência

Revise o fluxo completo

O fluxo que você vai validar

Em cada passagem por loop(), o programa:

  1. lê o botão;
  2. reinicia a janela de estabilidade se a leitura mudou;
  3. aceita um novo estado estável somente após o intervalo configurado;
  4. emite um evento apenas na transição estável de HIGH para LOW;
  5. alterna o LED integrado ao consumir esse evento.

A leitura pode oscilar durante o rebote, mas o evento só existe em uma passagem pelo loop(). Por isso, manter o botão pressionado não cria novas alternâncias.

Da leitura ao comando único

Diagrama temporal com leitura oscilante, estado estável confirmado depois, um pulso único de evento e uma onda periódica independente.

O evento é um pulso único gerado após a confirmação; a tarefa periódica continua durante todo o processo.

Execute e observe os ensaios

Roteiro de validação

Faça os ensaios na sua montagem do Uno R3 com o sketch integrado do step anterior. Observe o LED integrado, controlado pelo evento de pressionamento, e o LED externo em D7, que deve continuar alternando aproximadamente a cada 500 ms.

Anote o valor de INTERVALO_ESTAVEL que está no sketch antes de começar. Não acrescente delay() para testar o botão.

Resultados esperados

Compare o que ocorreu na sua montagem com estes resultados. A alternância do LED integrado indica que um pressionamento confirmado foi reconhecido.

Sequência de testes para a aplicação completa.
EnsaioLED integradoLED externo D7
Pressione e solte uma vezAlterna uma vezContinua periódico
Mantenha pressionadoNão alterna novamenteContinua periódico
Solte o botãoNão alternaContinua periódico
Pressione de novo após soltarAlterna uma vezContinua periódico
Dê um toque muito brevePode não alternarContinua periódico

Dica

Verifique o reset com botão pressionado

Teste de inicialização

Com a alimentação desconectada, mantenha o botão pressionado. Conecte somente o USB e aguarde mais que INTERVALO_ESTAVEL. O LED integrado não deve alternar apenas por iniciar nessa condição.

Na inicialização, o programa adota a leitura inicial como ultimoNivelObservado e estadoEstavel. Como não houve uma transição confirmada de HIGH para LOW depois da partida, não existe evento de pressionamento.

Aplique a lógica de revisão

Reconstrua a decisão essencial

Complete as condições do mesmo algoritmo usado no sketch integrado. A janela deve reiniciar diante de uma leitura diferente; o LED só pode receber o evento quando a transição estável confirmada for de HIGH para LOW.

Complete a confirmação e o evento

Preencha as condições sem trocar os identificadores do sketch que você executou.

cpp
bool pressionou = false;
unsigned long agora = millis();
int leituraAtual = digitalRead(BOTAO_PIN);

if () {
  ultimoNivelObservado = leituraAtual;
  instanteUltimaMudanca = agora;
}

if ((leituraAtual != estadoEstavel) &&
    ()) {
  int estadoEstavelAnterior = estadoEstavel;
  estadoEstavel = leituraAtual;

  if () {
    pressionou = true;
  }
}

if (pressionou) {
  ledIntegradoLigado = !ledIntegradoLigado;
  digitalWrite(LED_BUILTIN, ledIntegradoLigado);
}

Conclua com evidências

Registre sua validação

Registre o valor de INTERVALO_ESTAVEL e o resultado de: clique normal, pressão mantida, soltura e segundo clique. Inclua o comportamento do LED externo em D7 e o resultado do teste de reset com o botão mantido pressionado.

Se um toque muito breve não gerou alternância, registre também esta conclusão: pelos LEDs sozinhos, não é possível distinguir se o pulso passou inteiramente entre duas leituras ou se foi observado, mas não permaneceu estável pelo intervalo exigido. Para diferenciá-los seria preciso uma linha do tempo conhecida ou outra evidência da leitura.

Escreva pelo menos 120 caracteres (0/120).

Resumo

Síntese final

Você consolidou estes critérios:

  • leitura instantânea, estado estável e evento têm papéis diferentes;
  • rebotes reiniciam a janela, sem parar o loop();
  • um evento de pressionamento ocorre uma única vez por transição confirmada para LOW;
  • segurar ou soltar o botão não alterna a saída;
  • outra tarefa temporizada continua sendo atendida.

No próximo tutorial, esses eventos poderão participar de comportamentos mais estruturados.

  • A inicialização adota o nível presente sem emitir evento.
  • Um pressionamento confirmado alterna o LED integrado uma vez.
  • A tarefa de D7 continua durante rebote e pressão mantida.
  • Os LEDs verificam o funcionamento, mas não distinguem sozinhos as causas de um toque breve não aceito.

Tutorial concluído

Parabéns! Você concluiu: Eventos de botão com tratamento de rebote

Muito bem! Você separou leitura, estado estável e evento para responder ao botão sem comandos repetidos nem bloqueio da outra tarefa.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso