Índice:

Um menu no Arduino e como usar os botões: 10 etapas (com imagens)
Um menu no Arduino e como usar os botões: 10 etapas (com imagens)

Vídeo: Um menu no Arduino e como usar os botões: 10 etapas (com imagens)

Vídeo: Um menu no Arduino e como usar os botões: 10 etapas (com imagens)
Vídeo: Módulo LCD Keypad Shield para Arduino. Como usar e programar! 2024, Novembro
Anonim
Um menu no Arduino e como usar os botões
Um menu no Arduino e como usar os botões

Em meu tutorial do Arduino 101, você aprenderá como configurar seu ambiente no Tinkercad. Eu uso o Tinkercad porque é uma plataforma online muito poderosa que me permite demonstrar uma variedade de habilidades aos alunos para a construção de circuitos. Sinta-se à vontade para construir todos os meus tutoriais usando o IDE do Arduino e um Arduino real!

Neste tutorial, aprenderemos sobre botões! Nós precisamos saber:

  • Como conectá-los
  • Lendo o valor deles
  • Debounce, e por que é importante
  • Uma aplicação prática (criação de um menu)

A maioria das pessoas pensa que a coisa mais prática a fazer com um botão é acender e apagar uma luz. Vamos, não aqui! Vamos usar o nosso para criar um menu e definir algumas opções no Arduino.

Preparar? Vamos começar!

Etapa 1: configurar a placa

Configure a placa
Configure a placa
Configure a placa
Configure a placa

A primeira etapa é colocar um Arduino e um breadboard Small na área de prototipagem. Verifique as imagens acima para ver como fazer a fiação dos trilhos de alimentação.

A Breadboard Mini tem dois trilhos de alimentação superior e inferior. Nós os conectamos ao Arduino para que possamos fornecer energia para mais componentes. Posteriormente neste tutorial, usaremos 3 botões, portanto, precisaremos de mais energia. A coisa a notar é que em uma placa de ensaio pequena, os trilhos de alimentação correm ao longo da placa, horizontalmente. Isso é diferente das colunas na área de prototipagem principal no meio; estes correm verticalmente. Você pode usar qualquer um dos pinos de alimentação para fornecer energia a qualquer coluna na área principal no meio.

Ao adicionar energia, use os fios preto e vermelho no negativo e no positivo, respectivamente. Adicione fios na extremidade que passam energia para o outro lado da placa. Não vamos usar esse lado, mas é uma boa prática.

Etapa 2: adicionar o botão e o resistor

Adicione o botão e o resistor
Adicione o botão e o resistor
Adicione o botão e o resistor
Adicione o botão e o resistor
Adicione o botão e o resistor
Adicione o botão e o resistor

Adicione um pequeno botão da bandeja de componentes. Deve ser semelhante ao da imagem. Certifique-se de que não é um switch! Adicione um resistor também. Clique nele e defina seu valor para 10kΩ. Isso é o suficiente para puxar o pino para baixo quando não estiver conectado, o que é muito importante posteriormente no código.

Coloque o componente no meio da placa de ensaio. A forma como um botão funciona é:

  • De canto a canto, o botão não está conectado. Apertar o botão fecha os contatos e conecta os cantos.
  • Os lados do botão estão conectados. Se você conectou um fio no canto superior esquerdo e no canto inferior esquerdo, o circuito seria fechado.

É por isso que colocamos o componente no meio do espaço. Certifica-se de que os cantos não estão conectados sob os pinos da placa.

A próxima etapa fornece algumas imagens que ilustram esses pontos.

Coloque o resistor do pino inferior direito nas colunas, de forma que fique horizontalmente.

Etapa 3: Conexões de botões

Conexões de botão
Conexões de botão
Conexões de botão
Conexões de botão

As imagens acima deixam bem claro como os botões se conectam. Sempre foi um ponto de confusão quando você pensa que algo está bom e não funciona!

Agora, vamos adicionar os fios.

  • Coloque um fio vermelho de um pino de alimentação positivo na mesma coluna do pino inferior direito do botão
  • Coloque um fio preto de um pino de alimentação negativa na mesma coluna do resistor.
  • Coloque um fio colorido (não vermelho / preto) do pino superior esquerdo ao pino digital 2 no Arduino

Verifique as imagens acima para certificar-se de que a fiação está correta.

Etapa 4: O Código …

O código…
O código…
O código…
O código…

Vamos dar uma olhada no código de um botão básico.

Abra o editor de código e mude de Blocos para Texto. Limpe o aviso que surgir. Estamos felizes com o texto!

Você conhece a configuração básica, então vamos definir o botão e fazer uma leitura básica. Imprimiremos a saída em Serial.

Eu coloquei alguns comentários extras no código abaixo para que seja mais fácil de ler do que a imagem.

// Definir constantes

#define botão 2 void setup () {pinMode (botão, INPUT); Serial.begin (9600); } void loop () {// Lê o pino digital para verificar o status do botão int pressionado = digitalRead (botão); // O botão retorna HIGH se pressionado, LOW se não se (pressionado == HIGH) {Serial.println ("Pressed!"); }}

Ok, bem, isso funciona!

Basicamente, tudo o que estamos fazendo é verificar o status do pino digital cada vez que o código é executado em loop. Se você clicar em Iniciar Simulação e pressionar o botão, verá o Monitor Serial (clique no botão abaixo do código) exibir "Pressionado!" repetidamente.

Um recurso que você verá no código acima é a avaliação da condição if () ocorrendo. Tudo o que o código está fazendo é fazer uma pergunta e avaliar se ela é verdadeira, neste caso. Usamos o é igual (sinais de igual duplos, como este: ==) para verificar se o valor da variável é igual a um determinado valor. Um digitalRead () retorna HIGH ou LOW.

Usando if () else if / else podemos verificar muitas condições ou todas as condições, e se você voltar ao Arduino Basics, verá algumas das comparações que pode fazer.

Agora … Nosso código pode parecer completo … Mas temos um problema.

Veja, isso funciona muito bem no simulador. Mas a eletricidade real tem ruído, especialmente a eletrônica CC. Portanto, nosso botão pode retornar uma leitura falsa às vezes. E isso é um problema, porque seu projeto pode não responder da maneira certa para o usuário.

Vamos consertar!

Etapa 5: um pequeno comentário

Um pouco de Debounce
Um pouco de Debounce

Usamos um procedimento chamado debounce para superar nosso problema de botão. Essencialmente, isso espera um determinado período de tempo entre o momento em que o botão foi pressionado e a resposta real ao pressionamento. Ainda parece natural para o usuário (a menos que você torne o tempo muito longo). Você também pode usá-lo para verificar a duração do pressionamento, para que possa responder de forma diferente a cada vez. Você não precisa alterar nenhuma fiação!

Vejamos o código:

#define botão 2 # define debounceTimeout 100

A primeira mudança é no escopo global. Você se lembrará de que é onde definimos as variáveis que muitas das nossas funções podem usar ou aquelas que não podem ser redefinidas cada vez que o loop é acionado. Portanto, adicionamos debounceTimeout às constantes definidas. Fizemos 100 (que mais tarde se traduzirá em 100 ms), mas poderia ser mais curto. Mais e vai parecer anormal.

long int lastDebounceTime;

Esta variável é declarada abaixo das constantes. Este é um tipo long int, que basicamente nos permite armazenar números longos na memória. Chamamos de lastDebounceTime.

Não precisamos mudar nada na função void setup (). Vamos deixar esse aqui.

void loop () {// Lê o pino digital para verificar o status do botão int pressionado = digitalRead (botão); longo int currentTime = millis (); // Código do botão}

A primeira alteração que fazemos na função loop () está na chamada para ler o botão. Precisamos manter o controle da hora atual. A função millis () retorna a hora atual do relógio desde que o Arduino inicializou em milissegundos. Precisamos armazenar isso em uma variável do tipo int longo.

Agora, precisamos ter certeza de que estamos cientes do tempo desde que o botão foi pressionado, então zeramos o cronômetro quando ele não foi pressionado. Dê uma olhada:

void loop () {// Lê o pino digital para verificar o status do botão int pressionado = digitalRead (botão); longo int currentTime = millis (); if (pressionado == BAIXO) {// Zera o tempo de contagem enquanto o botão não está pressionado lastDebounceTime = currentTime; } // Código do botão}

O algoritmo if (pressionado == BAIXO) verifica se o botão não foi pressionado. Se não for, o código armazena a hora atual desde o último debounce. Dessa forma, cada vez que o botão é pressionado, temos um momento a partir do qual podemos verificar quando o botão foi pressionado. Podemos então fazer um cálculo matemático rápido para ver por quanto tempo o botão foi pressionado e responder corretamente. Vejamos o resto do código:

void loop () {// Lê o pino digital para verificar o status do botão int pressionado = digitalRead (botão); longo int currentTime = millis (); if (pressionado == BAIXO) {// Zera o tempo de contagem enquanto o botão não está pressionado lastDebounceTime = currentTime; } // O botão foi pressionado por um determinado tempo if (((currentTime - lastDebounceTime)> debounceTimeout)) {// Se o tempo limite for atingido, o botão pressionado! Serial.println ("Pressionado!"); }}

O último bloco de código pega o tempo atual, subtrai o tempo do último debounce e o compara com o tempo limite que definimos. Se for maior, o código assume que o botão foi pressionado naquele momento e responde. Arrumado!

Execute seu código e verifique se ele funciona. Se você tiver erros, verifique seu código!

Agora, vamos ver um exemplo prático.

Etapa 6: a confecção de um cardápio

A Fabricação de um Menu
A Fabricação de um Menu

Os botões são interessantes, porque existem tantas possibilidades com eles! Neste exemplo, vamos fazer um menu. Digamos que você tenha criado este dispositivo realmente excelente e precise que os usuários sejam capazes de alterar as opções para ligar ou desligar certas coisas, ou definir um valor específico para uma configuração. Este design de três botões pode fazer isso!

Então, para este projeto, precisamos:

  • Três botões
  • Três resistores ajustados para 10kΩ

Já temos um desses, só precisamos dos outros dois. Portanto, adicione-os ao quadro. A fiação é um pouco mais complexa, mas apenas porque eu queria mantê-la bem compacta. Você pode seguir o mesmo padrão para o primeiro botão ou seguir a imagem acima.

Os três botões são uma opção de menu abrir / próxima, uma opção de alteração (como em, alterar a configuração) e um botão de menu salvar / fechar.

Conecte-o, vamos dar uma olhada no código!

Etapa 7: Divisão de código - global

Ok, isso vai ser um longo passo, mas vou passar por cada seção do código.

Primeiro, vamos dar uma olhada nas variáveis globais necessárias.

// Definir constantes # define menuButton 2 #define menuSelect 3 # define menuSave 4 #define debounceTimeout 50 // Define variáveis int menuButtonPreviousState = LOW; int menuSelectPreviousState = LOW; int menuSavePreviousState = LOW; long int lastDebounceTime; // Opções do menu char * menuOptions = {"Check Temp", "Check Light"}; bool featureSetting = {false, false}; bool menuMode = false; bool menuNeedsPrint = false; int opçãoSelecionada = 0;

Esses três blocos são bastante semelhantes ao que vimos antes. No primeiro, defini os três botões e o tempo limite. Para esta parte do projeto, eu configurei para 50ms, então é preciso pressionar deliberadamente para que funcione.

O segundo bloco são todas as variáveis. Precisamos acompanhar o buttonPreviousState e precisamos acompanhar o lastDebounceTime. Todas essas são variáveis do tipo int, mas a última é um tipo longo, porque estou assumindo que precisamos de espaço na memória.

O bloco de opções do menu possui alguns novos recursos. Primeiro, o char * (sim, isso é um asterisco deliberado), que é uma variável literal de caractere / string. É um ponteiro para um armazenamento estático na memória. Você não pode mudá-lo (como você pode no Python, por exemplo). Esta linha char * menuOptions cria uma matriz de literais de string. Você pode adicionar quantos itens de menu quiser.

A variável bool featureSetting é apenas a matriz de valores que representa cada item de menu. Sim, você pode armazenar o que quiser, basta alterar o tipo de variável (todas devem ser do mesmo tipo). Agora, pode haver maneiras melhores de gerenciar isso, como dicionários ou tuplas, mas isso é simples para este aplicativo. Eu provavelmente criaria um deste último em um aplicativo implantado.

Eu mantive o controle do menuMode, então se eu quisesse outras coisas no meu display, eu poderia fazer isso. Além disso, se eu tivesse a lógica do sensor, poderia pausá-lo durante a operação do menu, apenas no caso de algo entrar em conflito. Eu tenho uma variável menuNeedsPrint porque quero imprimir o menu em horários específicos, não apenas o tempo todo. Finalmente, tenho uma variável optionSelected, então posso acompanhar a opção selecionada conforme a acesso em vários lugares.

Vejamos o próximo conjunto de funções.

Etapa 8: Divisão de código - configuração e funções personalizadas

A função setup () é fácil o suficiente, apenas três declarações de entrada:

void setup () {pinMode (menuSelect, INPUT); pinMode (menuSave, INPUT); pinMode (menuSelect, INPUT); Serial.begin (9600); }

A seguir estão as três funções personalizadas. Vejamos os dois primeiros, depois o último separadamente.

Precisamos de duas funções que retornam algumas informações. O motivo é que queremos ter certeza de que isso seja legível por humanos. Também ajudará a depurar o código se tivermos um problema. Código:

// Função para retornar a opção atual selecionada - char * ReturnOptionSelected () {char * menuOption = menuOptions [optionSelected]; // Retorna optionSelected return menuOption; } // Função para retornar o status da opção selecionada atualmente char * ReturnOptionStatus () {bool optionSetting = featureSetting [optionSelected]; char * optionSettingVal; if (optionSetting == false) {optionSettingVal = "False"; } else {optionSettingVal = "True"; } // Retorna optionSetting return optionSettingVal; }

A função char * ReturnOptionSelected () verifica a opção selecionada (se você ver acima, definimos uma variável para controlar isso) e puxa o literal de string do array que criamos anteriormente. Em seguida, ele o retorna como um tipo de char. Sabemos disso porque a função indica o tipo de retorno.

A segunda função, char * ReturnOptionStatus () lê o status da opção salva no array e retorna um literal de string que representa o valor. Por exemplo, se a configuração que armazenamos for falsa, eu retornaria "False". Isso porque mostramos ao usuário essa variável e é melhor manter toda essa lógica junta. Eu poderia fazer isso mais tarde, mas faz mais sentido fazer aqui.

// Função para alternar atual optionbool ToggleOptionSelected () {featureSetting [optionSelected] =! FeatureSetting [optionSelected]; return true; }

A função bool ToggleOptionSelected () é uma função conveniente para alterar o valor da configuração que selecionamos no menu. Apenas inverte o valor. Se você tivesse um conjunto de opções mais complexo, isso poderia ser bem diferente. Retorno verdadeiro nesta função, porque meu retorno de chamada (a chamada posterior no código que aciona esta função) espera uma resposta verdadeiro / falso. Tenho 100% de certeza de que isso vai funcionar, então não levei em consideração o fato de não funcionar, mas sim em um aplicativo implantado (por precaução).

Etapa 9: O Loop …

A função loop () é bastante longa, então faremos isso em partes. Você pode presumir que tudo o que está abaixo dos aninhamentos nesta função:

void loop () {

// Trabalhe aqui <-----}

Ok, já vimos isso antes:

// Lê os botões int menuButtonPressed = digitalRead (menuButton); int menuSelectPressed = digitalRead (menuSelect); int menuSavePressed = digitalRead (menuSave); // Obtém a hora atual long int currentTime = millis (); if (menuButtonPressed == LOW && menuSelectPressed == LOW && menuSavePressed == LOW) {// Reinicia o tempo de contagem enquanto o botão não está pressionado lastDebounceTime = currentTime; menuButtonPreviousState = LOW; menuSelectPreviousState = LOW; menuSavePreviousState = LOW; }

Tudo o que tive que fazer aqui foi adicionar as três chamadas digitalRead () e ter certeza de levar em conta o fato de que, se todos os botões estivessem baixos, deveríamos zerar o cronômetro (lastDebounceTime = currentTime) e definir todos os estados anteriores como baixos. Eu também armazeno millis () em currentTime.

A próxima seção se aninha dentro da linha

if (((currentTime - lastDebounceTime)> debounceTimeout)) {

// Trabalhe aqui <----}

Existem três seções. Sim, eu poderia tê-los movido para suas próprias funções, mas por uma questão de simplicidade, mantive os três algoritmos de botão principais aqui.

if ((menuButtonPressed == HIGH) && (menuButtonPreviousState == LOW)) {if (menuMode == false) {menuMode = true; // Avisa o usuário Serial.println ("Menu está ativo"); } else if (menuMode == true && optionSelected = 1) {// Reinicializar a opção optionSelected = 0; } // Imprime o menu menuNeedsPrint = true; // Alterna o botão anterior. estado para exibir apenas o menu // se o botão for liberado e pressionado novamente menuButtonPreviousState = menuButtonPressed; // Seria ALTO}

Este primeiro trata quando menuButtonPressed está HIGH, ou quando o botão de menu é pressionado. Ele também verifica se o estado anterior estava BAIXO, de modo que o botão tinha que ser liberado antes de ser pressionado novamente, o que impede o programa de disparar constantemente o mesmo evento repetidamente.

Em seguida, verifica se o menu não está ativo, o ativa. Ele imprimirá a primeira opção selecionada (que é o primeiro item na matriz menuOptions por padrão. Se você pressionar o botão uma segunda ou terceira vez (etc), obterá a próxima opção na lista. Algo que posso consertar é que quando chega ao fim, ele volta para o início. Isso poderia ler o comprimento do array e tornar o ciclo de volta mais fácil se você alterasse o número de opções, mas isso era simples por enquanto.

A última pequena seção (// Imprime o menu) obviamente imprime o menu, mas também define o estado anterior como HIGH para que a mesma função não faça loop (veja minha nota acima sobre como verificar se o botão estava anteriormente LOW).

// menuSelect é pressionado, fornece logicif ((menuSelectPressed == HIGH) && (menuSelectPreviousState == LOW)) {if (menuMode) {// Alterar a opção selecionada // No momento, isso é apenas verdadeiro / falso // mas pode ser qualquer coisa bool toggle = ToggleOptionSelected (); if (alternar) {menuNeedsPrint = true; } else {Serial.println ("Algo deu errado. Tente novamente"); }} // Alternar estado para alternar apenas se liberado e pressionado novamente menuSelectPreviousState = menuSelectPressed; }

Este trecho de código lida com o botão menuSelectPressed da mesma maneira, exceto que desta vez apenas disparamos a função ToggleOptionSelected (). Como eu disse antes, você poderia alterar essa função para que ela faça mais, mas isso é tudo que eu preciso fazer.

O principal a ser observado é a variável toggle, que rastreia o sucesso do retorno de chamada e imprime o menu se verdadeiro. Se não retornar nada ou falso, ele imprimirá a mensagem de erro. É aqui que você pode usar o retorno de chamada para fazer outras coisas.

if ((menuSavePressed == HIGH) && (menuSavePreviousState == LOW)) {// Saia do menu // Aqui você pode fazer qualquer arrumação // ou salvar em EEPROM menuMode = false; Serial.println ("Menu encerrado"); // Alterna o estado para que o menu saia apenas uma vez menuSavePreviousState = menuSavePressed; }}

Esta função lida com o botão menuSave, que apenas sai do menu. Aqui é onde você pode ter uma opção de cancelar ou salvar, talvez fazer alguma limpeza ou salvar na EEPROM. Acabei de imprimir "Menu encerrado" e definir o estado do botão como HIGH para que ele não faça loop.

if (menuMode && menuNeedsPrint) {// Imprimimos o menu, portanto, a menos que algo // aconteça, não há necessidade de imprimi-lo novamente menuNeedsPrint = false; char * optionActive = ReturnOptionSelected (); char * optionStatus = ReturnOptionStatus (); Serial.print ("Selecionado:"); Serial.print (optionActive); Serial.print (":"); Serial.print (optionStatus); Serial.println (); }

Este é o algoritmo menuPrint, que só dispara quando o menu está ativo e quando a variável menuNeedsPrint é definida como true.

Isso definitivamente poderia ser movido para sua própria função, mas por uma questão de simplicidade..!

Bem, é isso! Veja a próxima etapa para todo o bloco de código.

Etapa 10: Bloco de código final

// Definir constantes

#define menuButton 2 #define menuSelect 3 #define menuSave 4 #define debounceTimeout 50 int menuButtonPreviousState = LOW; int menuSelectPreviousState = LOW; int menuSavePreviousState = LOW; // Definir variáveis long int lastDebounceTime; bool lightSensor = true; bool tempSensor = true; // Opções do menu char * menuOptions = {"Check Temp", "Check Light"}; bool featureSetting = {false, false}; bool menuMode = false; bool menuNeedsPrint = false; int opçãoSelecionada = 0; // Função de configuração

void setup () {pinMode (menuSelect, INPUT); pinMode (menuSave, INPUT); pinMode (menuSelect, INPUT); Serial.begin (9600); }

// Função para retornar a opção selecionada atualmente char * ReturnOptionSelected () {char * menuOption = menuOptions [optionSelected]; // Retorna optionSelected return menuOption; } // Função para retornar o status da opção selecionada atualmente char * ReturnOptionStatus () {bool optionSetting = featureSetting [optionSelected]; char * optionSettingVal; if (optionSetting == false) {optionSettingVal = "False"; } else {optionSettingVal = "True"; } // Retorna optionSetting return optionSettingVal; } // Função para alternar a opção atual bool ToggleOptionSelected () {featureSetting [optionSelected] =! FeatureSetting [optionSelected]; return true; } // O loop principal

void loop () {// Lê os botões int menuButtonPressed = digitalRead (menuButton); int menuSelectPressed = digitalRead (menuSelect); int menuSavePressed = digitalRead (menuSave); // Obtém a hora atual long int currentTime = millis (); if (menuButtonPressed == LOW && menuSelectPressed == LOW && menuSavePressed == LOW) {// Reinicia o tempo de contagem enquanto o botão não está pressionado lastDebounceTime = currentTime; menuButtonPreviousState = LOW; menuSelectPreviousState = LOW; menuSavePreviousState = LOW; } if (((currentTime - lastDebounceTime)> debounceTimeout)) {// Se o tempo limite for atingido, botão pressionado!

// menuButton é pressionado, fornece lógica

// Só dispara quando o botão foi liberado anteriormente if ((menuButtonPressed == HIGH) && (menuButtonPreviousState == LOW)) {if (menuMode == false) {menuMode = true; // Avisa o usuário Serial.println ("Menu está ativo"); } else if (menuMode == true && optionSelected = 1) {// Reinicializar a opção optionSelected = 0; } // Imprime o menu menuNeedsPrint = true; // Alterna o botão anterior. estado para exibir apenas o menu // se o botão for liberado e pressionado novamente menuButtonPreviousState = menuButtonPressed; // Seria HIGH} // menuSelect é pressionado, fornece lógica se ((menuSelectPressed == HIGH) && (menuSelectPreviousState == LOW)) {if (menuMode) {// Alterar a opção selecionada // No momento, esta é apenas verdadeiro / falso // mas pode ser qualquer coisa bool toggle = ToggleOptionSelected (); if (alternar) {menuNeedsPrint = true; } else {Serial.print ("Algo deu errado. Tente novamente"); }} // Alternar estado para alternar apenas se liberado e pressionado novamente menuSelectPreviousState = menuSelectPressed; } if ((menuSavePressed == HIGH) && (menuSavePreviousState == LOW)) {// Saia do menu // Aqui você pode fazer qualquer arrumação // ou salvar em EEPROM menuMode = false; Serial.println ("Menu encerrado"); // Alterna o estado para que o menu saia apenas uma vez menuSavePreviousState = menuSavePressed; }} // Imprime a opção de menu atual ativa, mas imprime-a apenas uma vez if (menuMode && menuNeedsPrint) {// Imprimimos o menu, portanto, a menos que algo // aconteça, não há necessidade de imprimi-lo novamente menuNeedsPrint = false; char * optionActive = ReturnOptionSelected (); char * optionStatus = ReturnOptionStatus (); Serial.print ("Selecionado:"); Serial.print (optionActive); Serial.print (":"); Serial.print (optionStatus); Serial.println (); }}}

O circuito está disponível no site da Tinkercad. Eu embuti o circuito abaixo para vocês verem também!

Como sempre, se você tiver dúvidas ou problemas, entre em contato.

Recomendado: