// Inclua as bibliotecas necessárias
#define F_CPU 16000000 // Define a frequência do clock para 16 MHz
#include <avr/io.h> // Inclui o header para o hardware de I/O
#include <util/delay.h> // Inclui o header para as funções de delay
#include <stdlib.h> // Header para as funções atof e dtostrf
#define RS PB1 // Define o pino PB1 como RS (Register Select) do LCD
#define E PB0 // Define o pino PB0 como E (Enable) do LCD
#define DEBOUNCE_TIME 10 // Tempo de debounce em milissegundos
// Variável global para alternar entre as telas
int telaAtual = 1;
// Variáveis globais
float temperatura = 25.0; //Armazena o valor medido da temperatura
float nivel = 50.0; //Armazena o valor medido do nível
float aj_temperatura = 0.0; //Armazena valor de ajuste da temperatura (em °C)
float hist_temperatura = 0.0; ////Armazena valor da histerese temperatura (em °C)
float aj_nivel = 0.0; //Armazena valor de ajuste do nível (em %)
float hist_nivel = 0.0; //Armazena valor da histerese do nível (em %)
float temperatura_medida = 0.0; //Armazena o valor da temperatura medida
float nivel_medido = 0.0; // Armazena o valor do nível medido
// Variáveis globais para o status das bombas e da resistência
int statusBomba1 = 0; // 0 para OFF, 1 para ON
int statusBomba2 = 0; // 0 para OFF, 1 para ON
int statusResistencia = 0; // 0 para OFF, 1 para ON
char simboloGrau = 0xDF;
//-----------------------------------------------------------------//
// Funções do LCD (pulso_E, envia_dados, Lcd_cmd, Lcd_out, Lcd_init)
//-----------------------------------------------------------------//
// Função para gerar um pulso no pino E, sinalizando ao LCD para ler os dados
void pulso_E() {
PORTB &= ~(1 << E); // Coloca o pino E em nível baixo
PORTB |= (1 << E); // Coloca o pino E em nível alto
PORTB &= ~(1 << E); // Coloca o pino E em nível baixo novamente
return;
}
// Função para enviar comandos ou dados para o LCD
void envia_dados(unsigned char comando) {
PORTD = ((comando & 0xF0) | (PORTD & 0x0F)); // Envia os 4 bits mais significativos
pulso_E(); // Gera um pulso no E para processar os dados
PORTD = (((comando << 4) & 0xF0) | (PORTD & 0x0F)); // Envia os 4 bits menos significativos
pulso_E(); // Gera um pulso no E para processar os dados
return;
}
// Função para enviar comandos para o LCD
void Lcd_cmd(unsigned char comando) {
_delay_ms(1); // Pequeno delay para garantir a execução do comando
PORTB &= ~(1 << RS); // Coloca o RS em nível baixo para indicar um comando
envia_dados(comando); // Envia o comando para o LCD
return;
}
// Função para posicionar o cursor e enviar texto para o LCD
void Lcd_out(char linha_lcd, char coluna_lcd, char *ponteiro) {
// Verifica se a linha e coluna estão dentro dos limites do LCD
if ((linha_lcd > 0) && (linha_lcd < 3) && (coluna_lcd > 0) && (coluna_lcd < 41)) {
Lcd_cmd(128 + (coluna_lcd - 1) + ((linha_lcd - 1) * 64)); // Calcula a posição do cursor
while (*ponteiro) {
_delay_ms(1); // Delay para processamento de cada caractere
PORTB |= (1 << RS); // Coloca RS em nível alto para indicar dados (caractere)
envia_dados(*ponteiro++); // Envia o caractere para o LCD e incrementa o ponteiro
}
}
return;
}
// Função para inicializar o LCD
void Lcd_init() {
// Configura os pinos do LCD como saída e inicializa o LCD em modo 4 bits
DDRB |= ((1 << E) + (1 << RS));
DDRD |= ((1 << PD4) + (1 << PD5) + (1 << PD6) + (1 << PD7));
PORTB &= ~(1 << RS);
_delay_ms(15);
PORTD = ((0x30 & 0xF0) | (PORTD & 0x0F));
pulso_E();
_delay_ms(5);
pulso_E();
_delay_ms(1);
pulso_E();
_delay_ms(1);
PORTD = ((0x20 & 0xF0) | (PORTD & 0x0F));
pulso_E();
Lcd_cmd(0x28);
Lcd_cmd(0x08);
Lcd_cmd(0x01);
Lcd_cmd(0x06);
Lcd_cmd(0x0C);
}
//-----------------------------------------------------------------//
//****************INÍCIO DO MAPEAMENTO DO TECLADO******************//
//-----------------------------------------------------------------//
// Mapeamento dos caracteres do teclado matricial
char keymap[4][4] = {
// Define o layout do teclado matricial
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// Função para ler uma tecla do teclado matricial
char ler_teclado() {
char teclaPressionada = 0;
for (uint8_t linha = 0; linha < 4; linha++) {
PORTC = ~(1 << linha);
_delay_ms(1); // Estabilização do sinal
for (uint8_t coluna = 0; coluna < 4; coluna++) {
if (!(PINB & (1 << (coluna + 2)))) {
_delay_ms(30); // Debouncing - tempo de espera maior
if (!(PINB & (1 << (coluna + 2)))) {
teclaPressionada = keymap[linha][coluna];
// Espera até que a tecla seja liberada
while (!(PINB & (1 << (coluna + 2))));
_delay_ms(30); // Debouncing adicional após soltar a tecla
return teclaPressionada;
}
}
}
}
return 0;
}
// Função para inicializar os pinos do teclado
void init_teclado() {
// Configura os pinos das linhas como saída e das colunas como entrada
// Ativa os resistores de pull-up para as colunas
DDRC |= 0x0F;
PORTC |= 0x0F;
DDRB &= 0xC3;
PORTB |= 0x3C;
}
// Função modificada para ler um número com vírgula e Enter
void ler_numero(float *numero) {
char AUX[9] = {0}; // Inicializa a string com zeros
int COUNT = 0;
Lcd_out(2, 1, " "); // Limpa a segunda linha
while (1) {
Lcd_out(2, 1, AUX); // Atualiza o número na segunda linha
char tecla = ler_teclado();
if (tecla == '*') { // Trata '*' como vírgula
AUX[COUNT++] = '.';
} else if (tecla == '#') { // Trata '#' como Enter
break;
} else if (tecla >= '0' && tecla <= '9') { // Verifica se é um dígito
AUX[COUNT++] = tecla;
}
if (COUNT >= 8) break; // Limita o número de dígitos
AUX[COUNT] = '\0'; // Garante que a string está sempre terminada
Lcd_out(2, 1, AUX); // Atualiza o LCD com a string atualizada
}
*numero = atof(AUX); // Converte a string para float
}
//----------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------//
// Enumeração para os estados do menu
typedef enum {
MENU_INICIAL,
MENU_AJUSTES,
CASE_A,
CASE_B,
CASE_C,
CASE_D,
CASE_B1,
CASE_B2,
CASE_RES
} EstadoMenu;
EstadoMenu estadoAtual = MENU_INICIAL;
// Protótipos das funções do menu
void mostraMenuInicial();
void mostraMenuAjustes();
void caseA();
void caseB();
void caseC();
void caseD();
void caseB1();
void caseB2();
int main(void) {
// Inicializações
Lcd_init();
init_teclado();
while (1) {
switch (estadoAtual) {
case MENU_INICIAL:
mostraMenuInicial();
break;
case MENU_AJUSTES:
mostraMenuAjustes();
break;
case CASE_A:
caseA();
break;
case CASE_B:
caseB();
break;
case CASE_C:
caseC();
break;
case CASE_D:
caseD();
break;
case CASE_B1:
caseB1();
break;
case CASE_B2:
caseB2();
break;
}
// Pode remover esta parte se a leitura do teclado já estiver dentro das funções do menu
_delay_ms(5);
char tecla = ler_teclado();
if (tecla == '#') {
estadoAtual = MENU_AJUSTES;
}
// Restante do seu loop...
}
}
void mostraMenuInicial() {
Lcd_cmd(0x01); // Limpa o display
switch (telaAtual) {
case 1:
// Tela 1: Exibe informações de temperatura e nível
char tempString[20], nivelString[20];
char bufferTemp[40], bufferNivel[40];
// Formata a temperatura
dtostrf(temperatura, 5, 1, tempString);
sprintf(bufferTemp, "Temp: %s%cC", tempString, simboloGrau);
// Formata o nível
dtostrf(nivel, 5, 1, nivelString);
sprintf(bufferNivel, "Nivel: %s%%", nivelString);
// Exibe no LCD
Lcd_out(1, 1, bufferTemp);
Lcd_out(2, 1, bufferNivel);
break;
case 2:
// Tela 2: Exibe status das bombas e resistência
char statusB1[20], statusB2[20], statusResist[20];
// Preparar strings com o status das bombas
sprintf(statusB1, "B1:%s", statusBomba1 ? "ON" : "OFF");
sprintf(statusB2, "B2:%s", statusBomba2 ? "ON" : "OFF");
// Preparar string com o status da resistência
sprintf(statusResist, "RESIST: %s", statusResistencia ? "ON" : "OFF");
// Exibir status no LCD
Lcd_cmd(0x01); // Limpa o display
Lcd_out(1, 1, statusB1);
Lcd_out(1, 11, statusB2); // Ajuste a posição conforme necessário
Lcd_out(2, 1, statusResist);
break;
case 3:
// Tela 3: Exibe status e histerese de temperatura e nível
char aj_tempString[20], hist_tempString[20];
char aj_nivelString[20], hist_nivelString[20];
char bufferAjTemp[40], bufferAjNivel[40];
// Formata a temperatura e sua histerese
dtostrf(aj_temperatura, 2, 0, aj_tempString);
dtostrf(hist_temperatura, 2, 0, hist_tempString);
sprintf(bufferAjTemp, "SET:%s%cC H:%s%cC", aj_tempString, simboloGrau, hist_tempString, simboloGrau);
// Formata o nível e sua histerese
dtostrf(aj_nivel, 2, 0, aj_nivelString);
dtostrf(hist_nivel, 2, 0, hist_nivelString);
sprintf(bufferAjNivel, "SET:%s%% H:%s%%", aj_nivelString, hist_nivelString);
// Exibe no LCD
Lcd_out(1, 1, bufferAjTemp);
Lcd_out(2, 1, bufferAjNivel);
break;
case 4:
// Tela 4: Instrução para entrar no menu de configuração
Lcd_out(1, 1, "PRESSIONE #");
Lcd_out(2, 1, "PARA SETUP");
break;
}
// Incrementa telaAtual e reseta se exceder 4
telaAtual++;
if (telaAtual > 4) {
telaAtual = 1;
}
_delay_ms(5000); // Delay para a alternância entre as telas
}
void mostraMenuAjustes() {
Lcd_cmd(0x01); // Limpa o display
Lcd_out(1, 1, "A:TEMPER B:NIVEL");
Lcd_out(2, 1, "C:BOMBAS D:RESIS");
while (1) {
char tecla = ler_teclado();
if (tecla >= 'A' && tecla <= 'D') {
switch (tecla) {
case 'A':
estadoAtual = CASE_A;
break;
case 'B':
estadoAtual = CASE_B;
break;
case 'C':
estadoAtual = CASE_C;
break;
case 'D':
estadoAtual = CASE_D;
break;
}
break; // Sai do loop quando uma tecla válida é pressionada
} else if (tecla == '*') {
estadoAtual = MENU_INICIAL; // Retorna à tela inicial quando "*" é pressionado
break; // Sai do loop quando "*" é pressionado
}
}
}
void caseA() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Ajuste Temp.");
float temp;
ler_numero(&temp); // Lê o número inserido pelo usuário e armazena em 'temp'
if (temp >= 0.0 && temp <= 80.0) {
aj_temperatura = temp; // Atualiza a variável global com o valor lido
Lcd_cmd(0x01);
char tempString[10];
dtostrf(aj_temperatura, 5, 2, tempString);
char buffer[20];
sprintf(buffer, "%s oC", tempString);
Lcd_out(1, 1, "Temp. Atualizada");
Lcd_out(2, 1, buffer);
_delay_ms(2000);
// Ajuste de Histerese
Lcd_cmd(0x01);
Lcd_out(1, 1, "Ajuste Histerese");
float histTemp;
ler_numero(&histTemp); // Lê o número inserido pelo usuário para histerese
if (histTemp >= 0.0 && histTemp <= 10.0) {
hist_temperatura = histTemp; // Atualiza a variável global com o valor lido
Lcd_cmd(0x01);
char histTempString[10];
dtostrf(hist_temperatura, 5, 2, histTempString);
sprintf(buffer, "%s oC", histTempString);
Lcd_out(1, 1, "Hist. Atualizada");
Lcd_out(2, 1, buffer);
_delay_ms(2000);
} else {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Valor Invalido");
_delay_ms(2000); // Espera por 2 segundos
}
} else {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Valor Invalido");
_delay_ms(2000); // Espera por 2 segundos
}
estadoAtual = MENU_AJUSTES; // Retorna ao menu de ajustes
}
void caseB() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Ajuste Nivel");
float nivel_;
ler_numero(&nivel_); // Lê o número inserido pelo usuário e armazena em 'nivel_'
if (nivel_ >= 0.0 && nivel_ <= 100.0) {
aj_nivel = nivel_;
Lcd_cmd(0x01);
char nivelString[10];
dtostrf(aj_nivel, 5, 2, nivelString);
char buffer[20];
sprintf(buffer, "%s %%", nivelString);
Lcd_out(1, 1, "Nivel. Atualizado");
Lcd_out(2, 1, buffer);
_delay_ms(2000);
//Ajuste da Histerese
Lcd_cmd(0x01);
Lcd_out(1, 1, "Ajuste Histerese");
float histNivel;
ler_numero (&histNivel);
if (histNivel >= 0.0 && histNivel <= 10.0) {
hist_nivel = histNivel; // Atualiza a variável global com o valor lido
Lcd_cmd(0x01);
char histNivelString[10];
dtostrf(hist_nivel, 5, 2, histNivelString);
sprintf(buffer, "%s%%", histNivelString);
Lcd_out(1, 1, "Hist. Atualizada");
Lcd_out(2, 1, buffer);
_delay_ms(2000);
} else {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Valor Invalido");
_delay_ms(2000); // Espera por 2 segundos
}
} else {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Valor Invalido");
_delay_ms(2000);
}
estadoAtual = MENU_AJUSTES;
}
void caseC() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "Status Bombas");
Lcd_out(2, 1, "1:B1 2:B2");
while (1) {
char tecla = ler_teclado();
if (tecla >= '1' && tecla <= '2') {
switch (tecla) {
case '1':
estadoAtual = CASE_B1;
break;
case '2':
estadoAtual = CASE_B2;
break;
}
break; // Sai do loop quando uma tecla válida é pressionada
} else if (tecla == '*') {
estadoAtual = MENU_INICIAL; // Retorna à tela inicial quando "*" é pressionado
break; // Sai do loop quando "*" é pressionado
}
}
// Implementação de teste
}
void caseD() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "STATUS RESIST.");
Lcd_out(2, 1, "1:ON 2:OFF");
while (1) {
char tecla = ler_teclado();
if (tecla == '1' || tecla == '2') {
statusResistencia = (tecla == '1'); // Atualiza para ON se '1', OFF se '2'
Lcd_cmd(0x01);
char statusMsg[20];
sprintf(statusMsg, "RESIST: %s", statusResistencia ? "ON" : "OFF");
Lcd_out(1, 1, statusMsg); // Exibe "RESIST.: ON" ou "RESIST: OFF"
_delay_ms(2000); // Tempo para o usuário ler a mensagem
break; // Sai do loop após a exibição da mensagem
} else if (tecla == '*') {
break; // Sai do loop e retorna ao menu anterior
}
}
estadoAtual = MENU_AJUSTES; // Retorna ao menu de ajustes
}
void caseB1() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "STATUS BOMBA 1");
Lcd_out(2, 1, "1:ON 2:OFF");
while (1) {
char tecla = ler_teclado();
if (tecla == '1' || tecla == '2') {
statusBomba1 = (tecla == '1'); // Atualiza para ON se '1', OFF se '2'
Lcd_cmd(0x01);
char statusMsg[20];
sprintf(statusMsg, "BOMBA 1: %s", statusBomba1 ? "ON" : "OFF");
Lcd_out(1, 1, statusMsg); // Exibe "BOMBA 1: ON" ou "BOMBA 1: OFF"
_delay_ms(2000); // Tempo para o usuário ler a mensagem
break; // Sai do loop após a exibição da mensagem
} else if (tecla == '*') {
break; // Sai do loop e retorna ao menu anterior
}
}
estadoAtual = MENU_AJUSTES; // Retorna ao menu de ajustes
}
void caseB2() {
Lcd_cmd(0x01);
Lcd_out(1, 1, "STATUS BOMBA 2");
Lcd_out(2, 1, "1:ON 2:OFF");
while (1) {
char tecla = ler_teclado();
if (tecla == '1' || tecla == '2') {
statusBomba2 = (tecla == '1'); // Atualiza para ON se '1', OFF se '2'
Lcd_cmd(0x01);
char statusMsg[20];
sprintf(statusMsg, "BOMBA 2: %s", statusBomba2 ? "ON" : "OFF");
Lcd_out(1, 1, statusMsg); // Exibe "BOMBA 2: ON" ou "BOMBA 2: OFF"
_delay_ms(2000); // Tempo para o usuário ler a mensagem
break; // Sai do loop após a exibição da mensagem
} else if (tecla == '*') {
break; // Sai do loop e retorna ao menu anterior
}
}
estadoAtual = MENU_AJUSTES; // Retorna ao menu de ajustes
}
// Resto do código...