// Artur Freire Camargo e Danillo Messa Kubit
// Codigo do projeto final de Sistemas Embarcados I
#include <Arduino.h>
// PINOS DO HARDWARE
// LCD
#define LCD_RS 3
#define LCD_E 8
#define LCD_D4 18
#define LCD_D5 17
#define LCD_D6 16
#define LCD_D7 15
#define LCD_BACKLIGHT 4
// Teclado 4x3
#define KEY_C1 41
#define KEY_C2 40
#define KEY_C3 39
#define KEY_R1 35
#define KEY_R2 37
#define KEY_R3 36
#define KEY_R4 38
// Sensor de luz e saída da tranca
#define LDR_PIN 9
#define TRANCA_PIN 42
// ESTADOS DO SISTEMA
// Cada tela do sistema é representada por um estado
enum State {
TELA_ESPERA,
SELECIONA_ACESSO,
ENTRADA_SENHA,
MENU_ADMIN,
CFG_USER,
CFG_NOVA_SENHA,
CFG_CFR_ADM,
CFG_DEL_USER,
CFG_TEMPO
};
State currentState = TELA_ESPERA;
// VARIÁVEIS PRINCIPAIS
// Senha do administrador (padrão 1234)
String adminPass = "1234";
// Vetor com até 5 senhas de usuários
String userPasses[5] = {"", "", "", "", ""};
// Guarda o que o usuário está digitando
String inputBuffer = "";
// Guarda temporariamente a nova senha do admin para confirmar depois
String tempNewPass = "";
// Tempo que a tranca fica aberta (em milissegundos)
int trancaDuration = 3000;
// Indica se o acesso foi liberado
bool accessGranted = false;
// Índice do usuário que está sendo configurado
int targetUserIndex = 0;
// Indica se o login atual é de administrador
bool isAdminLogin = false;
// ---------------- FUNÇÕES DO LCD ----------------
// Gera o pulso no pino Enable
void pulseEnable() {
digitalWrite(LCD_E, HIGH);
delayMicroseconds(1);
digitalWrite(LCD_E, LOW);
delayMicroseconds(100);
}
// Envia meio byte (4 bits) para o LCD
void sendNibble(uint8_t nibble) {
digitalWrite(LCD_D4, (nibble >> 0) & 1);
digitalWrite(LCD_D5, (nibble >> 1) & 1);
digitalWrite(LCD_D6, (nibble >> 2) & 1);
digitalWrite(LCD_D7, (nibble >> 3) & 1);
pulseEnable();
}
// Envia comando para o LCD
void lcdCommand(uint8_t cmd) {
digitalWrite(LCD_RS, LOW);
sendNibble(cmd >> 4);
sendNibble(cmd & 0x0F);
delay(2);
}
// Escreve um caractere no LCD
void lcdWrite(uint8_t data) {
digitalWrite(LCD_RS, HIGH);
sendNibble((data >> 4) & 0x0F);
sendNibble(data & 0x0F);
}
// Escreve um texto inteiro
void lcdPrint(const char* str) {
while (*str) lcdWrite(*str++);
}
// Define posição do cursor
void lcdSetCursor(uint8_t col, uint8_t row) {
uint8_t addr = (row == 0 ? 0x00 : 0x40) + col;
lcdCommand(0x80 | addr);
}
// Inicializa o LCD em modo 4 bits (nibble)
void lcdInit() {
pinMode(LCD_RS, OUTPUT);
pinMode(LCD_E, OUTPUT);
pinMode(LCD_D4, OUTPUT);
pinMode(LCD_D5, OUTPUT);
pinMode(LCD_D6, OUTPUT);
pinMode(LCD_D7, OUTPUT);
delay(50);
sendNibble(0x03); delay(5);
sendNibble(0x03); delay(5);
sendNibble(0x03); delay(5);
sendNibble(0x02);
lcdCommand(0x28);
lcdCommand(0x0C);
lcdCommand(0x06);
lcdCommand(0x01);
}
// CONTROLE DO BRILHO
// Ajusta automaticamente o brilho do LCD com base na luz ambiente
void updateBacklight() {
int ldrRaw = analogRead(LDR_PIN);
int brightness = map(ldrRaw, 0, 4095, 10, 255);
brightness = constrain(brightness, 10, 255);
analogWrite(LCD_BACKLIGHT, brightness);
}
// LEITURA DO TECLADO
// Faz a varredura das colunas e linhas para descobrir qual tecla foi pressionada, fazendo a ativaçao das colunas primeiro iguaal pede no projeto
char scanKeypad() {
const char keys[4][3] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
int cols[3] = {KEY_C1, KEY_C2, KEY_C3};
int rows[4] = {KEY_R1, KEY_R2, KEY_R3, KEY_R4};
digitalWrite(KEY_C1, LOW);
digitalWrite(KEY_C2, LOW);
digitalWrite(KEY_C3, LOW);
for (int c = 0; c < 3; c++) {
digitalWrite(cols[c], HIGH);
for (int r = 0; r < 4; r++) {
if (digitalRead(rows[r]) == HIGH) {
delay(40); // pequeno debounce
if (digitalRead(rows[r]) == HIGH) {
while (digitalRead(rows[r]) == HIGH);
digitalWrite(cols[c], LOW);
return keys[r][c];
}
}
}
digitalWrite(cols[c], LOW);
}
return '\0';
}
// TROCA DE TELAS
// Sempre que o estado muda, a tela é limpa e redesenhada, fluxo de estados
void changeState(State newState) {
currentState = newState;
lcdCommand(0x01);
inputBuffer = "";
switch(currentState) {
case TELA_ESPERA:
lcdPrint("SISTEMA PRONTO");
lcdSetCursor(0,1);
lcdPrint("PRESSIONE TECLA");
break;
case SELECIONA_ACESSO:
lcdPrint("1:Usuario");
lcdSetCursor(0,1);
lcdPrint("2:Administrador");
break;
case ENTRADA_SENHA:
if(isAdminLogin) lcdPrint("SENHA ADMIN:");
else lcdPrint("SENHA USUARIO:");
lcdSetCursor(0,1);
break;
case MENU_ADMIN:
lcdPrint("1:User 2:Del");
lcdSetCursor(0,1);
lcdPrint("3:Adm 4:Tempo");
break;
case CFG_USER:
lcdPrint("EDITAR U(1-5):");
break;
case CFG_DEL_USER:
lcdPrint("APAGAR U(1-5):");
break;
case CFG_TEMPO:
lcdPrint("TEMPO (S):");
lcdSetCursor(0,1);
break;
case CFG_CFR_ADM:
lcdPrint("CONFIRME SENHA:");
lcdSetCursor(0,1);
break;
default: break;
}
}
// CONTROLE DE ACESSO
// Libera ou nega a tranca dependendo da validação da senha
void handleAccess() {
lcdCommand(0x01);
if (accessGranted) {
lcdPrint("ACESSO LIBERADO");
digitalWrite(TRANCA_PIN, HIGH);
delay(trancaDuration);
} else {
lcdPrint("ACESSO NEGADO");
delay(1500);
}
digitalWrite(TRANCA_PIN, LOW);
changeState(TELA_ESPERA);
}
// PROCESSAMENTO DAS TECLAS
// Aqui acontece toda a lógica principal do sistema, é a implementaçao geral da maquina de estados
void goBack() {
switch (currentState) {
case SELECIONA_ACESSO:
case ENTRADA_SENHA:
changeState(TELA_ESPERA);
break;
case MENU_ADMIN:
// Se estiver no menu principal do admin, o '*' faz logout
changeState(TELA_ESPERA);
break;
case CFG_USER:
case CFG_DEL_USER:
case CFG_NOVA_SENHA:
case CFG_TEMPO:
// Se estiver em qualquer sub-menu de config, volta para o menu admin
changeState(MENU_ADMIN);
break;
case CFG_CFR_ADM:
// Se errar ou quiser desistir da confirmação, volta para digitar a nova senha
changeState(CFG_NOVA_SENHA);
break;
default:
changeState(TELA_ESPERA);
break;
}
}
void processKey(char key) {
if (key == '*' && currentState != TELA_ESPERA) {
goBack();
return;
}
// -----------------------
if (currentState == TELA_ESPERA) {
changeState(SELECIONA_ACESSO);
return;
}
if (currentState == SELECIONA_ACESSO) {
if (key == '1') {
isAdminLogin = false;
changeState(ENTRADA_SENHA);
}
else if (key == '2') {
isAdminLogin = true;
changeState(ENTRADA_SENHA);
}
return;
}
if (currentState == ENTRADA_SENHA) {
if (key >= '0' && key <= '9' && inputBuffer.length() < 4) {
inputBuffer += key;
lcdSetCursor(inputBuffer.length()-1, 1);
lcdWrite('*'); //trocar para asterisco depois
}
else if (key == '#' && inputBuffer.length() == 4) {
if (isAdminLogin) {
if (inputBuffer == adminPass) {
changeState(MENU_ADMIN);
} else {
accessGranted = false;
handleAccess();
}
}
else {
accessGranted = false;
for(int i=0; i<5; i++) {
if(userPasses[i] != "" && userPasses[i] == inputBuffer) {
accessGranted = true;
}
}
handleAccess();
}
}
}
else if (currentState == MENU_ADMIN) {
if (key == '1') changeState(CFG_USER);
else if (key == '2') changeState(CFG_DEL_USER);
else if (key == '3') {
targetUserIndex = 9;
changeState(CFG_NOVA_SENHA);
lcdPrint("NOVA SENHA ADM:");
lcdSetCursor(0,1);
}
else if (key == '4') changeState(CFG_TEMPO);
}
else if (currentState == CFG_USER) {
if (key >= '1' && key <= '5') {
targetUserIndex = key - '1';
changeState(CFG_NOVA_SENHA);
lcdPrint("NOVA SENHA U");
lcdWrite(key);
lcdPrint(":");
lcdSetCursor(0,1);
}
}
else if (currentState == CFG_DEL_USER) {
if (key >= '1' && key <= '5') {
int index = key - '1';
userPasses[index] = "";
lcdCommand(0x01);
lcdPrint("USUARIO APAGADO");
delay(1500);
changeState(MENU_ADMIN);
}
}
else if (currentState == CFG_NOVA_SENHA) {
if (key >= '0' && key <= '9' && inputBuffer.length() < 4) {
inputBuffer += key;
lcdSetCursor(inputBuffer.length()-1, 1);
lcdWrite('*');
}
else if (key == '#' && inputBuffer.length() == 4) {
if (targetUserIndex == 9) {
tempNewPass = inputBuffer;
changeState(CFG_CFR_ADM);
} else {
userPasses[targetUserIndex] = inputBuffer;
lcdCommand(0x01);
lcdPrint("SENHA SALVA");
delay(1500);
changeState(MENU_ADMIN);
}
}
}
else if (currentState == CFG_CFR_ADM) {
if (key >= '0' && key <= '9' && inputBuffer.length() < 4) {
inputBuffer += key;
lcdSetCursor(inputBuffer.length()-1, 1);
lcdWrite('*');//asterisco
}
else if (key == '#' && inputBuffer.length() == 4) {
if (inputBuffer == tempNewPass) {
adminPass = inputBuffer;
lcdCommand(0x01);
lcdPrint("SENHA SALVA");
delay(1500);
changeState(TELA_ESPERA);
} else {
lcdCommand(0x01);
lcdPrint("SENHAS DIFF!");
delay(1500);
changeState(MENU_ADMIN);
}
}
}
else if (currentState == CFG_TEMPO) {
if (key >= '0' && key <= '9' && inputBuffer.length() < 3) {
inputBuffer += key;
lcdSetCursor(inputBuffer.length()-1, 1);
lcdWrite(key);
}
else if (key == '#' && inputBuffer.length() > 0) {
trancaDuration = inputBuffer.toInt() * 1000;
lcdCommand(0x01);
lcdPrint("TEMPO SALVO");
delay(1500);
changeState(MENU_ADMIN);
}
}
}
// INICIALIZAÇÃO
void setup() {
lcdInit();
pinMode(LCD_BACKLIGHT, OUTPUT);
analogWrite(LCD_BACKLIGHT, 128);
pinMode(TRANCA_PIN, OUTPUT);
digitalWrite(TRANCA_PIN, LOW);
accessGranted = false;
pinMode(KEY_C1, OUTPUT);
pinMode(KEY_C2, OUTPUT);
pinMode(KEY_C3, OUTPUT);
pinMode(KEY_R1, INPUT);
pinMode(KEY_R2, INPUT);
pinMode(KEY_R3, INPUT);
pinMode(KEY_R4, INPUT);
changeState(TELA_ESPERA);
}
// LOOP PRINCIPAL
// Executa continuamente e define a taxa de atualizaçao do sistema
void loop() {
updateBacklight();
char key = scanKeypad();
if (key != '\0')
processKey(key);
delay(20);
}