Projeto109 - Mini Calculadora com Teclado Matricial 4X4 e Arduino

Básico - Projeto 109

Mini Calculadora com teclado membrana matricial 16 teclas com Arduino - Com uso da biblioteca "Keypad"

Objetivo

Este projeto tem como objetivo desenvolver uma calculadora simples capaz de realizar operações básicas de adição, subtração, multiplicação e divisão, utilizando um teclado matricial 4x4 como interface de entrada e um display LCD com comunicação I2C como interface de saída.

Neste projeto, o Arduino é responsável por ler as teclas pressionadas, identificar se o usuário está inserindo números ou operações, realizar o cálculo correspondente e exibir o resultado no display. Para facilitar a comunicação com o teclado matricial, utilizamos a biblioteca Keypad, que simplifica a detecção de teclas pressionadas, permitindo o mapeamento de botões de maneira intuitiva e organizada.

Além de praticar conceitos de programação no Arduino, este projeto proporciona o aprendizado sobre:

  • Leitura de entradas digitais (teclado matricial);

  • Exibição de dados em LCD via protocolo I2C;

  • Manipulação de strings e conversão de tipos de dados;

  • Implementação de lógica de fluxo (como estados de operação e controle de erros).

Assim, o projeto integra eletrônica básica, programação estruturada e controle de interfaces, sendo ideal para quem está avançando no aprendizado de Arduino.

Definições

Teclado Matricial De Membrana 4 X 4: O Teclado Matricial foi desenvolvido para servir como interface de entrada de dados em sistemas eletrônicos. Este teclado possui 16 teclas distribuídos em 4 linhas e 4 colunas, onde 10 delas são números (0 a 9), 4 são letras (A, B, C e D) e 2 são caracteres especiais. Também possui um terminal com 8 vias para a ligação.

Como o teclado matricial de membrana 4 X 4 trabalha:

a. O teclado possui 16 teclas, que estão dispostas em 4 linhas por 4 colunas, e ele possui 8 pinos para ligação. 

b. Abaixo de cada tecla do teclado matricial existe um pequeno interruptor de membrana. Esses interruptores estão organizados em linhas e colunas, formando uma malha de conexões. Todos os interruptores de uma mesma linha estão interligados por uma trilha condutora, e o mesmo acontece com os interruptores de uma mesma coluna. Ou seja, cada tecla está localizada na interseção de uma linha com uma coluna, o que permite identificar exatamente qual botão foi pressionado.

c. Por isso, esse tipo de teclado é chamado de teclado matricial, já que sua estrutura elétrica se baseia em uma matriz de linhas e colunas. Em um teclado 4x4, por exemplo, são utilizadas 4 linhas e 4 colunas, totalizando 8 pinos (vias) de conexão com o Arduino. A imagem abaixo ilustra essa disposição de forma mais clara.

d. O Arduino detecta qual botão foi pressionado detectando o pino de linha (R1, R2, R3 ou R4) e coluna (C1, C2, C3 ou C4) conectado ao botão.

e. Esse método é eficiente porque permite a leitura de múltiplas teclas usando uma quantidade reduzida de pinos, sem a necessidade de um fio separado para cada botão. No caso de um teclado 4x4, por exemplo, são necessários apenas 8 pinos (4 linhas + 4 colunas) para monitorar 16 teclas.

f. A biblioteca Keypad do Arduino automatiza esse processo por meio de uma varredura multiplexada das linhas e colunas. Ela configura dinamicamente os pinos como entrada ou saída e verifica, em alta velocidade, as conexões entre linhas e colunas para identificar qual tecla foi pressionada. Isso elimina a necessidade de escrever manualmente toda a lógica de detecção, tornando o código mais simples, legível e confiável.

Como o Arduino detecta a tecla pressionada:

Se você deseja entender em detalhes como o Arduino detecta uma tecla pressionada em um teclado matricial 4x4 sem utilizar bibliotecas, recomendo a leitura do artigo completo no site da Squids:​

Como usar um Teclado Matricial 4x4 com Arduino (Sem Biblioteca)

Neste tutorial, você encontrará uma explicação detalhada sobre o funcionamento de teclados matriciais, incluindo a estrutura de linhas e colunas, o processo de varredura multiplexada e como implementar a leitura das teclas diretamente no código do Arduino, sem depender de bibliotecas externas. O artigo também aborda conceitos fundamentais de controle de hardware em baixo nível e gerenciamento de entradas digitais, proporcionando uma compreensão aprofundada do tema.

Referências

Projeto108 - Como usar um Teclado Matricial 4x4 com Arduino (Com Biblioteca Keypad)

Projeto107 - Como usar um Teclado Matricial 4x4 com Arduino (Sem Biblioteca)

Projeto 48 - Como controlar um display LCD com o módulo I2C

Projeto 38 - Controlando um display LCD (instalação e comandos básicos) (Caso não possua um módulo I2C e queira adaptar o projeto para usar o display LCD sem adaptador)

Aplicação

Para fins didáticos e sistemas que precisam de interação humana, como por exemplo: sistema de senha para abertura de fechadura eletrônica, menu de navegação em displays, mini calculadores e conversores de unidade, controle de robôs e dispositivos (luzes, ventilação, irrigação), jogos como quiz ou perguntas de múltiplas escolhas, temporizadores, automação residência, etc.

Componentes necessários

Referência

Componente

Quantidade

Imagem

Observação

Teclado Matricial de Membrana Teclado Matricial de Membrana 4 X 4 (16 teclas) 1

Teclas: 16

Conector: 8 pinos (2,54mm)

Montagem: Auto-Adesivo

Limites de Operação: 35VDC, 100mA

Isolação: 100MΩ, 100V

Tempo de contato: 5ms

Durabilidade: 1 milhão de ciclos por tecla

Temperatura de Funcionamento: 0-70°C

Jumpers Kit cabos ligação macho / macho 1  
Display LCD Display LCD 16 X 2 com pinos soldados 1

LCD que utilize o controlador HD44780 (veja na descrição ou datasheet do componente)

O display poderá ser de qualquer cor (fundo verde, azul ou vermelho)

Módulo I2C

Módulo I2C com CI PCF8574

1 Módulo I2C display LCD Arduino

O módulo I2C poderá vir separado ou já soldado no display LCD

(datasheet)

Arduino UNO R3 Arduino UNO 1

Você poderá utilizar uma placa Arduino UNO original ou similar

Montagem do Circuito

Conecte os componentes como mostra a figura abaixo. Verifique cuidadosamente os cabos de ligação antes de ligar seu Arduino. Lembre-se que o Arduino deve estar totalmente desconectado da força enquanto você monta o circuito.

Fonte: Circuit Basics

Atenção

1. Verifique abaixo como devem ser as conexões do Teclado Matricial no Arduino:

Pinos de conexão do Teclado Matricial

1.1. Tabela de conexão com o Arduino (de acordo com o nosso exemplo)

Pinos do Teclado Matricial Pinos Digitais do Arduino
L1 9
L2 8
L3 7
L4 6
C1 5
C2 4
C3 3
C4 2

2. No projeto, vamos utilizar também um display LCD 16x2 com controlador HD44780, que se adapta aos mais diversos projetos com vários modelos de placas e microcontroladores. Este display possui 16 colunas por 2 linhas com backlight (luz de fundo) verde, azul ou vermelha e tem 16 pinos para a conexão.

3. Para a montagem do display com adaptador I2C, entenda a estrutura do módulo I2C para display LCD 16x2 / 20X4:

Módulo I2C - Detalhes

3.1. Na lateral do adaptador encontramos 4 pinos, sendo: 2 pinos para alimentação (Vcc e GND) e 2 pinos para conexão com a interface I2C (SDA e SCL) que deverão estar conectados nos pinos analógicos A4 (SDA) e A5 (SCL) do Arduino Uno ou nos pinos A20 (SDA) e A21 (SCL) do Arduino Mega 2560. Veja a tabela abaixo com onde temos as principais placas Arduino e suas conexões com o I2C.

3.2. Para controlar o contraste do display, utilize o potenciômetro de ajuste de contraste. O jumper lateral, quando utilizado, permite que a luz do fundo (backlight) seja controlada pelo programa ou permaneça apagada.

3.3. O módulo I2C pode ser separado ou já vir soldado no próprio display LCD. Para saber todos os detalhes sobre a montagem e utilização de display LCD com módulo I2C leia: Projeto 48 - Como controlar um display LCD com o módulo I2C.

3.4. Caso não possua um módulo I2C e queira adaptar o projeto para usar o display LCD sem adaptador leia: Projeto 38 - Controlando um display LCD (instalação e comandos básicos). Depois você terá que adaptar o projeto aproveitando os 8 pinos restantes para a montagem do Teclado Matricial. Também terá que ajustar o código para essas alterações.

4. A montagem do nosso exemplo foi realizada com um display LCD com comunicação via protocolo I2C.

Incluindo a biblioteca LiquidCrystal_I2C

Atenção 1: Caso você opte pela utilização do display de LCD sem o módulo I2C, siga os procedimentos do Projeto 38 - Controlando um display LCD (instalação e comandos básicos) e não instale a biblioteca abaixo.

Atenção 2: Se estiver utilizando uma biblioteca diferente para o controle do módulo I2C, lembre-se de ajustar o código do projeto de acordo com os comandos específicos dessa biblioteca.

1. Para que o módulo I2C funcione corretamente é necessário adicionarmos a biblioteca LiquidCrystal_I2C no IDE do Arduino. Uma das grandes vantagens das placas Arduino é a diversidade de bibliotecas disponíveis que podem ser utilizadas em seus programas. Estas bibliotecas podem ser criadas para a linguagem "C" ou especificamente para o Arduino, reduzindo drasticamente o tempo gasto com programação.  Veja a tabela Coletânea de bibliotecas para módulos.

Download dos arquivos da biblioteca LiquidCrystal_I2C 

 DOWNLOAD - NewliquidCrystal_1.3.4.zip

Para saber detalhes desta biblioteca clique aqui.

2. Após fazer o download do arquivo NewliquidCrystal_1.3.4.zip com todos os arquivos da biblioteca compactados no formato zip, abra o IDE do Arduino e siga o tutorial: Como incluir uma biblioteca no IDE do Arduino.

Biblioteca Wire

1. Para realizar a comunicação via protocolo I2C (comunicação com 2 fios: clock (SCL) e dado (SDA) podemos utilizar a biblioteca Wire.h que já vem instalada por padrão no IDE do Arduino.

2. A biblioteca Wire.h é responsável por conter as funções necessárias para gerenciar a comunicação entre os dispositivos através do protocolo I2C.

3. Muitas bibliotecas específicas para módulos e sensores com comunicação I2C já tem incluído a biblioteca Wire no seu escopo. Entretanto, é recomendável utilizar sempre essa biblioteca quando utilizarmos a comunicação via protocolo I2C, afim de evitarmos possíveis erros de comunicação.

Incluindo a biblioteca Keypad

Para facilitar o uso do Teclado Matricial de Membrana 4x4, utilizaremos a biblioteca Keypad, que automatiza o processo de detecção das teclas pressionadas. Com ela, não é necessário lidar diretamente com os detalhes da varredura multiplexada, pois toda a lógica de leitura da matriz de linhas e colunas é gerenciada internamente pela biblioteca, tornando o desenvolvimento mais simples e eficiente.

Instalação da biblioteca

1. No IDE do Arduino, acesse a aba Sketch (Rascunho), selecione [Incluir Biblioteca] e depois [Gerenciar Bibliotecas...].

2.  No campo de pesquisa digite Keypad. Localizada a biblioteca Keypad (por Mark Stanley, Alexander) clique no botão [Instalar].

3. Após a instalação, observe que aparecerá a informação que a biblioteca foi instalada.

Código do Projeto (Sketch)

1. Faça o download e abra o arquivo projeto109.ino no IDE do Arduino: DOWNLOAD - projeto109.ino

Obs. 1: Nos casos em que módulo I2C estiver configurado com um endereço diferente do endereço 0X27, altere a alinha de programação -> LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3, POSITIVE); com o endereço correto. - Veja o tutorial Projeto 48 - Como controlar um display LCD com o módulo I2C

Obs. 2: Se estiver utilizando uma biblioteca diferente para o controle do módulo I2C, ajuste o código do projeto de acordo com os comandos específicos dessa biblioteca.

Obs. 3: Se não possuir o módulo I2C utilize apenas o display LCD conforme Projeto 38 - Controlando um display LCD (instalação e comandos básicos) e ajuste o sketch para esta configuação. Lembrando que você terá que alterar a montagem do Teclado Matricial conectando-o nos pinos digitais que restarem.

2. Se preferir, copie e cole o código abaixo no IDE do Arduino:

/*******************************************************************************
*
*          Projeto 109: Mini Calculadora com Teclado Matricial 4X4 e Arduino
*               http://squids.com.br/arduino
*               autor: Angelo Luis Ferreira
*
*******************************************************************************/
#include <Wire.h>                   // Biblioteca para comunicação I2C
#include <LiquidCrystal_I2C.h>       // Biblioteca para usar display LCD com I2C
#include <Keypad.h>                  // Biblioteca para usar o Teclado Matricial

const int rows = 4;                  // Número de linhas do teclado
const int cols = 4;                  // Número de colunas do teclado

char keys[rows][cols] = {            // Definição do layout do teclado
  {'1', '2', '3', 'A'}, // 'A' será subtração (-)
  {'4', '5', '6', 'B'}, // 'B' será adição (+)
  {'7', '8', '9', 'C'}, // 'C' será limpar
  {'*', '0', '#', 'D'}  // '*' para multiplicação (x), '#' para divisão (/), 'D' para igual (=)
};

byte rowPins[rows] = {9, 8, 7, 6};   // Pinos do Arduino conectados às linhas do teclado
byte colPins[cols] = {5, 4, 3, 2};   // Pinos do Arduino conectados às colunas do teclado

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3, POSITIVE); // Inicializa o display no endereço 0x27

Keypad teclado = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols); // Inicia teclado

String num1 = "";
String num2 = "";
char operacao = '\0';
bool esperandoSegundoNumero = false;

void setup() {
  lcd.begin(16,2);                   // Inicializa o LCD (16x2)
  lcd.setBacklight(HIGH);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("-- Calculadora --");
  delay(2000);
  lcd.clear();
}

void loop() {
  char tecla = teclado.getKey();

  if(tecla) {
    if(tecla >= '0' && tecla <= '9') { // Se a tecla for número
      if(!esperandoSegundoNumero) {
        num1 += tecla;
      } else {
        num2 += tecla;
      }
      mostrarDisplay();
    }
    else if(tecla == 'A' || tecla == 'B' || tecla == '*' || tecla == '#') { // Operações
      if(num1.length() > 0) {
        esperandoSegundoNumero = true;
        operacao = tecla;
        mostrarDisplay();
      }
    }
    else if(tecla == 'D') { // 'D' usado como botão Igual
      if(num1.length() > 0 && num2.length() > 0 && operacao != '\0') {
        calcular();
      }
    }
    else if(tecla == 'C') { // 'C' usado para limpar
      limpar();
    }
  }
}

void mostrarDisplay() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(num1);
  if(operacao != '\0') {
    lcd.print(" ");
    lcd.print(simboloOperacao(operacao));
    lcd.print(" ");
  }
  lcd.print(num2);
}

void calcular() {
  float n1 = num1.toFloat();
  float n2 = num2.toFloat();
  float resultado = 0;

  switch(operacao) {
    case 'B': // Soma
      resultado = n1 + n2;
      break;
    case 'A': // Subtração
      resultado = n1 - n2;
      break;
    case '*': // Multiplicação
      resultado = n1 * n2;
      break;
    case '#': // Divisão
      if(n2 != 0) {
        resultado = n1 / n2;
      } else {
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Erro: Div/0");
        delay(2000);
        limpar();
        return;
      }
      break;
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Resultado:");
  lcd.setCursor(0,1);
  lcd.print(resultado);
  
  delay(3000); // Espera 3 segundos para exibir o resultado
  limpar();
}

void limpar() {
  num1 = "";
  num2 = "";
  operacao = '\0';
  esperandoSegundoNumero = false;
  lcd.clear();
}

char simboloOperacao(char op) {
  switch(op) {
    case 'B': return '+';
    case 'A': return '-';
    case '*': return 'x';
    case '#': return '/';
    default: return ' ';
  }
}

Vídeo

Como o projeto deve funcionar

1. Ao iniciar o programa, o display LCD 16x2 exibe a mensagem -- Calculadora -- por 2 segundos, funcionando como uma tela de abertura. Após esse tempo, o display é automaticamente limpo, sinalizando que a calculadora está pronta para uso.

2. Assim que o usuário começa a digitar números no teclado, os dígitos são armazenados na variável num1, que representa o primeiro operando da operação matemática. Essa entrada continua até que uma tecla de operação seja pressionada.

4. Ao pressionar uma das teclas de operação (como soma, subtração, multiplicação ou divisão), a calculadora registra o operador escolhido e começa a armazenar os próximos dígitos digitados na variável num2, que representa o segundo operando.

5. Teclas de Operação: Cada tecla especial do teclado 4x4 tem uma função definida:

  • "B" (Adição) → Define a operação como soma (+).

  • "A" (Subtração) → Define a operação como subtração (-).

  • "*" (Multiplicação) → Define a operação como multiplicação (x).

  • "#" (Divisão) → Define a operação como divisão (/).

  • "C" (Limpar) → Reseta tudo, apagando os números e a operação em andamento.

  • "D" (Igual) → Calcula o resultado da operação selecionada entre num1 e num2.

6. À medida que os números e a operação são inseridos, o display LCD mostra as informações em tempo real no formato: num1 operador num2, facilitando a visualização da operação em andamento.

7. Ao pressionar a tecla"D" (igual), o Arduino converte as variáveis num1 e num2 (do tipo String) para float e executa a operação correspondente com base no operador selecionado.

Obs.: Se houver tentativa de divisão por zero (0 como divisor), o sistema detecta o erro e exibe: Erro: Div/0, esperando 2 segundos antes de limpar e resetar a calculadora automaticamente.

8. O resultado da operação é exibido no display por 3 segundos. Em seguida, a calculadora é automaticamente reiniciada, pronta para uma nova operação.

Obs.: A qualquer momento, o usuário pode pressionar a tecla "C" para limpar os dados digitados e começar uma nova conta do zero.

9. Esse projeto simula uma calculadora de verdade, utilizando o teclado e o o teclado matricial 4x4 como interface de entrada e o display LCD como saída visual. Toda a lógica — desde a captura das teclas até os cálculos e exibição dos resultados — é gerenciada pelo Arduino, proporcionando uma experiência simples, prática e educativa.

Explicando o código do projeto

Cabeçalho Informativo (comentário)

➡ Comentário de cabeçalho que identifica o projeto.

/*******************************************************************************
*
*          Projeto 109: Mini Calculadora com Teclado Matricial 4X4 e Arduino
*               http://squids.com.br/arduino
*               autor: Angelo Luis Ferreira
*
*******************************************************************************/

Esses comentários com asteriscos não têm efeito no código. Servem apenas como documentação para identificar o projeto, autoria e origem. Uma boa prática profissional.

Inclusão das bibliotecas

#include <Wire.h>                   // Biblioteca para comunicação I2C
#include <LiquidCrystal_I2C.h>      // Biblioteca para controle do LCD via I2C
#include <Keypad.h>                 // Biblioteca para uso do teclado matricial

Essas diretivas #include inserem bibliotecas que contêm funções e definições prontas. Isso evita a necessidade de escrever tudo do zero.

  • Wire.h: Necessária para comunicação via barramento I2C, muito comum com displays e sensores.

  • LiquidCrystal_I2C.h: Interface amigável para displays LCD via I2C, usando menos conexões graças ao módulo I2C.

  • Keypad.h: Facilita a leitura de teclas em teclados matriciais (ex: 4x4, 3x4).

Definições do teclado

➡ Define o número de linhas e colunas do teclado 4x4.

const int rows = 4; // número de linhas
const int cols = 4; // número de colunas

➡ Define o número de linhas e colunas do teclado 4x4.

Aqui, definimos constantes (const) que representam a quantidade de linhas e colunas do teclado matricial.

O uso de const garante que esses valores não serão alterados durante a execução do programa.

  • const int: define uma variável constante do tipo inteiro.

Mapeamento das teclas

➡ Matriz que representa as teclas dispostas no teclado físico. Cada tecla corresponde a uma posição (linha, coluna).

char keys[rows][cols] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

Esse é um array bidimensional (matriz) de caracteres (char), que define a disposição física das teclas e os símbolos atribuídos a cada uma.

  • char é um tipo de dados usado para armazenar um único caractere. Ele ocupa um byte de memória e pode armazenar valores de -128 a 127 (se signed) ou de 0 a 255 (se unsigned). É importante notar que char não armazena o caractere propriamente dito, mas sim o seu código ASCII (um valor inteiro).

O teclado será interpretado com base nesse layout. Por exemplo, ao pressionar o botão físico que está na terceira linha e segunda coluna, o caractere '8' será reconhecido.

Pinos do Arduino conectados ao teclado

➡ Define os pinos digitais do Arduino conectados às linhas e colunas do teclado matricial.

byte rowPins[rows] = {9, 8, 7, 6};   // Pinos do Arduino conectados às linhas do teclado
byte colPins[cols] = {5, 4, 3, 2};   // Pinos do Arduino conectados às colunas do teclado

Esses arrays unidimensionais informam ao Arduino quais pinos estão ligados às linhas e colunas do teclado.

  • byte: tipo de dado usado para valores entre 0 e 255 (8 bits). Usado aqui por ser mais leve que int, economizando memória.
  • Ao pressionar uma tecla, o teclado matricial gera uma conexão elétrica entre uma linha e uma coluna. A biblioteca Keypad.h faz a varredura combinando linhas e colunas para identificar qual tecla foi pressionada.

Inicialização do LCD I2C e do Teclado

➡ Comandos como LiquidCrystal_I2C e Keypad instanciam objetos baseados nas bibliotecas importadas — são fundamentais no Arduino para trabalhar com hardware de forma mais simples.

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3, POSITIVE);

Keypad teclado = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);

➡ Cria o objeto lcd, informando o endereço I2C (0x27) e os pinos internos do módulo I2C (dependem do modelo).

Instancia-se um objeto lcd da classe LiquidCrystal_I2C. O endereço I2C (0x27) é padrão para muitos displays. Os números seguintes indicam os pinos internos do expansor I2C (PCF8574 ou similar), que se conectam ao LCD (geralmente não precisam ser alterados). O POSITIVE indica que a luz de fundo deve iniciar ligada (backlight)

➡ Cria o objeto teclado que usará a matriz keys, os pinos de linha e coluna, e as dimensões definidas..

Aqui criamos o objeto teclado, usando a função makeKeymap() que converte a matriz keys em um formato interno aceito pela biblioteca. Com os arrays rowPins e colPins, ela sabe como interagir com o hardware.

Variáveis Globais

➡ Define as variáveis globais num1, num2, operação e esperandoSegundoNumero.

String num1 = "";
String num2 = "";
char operacao = '\0';
bool esperandoSegundoNumero = false;
Essas variáveis controlam o estado da calculadora:
String: Guarda números digitados, num1, num2, como texto (facilita a concatenização dos dígitos um após o outro).char: Define que operacao é uma variável do tipo caractere (1 byte, que representa um símbolo ou letra). Em Arduino, String facilita a manipulação de texto, mas consome mais RAM.
  • '\0': É o caractere nulo (chamado de null character) utilizado para . Em C, C++ e também no Arduino, '\0' representa "nada" ou "vazio". A maneira correta para um caractere vazio é sempre usar '\0'.
bool: Variável booleana (verdadeira/falsa) para saber se estamos esperando o segundo número. Definimos que a variável é falsa, ou seja false.Em programação, uma variável global é uma variável que é acessível em todo o escopo de um programa de computadorEla é declarada fora de qualquer função ou bloco de código, o que significa que qualquer parte do código pode lê-la ou modificá-la. 

Void Setup

➡ Vamos analisar o bloco setup() do projeto da mini calculadora com teclado 4x4 e display LCD. Essa função é fundamental no Arduino e é executada uma única vez quando o microcontrolador é ligado ou reiniciado. Ela prepara o ambiente inicial.
void setup() {
  lcd.begin(16,2);                   // Inicializa o LCD (16x2)
  lcd.setBacklight(HIGH);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("-- Calculadora --");
  delay(2000);
  lcd.clear();
}
void setup() {
  • void: Tipo de retorno da função — neste caso, não retorna nada.
  • setup(): Função especial no Arduino que é chamada apenas uma vez, logo após o boot.
  • As instruções dentro das {} são executadas nessa ordem para preparar o sistema.
lcd.begin(16,2);
  • Inicializa o LCD informando o número de colunas (16) e linhas (2).
  • Esta chamada configura o LCD e o driver I2C para que ele possa começar a exibir dados corretamente.
    • O LCD 16x2 tem 16 caracteres por linha e 2 linhas.
    • Essa função geralmente configura o modo de operação 4 bits do LCD via I2C, e depende da biblioteca LiquidCrystal_I2C.
lcd.setBacklight(HIGH);
  • Liga a luz de fundo (backlight) do LCD.
  • HIGH é uma constante que geralmente representa nível lógico 1 ou nível lógico alto (5V).
  • Algumas bibliotecas usam lcd.backlight();, mas esta versão permite controle direto com valores como HIGH ou LOW.
lcd.clear();
  • Limpa completamente o conteúdo da tela do LCD.
  • Também posiciona o cursor no canto superior esquerdo (posição 0,0).
  • Dica: É uma boa prática limpar a tela antes de iniciar uma nova escrita.
lcd.setCursor(0,0);
  • Define onde o texto será impresso.
  • (0, 0) significa coluna 0, linha 0, ou seja, o canto superior esquerdo do LCD.
  • Importante para alinhar corretamente o que será exibido.

lcd.print("-- Calculadora --");

  • Exibe o texto literal na posição atual do cursor.
  • "-- Calculadora --" aparece na primeira linha do LCD.
  • O print() funciona de forma semelhante ao Serial.print(), mas neste caso direcionado ao display.
delay(2000);
  • Pausa o programa por 2000 milissegundos (ou 2 segundos).
  • Usado aqui para dar tempo ao usuário de ler a mensagem inicial.
  • Cuidado: delay() bloqueia completamente o Arduino. Durante esse tempo, nenhuma outra instrução é executada.
  • Para avançados: Em projetos mais complexos, recomenda-se usar millis() para controle de tempo sem bloquear o sistema.
lcd.clear();
  • Limpa novamente a tela, agora que a mensagem inicial foi mostrada.
  • Pronto para iniciar o uso da calculadora sem texto residual.

Void Loop

➡ A função loop() da calculadora com teclado 4x4 e display LCD. Esta função é o coração do programa Arduino — ela é executada repetidamente, milhares de vezes por segundo.
void loop() {
  char tecla = teclado.getKey();

  if(tecla) {
    if(tecla >= '0' && tecla <= '9') { // Se a tecla for número
      if(!esperandoSegundoNumero) {
        num1 += tecla;
      } else {
        num2 += tecla;
      }
      mostrarDisplay();
    }
    else if(tecla == 'A' || tecla == 'B' || tecla == '*' || tecla == '#') { // Operações
      if(num1.length() > 0) {
        esperandoSegundoNumero = true;
        operacao = tecla;
        mostrarDisplay();
      }
    }
    else if(tecla == 'D') { // 'D' usado como botão Igual
      if(num1.length() > 0 && num2.length() > 0 && operacao != '\0') {
        calcular();
      }
    }
    else if(tecla == 'C') { // 'C' usado para limpar
      limpar();
    }
  }
}

void loop() {

  • A função loop() nunca termina enquanto o Arduino estiver ligado.

  • Ideal para leituras de sensores, verificações de estado e respostas a eventos — como pressionar uma tecla, neste caso.

 Captura da tecla pressionada

  • A função getKey() retorna o caractere da tecla pressionada no momento, ou NO_KEY (valor especial da biblioteca Keypad.h) se nenhuma tecla foi pressionada.

  • O resultado é armazenado em uma variável do tipo char, chamada tecla.

Verificação se houve tecla pressionada

  • if(tecla) {
    •  Em C++, um char com valor diferente de '\0' (caractere nulo) é considerado true.
    • Isso evita processar o resto do código quando nenhuma tecla foi pressionada.

Comparação de caracteres: '0' a '9' são tratados como seus valores ASCII.

  • num1 ou num2 (tipo String) recebe o caractere pressionado. O operador += concatena o caractere ao final da string existente.

  • esperandoSegundoNumero determina se estamos digitando o primeiro ou o segundo número.

  • mostrarDisplay() exibe num1, num2 e o operador no LCD (função implementada fora deste trecho).

    Aqui é feita uma entrada de números caractere por caractere, como uma calculadora real. O número completo é montado dinamicamente como string.

Se a tecla for uma operação

  • 'A', 'B', '*', '#' representam, respectivamente: subtração, adição, multiplicação e divisão.

  • Só permite definir a operação se já digitamos um número antes (num1.length() > 0).

    • A propriedade length representa um inteiro de 32-bit sem sinal, que especifíca o número de elementos em um array.
  • Define a variável operacao com a tecla pressionada.

  • Ativa o modo esperandoSegundoNumero, ou seja, os próximos dígitos irão para num2.

Importante: Esse controle evita que alguém pressione uma operação antes de digitar o primeiro número.

Se a tecla for ‘D’ (igual)

  • 'D' é interpretado como a tecla igual (=).

  • A função calcular() só será chamada se:

    • num1 e num2 tiverem sido digitados;

    • operacao já estiver definida (diferente de '\0', o caractere nulo).

Se a tecla for ‘C’ (limpar)

  • Pressionar 'C' ativa a função limpar(), que deve zerar todas as variáveis (num1, num2, operacao, etc.) e limpar o LCD.

  • Permite começar um novo cálculo do zero.

Função mostrarDisplay()

➡ Vamos analisar linha por linha a função mostrarDisplay(), que é responsável por atualizar o conteúdo do display LCD 16x2 da mini calculadora. O objetivo principal dela é exibir o número digitado (num1), a operação matemática (caso definida) e o segundo número (num2), tudo em uma única linha da tela.

void mostrarDisplay() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(num1);
  if(operacao != '\0') {
    lcd.print(" ");
    lcd.print(simboloOperacao(operacao));
    lcd.print(" ");
  }
  lcd.print(num2);
}

 void mostrarDisplay() {

  • Declaração de uma função chamada mostrarDisplay.

  • void indica que ela não retorna nenhum valor.

  • É chamada sempre que se deseja atualizar a tela com a equação parcial ou completa.

lcd.clear();
  • Limpa completamente a tela LCD antes de escrever qualquer coisa.

  • Evita que resíduos da última operação permaneçam visíveis.

  • Também reseta o cursor para a posição inicial (0,0).

lcd.setCursor(0,0);

  • Move o cursor para a coluna 0, linha 0, o início da primeira linha do display.

  • Garante que o conteúdo sempre será exibido a partir do canto superior esquerdo.

lcd.print(num1);

  • Imprime o valor contido na variável num1, que representa o primeiro número digitado.

  • A variável num1 é do tipo String, o que permite acumular dígitos ao longo do tempo com +=.

if(operacao != '\0') {

  • Verifica se uma operação foi escolhida.

  • operacao é do tipo char, e '\0' (caractere nulo) é usado como valor padrão para indicar que nenhuma operação foi selecionada ainda.

  • Se operacao for diferente de '\0', então uma operação já foi digitada (como '+', '-', etc).

Dentro do if:

lcd.print(" ");

  • Imprime um espaço em branco para separar num1 do símbolo da operação, melhorando a legibilidade.

lcd.print(simboloOperacao(operacao));

  • Chama a função simboloOperacao(), passando o char armazenado em operacao.

  • Essa função retorna o símbolo correspondente (por exemplo, se operacao == 'B', retorna '+').

  • Esse passo converte os códigos do teclado ('A', 'B', '*', '#') em símbolos tradicionais de operação matemática.

lcd.print(" ");

  • Outro espaço em branco, agora separando a operação do segundo número.

lcd.print(num2);
  • Por fim, imprime o segundo número digitado (num2), se houver.

  • num2 também é do tipo String, e inicialmente estará vazio até que o usuário digite mais números depois da operação.

Função void calcular()

➡ Vamos detalhar a função calcular() linha por linha, explicando a lógica C++, os conceitos usados e particularidades úteis para iniciantes e usuários avançados. Essa função é chamada quando o usuário pressiona a tecla 'D', que representa o sinal de igual (=) na mini calculadora.

void calcular() {
  float n1 = num1.toFloat();
  float n2 = num2.toFloat();
  float resultado = 0;

  switch(operacao) {
    case 'B': // Soma
      resultado = n1 + n2;
      break;
    case 'A': // Subtração
      resultado = n1 - n2;
      break;
    case '*': // Multiplicação
      resultado = n1 * n2;
      break;
    case '#': // Divisão
      if(n2 != 0) {
        resultado = n1 / n2;
      } else {
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Erro: Div/0");
        delay(2000);
        limpar();
        return;
      }
      break;
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Resultado:");
  lcd.setCursor(0,1);
  lcd.print(resultado);
  
  delay(3000); // Espera 3 segundos para exibir o resultado
  limpar();
}

void calcular() {

  • Declara a função chamada calcular, que não retorna valor (void).

  • Responsável por realizar a operação aritmética com os dois operandos já digitados.

float n1 = num1.toFloat();
  • Converte o conteúdo da String num1 para um número float (ponto flutuante).

  • toFloat() é um método da classe String no Arduino que converte texto para número decimal.

    • Exemplo: Se num1 == "12.5", então n1 == 12.5.

float n2 = num2.toFloat();
  • Faz o mesmo para o segundo número.

    • Exemplo: Se num2 == "3.0", então n2 == 3.0.

float resultado = 0;
  • Declara uma variável resultado do tipo float, inicializada em zero.

  • Guardará o resultado da operação a ser exibida no display.

switch(operacao) {
  • Inicia uma estrutura switch para verificar qual operação foi selecionada.

  • switch é mais eficiente que múltiplos if...else quando há várias comparações simples com char ou int.

case 'B': // Soma
  • Se o usuário pressionou a tecla 'B', representa adição (+).

  • Realiza resultado = n1 + n2;.

case 'A': // Subtração
  • Se a operação for 'A', representa subtração (-).

  • Executa a operação n1 - n2.

case '*': // Multiplicação
  • Se a tecla for '*', representa multiplicação.

  • Executa resultado = n1 * n2;.

case '#': // Divisão
  • '#' representa divisão.

  • Antes de dividir, é necessário verificar se n2 é diferente de zero, para evitar erro de divisão por zero.

if(n2 != 0) { resultado = n1 / n2; }
  • Se o divisor (n2) for válido, realiza a divisão.

else { ... }
  • Se o divisor for zero, a função trata o erro com elegância:

lcd.clear();

  • Limpa a tela.

lcd.setCursor(0,0); lcd.print("Erro: Div/0");

  • Exibe uma mensagem de erro: "Erro: Div/0".

delay(2000);

  • Aguarda 2 segundos para que o usuário leia a mensagem.

limpar(); return;

  • Chama a função limpar() para reiniciar a calculadora.

  • return; interrompe a função e não mostra nenhum resultado inválido.

EXIBIÇÃO DO RESULTADO
lcd.clear();
  • Limpa o display antes de mostrar o resultado final.

lcd.setCursor(0,0); lcd.print("Resultado:");
  • Na linha 0, escreve a palavra “Resultado:”.

lcd.setCursor(0,1); lcd.print(resultado);
  • Na linha 1, imprime o número resultante do cálculo.

delay(3000);
  • Aguarda 3 segundos para que o usuário veja o valor.

limpar();
  • Ao fim, chama a função limpar() que:

    • Reseta os números (num1, num2)

    • Reseta o operador

    • Limpa o display novamente

  • Deixa a calculadora pronta para novo cálculo.

função limpar()

➡ Vamos analisar detalhadamente a função limpar() linha por linha, com foco didático e técnico nas instruções C++, lógica envolvida e suas particularidades — ideal para iniciantes e avançados que estão aprendendo a estruturar programas no Arduino.

void limpar() {
  num1 = "";
  num2 = "";
  operacao = '\0';
  esperandoSegundoNumero = false;
  lcd.clear();
}

void limpar() {

  • Declara uma função do tipo void, ou seja, não retorna nenhum valor.

  • limpar() é usada para resetar o estado interno da calculadora, preparando-a para uma nova operação.

  • Muito útil após um cálculo ou quando o usuário pressiona a tecla 'C' (limpar).

num1 = "";
  • A variável num1 (do tipo String) armazena o primeiro número digitado.

  • Ao atribuir uma string vazia, apagamos seu conteúdo.

  • Isso garante que, ao iniciar um novo cálculo, o primeiro número comece do zero.

num2 = "";
  • Mesma ideia que a linha anterior, mas para o segundo número da operação.

  • Isso evita que valores antigos interfiram no próximo cálculo.

operacao = '\0';
  • operacao é uma variável do tipo char (caractere), usada para armazenar o operador escolhido ('A', 'B', '*', ou '#').

  • '\0' é o caractere nulo — equivalente a dizer “nenhum operador definido”.

  • É uma prática comum para indicar que a variável está “em branco” ou “sem valor”.

esperandoSegundoNumero = false;
  • Essa variável booleana (bool) controla o estado da calculadora.

  • Quando false, significa que ainda estamos digitando o primeiro número.

  • Quando true, indica que o operador já foi escolhido e agora esperamos o segundo número.

  • Aqui, definimos como false para reiniciar corretamente o fluxo lógico da aplicação.

lcd.clear();
  • Chama o método clear() da biblioteca LiquidCrystal_I2C para apagar todo o conteúdo do display LCD.

  • Muito importante para o usuário ter um feedback visual de que tudo foi zerado.

  • Deixa a tela pronta para exibir a próxima sequência de entrada.

função simboloOperacao(char op)

➡ Vamos detalhar linha por linha a função simboloOperacao(char op) de maneira didática e técnica, explicando cada instrução C++, a lógica aplicada e suas particularidades — voltado tanto para iniciantes quanto para usuários avançados.

char simboloOperacao(char op) {
  switch(op) {
    case 'B': return '+';
    case 'A': return '-';
    case '*': return 'x';
    case '#': return '/';
    default: return ' ';
  }
}

char simboloOperacao(char op) {

  • Declara uma função chamada simboloOperacao.

  • Retorna um char (caractere), ou seja, a função devolve um único símbolo como '+', '-', etc.

  • Recebe como parâmetro op — um caractere que representa a operação escolhida no teclado:

    • 'A' → subtração

    • 'B' → adição

    • '*' → multiplicação

    • '#' → divisão

Essa função serve para converter códigos internos do teclado (como 'A', 'B', etc.) em símbolos matemáticos compreensíveis para o usuário.

switch(op) {
  • O comando switch é uma estrutura de controle muito comum em C/C++, ideal para testar múltiplos valores de uma variável.

  • Ele compara o valor de op com diversos case e executa o bloco correspondente.

case 'B': return '+';
  • Se op for igual a 'B' (tecla do teclado atribuída à soma), a função retorna o caractere '+'.

  • O return encerra imediatamente a função com esse valor.

case 'A': return '-';
  • Se op for 'A', retorna '-', representando subtração.

  • Lembrando: o layout do teclado foi definido de forma personalizada, então 'A' = subtração e 'B' = adição.

case '*': return 'x';
  • Se op for '*', retorna 'x', indicando multiplicação.

  • A escolha por 'x' ao invés de '*' é estética — o caractere 'x' é mais claro para o usuário comum em um LCD.

case '#': return '/';
  • Se op for '#', retorna '/', que é o símbolo da divisão.

default: return ' ';
  • O bloco default é executado quando nenhum dos case anteriores corresponde ao valor de op.

  • Aqui, retorna um espaço em branco ' ' como valor padrão, útil para evitar erros ou exibições incorretas caso op esteja inválido.

Conclusão

Este projeto simula o funcionamento de uma calculadora básica utilizando recursos simples do Arduino, combinando o uso de teclado matricial, display LCD com I2C e controle lógico com programação estruturada. É ideal para quem está aprendendo eletrônica e lógica de programação com Arduino.

O anúncio abaixo ajuda a manter o Squids Arduino funcionando

Comentários

×

Infomações do site / SEO








×

Adicionar Marcadores