//Nome: Jussara Miliano Soares
//Projeto 0407 - Sistema de Acesso
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
#include <Wire.h>
#include <MPU6050.h>

//Pinos utilizados
#define SERVO_PIN 10
#define BUZZER_PIN 11
#define LED_ORANGE_PIN 12
#define LED_BLUE_PIN 13
#define LED_RED_PIN A0

// Configuracao do LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Configuracao do keypad
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);


// Configuracao do servo motor
Servo servoMotor;

// Configuracao do sensor MPU6050
MPU6050 mpu;

// Estados
enum State {
  STATE_IDLE,
  STATE_AUTH,
  STATE_VERIFY,
  STATE_MOTION_CHECK,
  STATE_UNLOCK
};

// Variaveis
State currentState = STATE_IDLE;
String inputPassword = "";
const String correctPassword = "1234";
unsigned long stateTimer = 0;
unsigned long motionCheckStartTime = 0;
const unsigned long MOTION_CHECK_DURATION = 2000; // milisegundos - 2 segundos
const unsigned long UNLOCK_DURATION = 5000; // 5 segundos
const float MOTION_THRESHOLD = 1.5; // Limite de movimento (g)

// Variáveis para o MPU6050
int16_t ax, ay, az;
int16_t gx, gy, gz;
float axG, ayG, azG;

void setup() {
  Serial.begin(9600);
  
  // Start do LCD
  lcd.init();
  lcd.backlight();
  
  // Start dos LEDs e Buzzer
  pinMode(LED_ORANGE_PIN, OUTPUT);
  pinMode(LED_BLUE_PIN, OUTPUT);
  pinMode(LED_RED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  
  // Start do servo
  servoMotor.attach(SERVO_PIN);
  servoMotor.write(0); // Fechado na posicao inicial
  
  // Start do MPU6050
  Wire.begin();
  mpu.initialize();
  
  if (mpu.testConnection()) {
    Serial.println("MPU6050 conectado");
  } else {
    Serial.println("Erro ao conectar MPU6050!");
  }
  
  // Apagar todos os LEDs
  digitalWrite(LED_ORANGE_PIN, LOW);
  digitalWrite(LED_BLUE_PIN, LOW);
  digitalWrite(LED_RED_PIN, LOW);
  
  // Estado inicial
  enterIdleState();
}

//switch-case dos estados
void loop() {
  char key = keypad.getKey();
  
  switch (currentState) {
    case STATE_IDLE:
      handleIdleState(key);
      break;
      
    case STATE_AUTH:
      handleAuthState(key);
      break;
      
    case STATE_VERIFY:
      handleVerifyState();
      break;
      
    case STATE_MOTION_CHECK:
      handleMotionCheckState();
      break;
      
    case STATE_UNLOCK:
      handleUnlockState();
      break;
  }
}

void enterIdleState() {
  currentState = STATE_IDLE;
  inputPassword = "";
  digitalWrite(LED_ORANGE_PIN, LOW);
  digitalWrite(LED_BLUE_PIN, LOW);
  digitalWrite(LED_RED_PIN, LOW);
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Acesso restrito");
  lcd.setCursor(0, 1);
  lcd.print("Pressione *");
  
  Serial.println("Estado atual: STATE_IDLE");
}

void handleIdleState(char key) {
  if (key == '*') {
    enterAuthState();
  }
}

void enterAuthState() {
  currentState = STATE_AUTH;
  inputPassword = "";
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Digite a senha:");
  lcd.setCursor(0, 1);
  lcd.print("____");
  
  Serial.println("Estado atual: STATE_AUTH");
}

void handleAuthState(char key) {
  if (key && key != '*' && key != '#' && key != 'A' && key != 'B' && key != 'C' && key != 'D') {
    if (inputPassword.length() < 4) {
      inputPassword += key;
      
      // Atualizar display
      lcd.setCursor(inputPassword.length() - 1, 1);
      lcd.print('*');
      
      // verificando senha apos 4 digitos
      if (inputPassword.length() == 4) {
        delay(500); // Pequena pausa para visualizacao
        enterVerifyState();
      }
    }
  }
  
  // Cancelar com '#' e volta para o estado Idle
  //if (key == '#') {
    //enterIdleState();
  //}
}


void enterVerifyState() {
  currentState = STATE_VERIFY;
  Serial.println("Estado atual: STATE_VERIFY");
  Serial.println("Senha digitada: " + inputPassword);
}

void handleVerifyState() {
  if (inputPassword == correctPassword) {
    // Senha correta
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Senha correta!");
    lcd.setCursor(0, 1);
    lcd.print("Verificando...");
    
    delay(1000);
    enterMotionCheckState();
  } else {
    // Senha incorreta
    digitalWrite(LED_RED_PIN, HIGH);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Acesso negado");
    lcd.setCursor(0, 1);
    lcd.print("Senha incorreta");
    
    // Buzzer de erro
    tone(BUZZER_PIN, 1000, 500);
    
    delay(2000);
    digitalWrite(LED_RED_PIN, LOW);
    enterIdleState();
  }
}

void enterMotionCheckState() {
  currentState = STATE_MOTION_CHECK;
  motionCheckStartTime = millis();
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Fique parado!");
  lcd.setCursor(0, 1);
  lcd.print("Verificando...");
  
  Serial.println("Estado atual: STATE_MOTION_CHECK");
}

void handleMotionCheckState() {
  unsigned long currentTime = millis();
  
  if (currentTime - motionCheckStartTime >= MOTION_CHECK_DURATION) {
    // Verificacao de movimento concluida
    if (isOperatorStill()) {
      // Operador parado
      enterUnlockState();
    } else {
      // Operador em movimento
      digitalWrite(LED_ORANGE_PIN, HIGH);
      digitalWrite(LED_RED_PIN, HIGH);
      
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Acesso negado");
      lcd.setCursor(0, 1);
      lcd.print("Em movimento!");
      
      // Buzzer de alerta
      tone(BUZZER_PIN, 800, 1000);
      
      delay(2000);
      digitalWrite(LED_ORANGE_PIN, LOW);
      digitalWrite(LED_RED_PIN, LOW);
      enterIdleState();
    }
  } else {
    // Continuar verificando movimento
    checkMotion();
  }
}

bool isOperatorStill() {
  // Leitursa do valores do MPU6050
  int samples = 10;
  float totalAcceleration = 0;
  
  for (int i = 0; i < samples; i++) {
    mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    
  
    axG = ax / 16384.0;
    ayG = ay / 16384.0;
    azG = az / 16384.0;
    
  
    float magnitude = sqrt(axG*axG + ayG*ayG + azG*azG);
    totalAcceleration += abs(magnitude - 1.0); 
    
    delay(50);
  }
  
  float averageAcceleration = totalAcceleration / samples;
  Serial.print("Aceleracao media: ");
  Serial.println(averageAcceleration);
  
  return averageAcceleration < MOTION_THRESHOLD;
}

void checkMotion() {
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  
  // Converter para g
  axG = ax / 16384.0;
  ayG = ay / 16384.0;
  azG = az / 16384.0;
  
  // Calcular magnitude da aceleracao
  float magnitude = sqrt(axG*axG + ayG*ayG + azG*azG);
  float motion = abs(magnitude - 1.0); // Subtrair 1g da gravidade
  
  // Acender LED laranja se detectar movimento alto
  if (motion > MOTION_THRESHOLD) {
    digitalWrite(LED_ORANGE_PIN, HIGH);
    tone(BUZZER_PIN, 500, 100);
  } else {
    digitalWrite(LED_ORANGE_PIN, LOW);
  }
}

void enterUnlockState() {
  currentState = STATE_UNLOCK;
  stateTimer = millis();
  
  digitalWrite(LED_BLUE_PIN, HIGH);
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Acesso liberado!");
  lcd.setCursor(0, 1);
  lcd.print("Bem-vindo!");
  
  // Movimentacao do servo para posicao aberta
  servoMotor.write(90);
  
  // Som de liberacao
  tone(BUZZER_PIN, 1500, 200);
  delay(300);
  tone(BUZZER_PIN, 2000, 200);
  
  Serial.println("Estado atual: STATE_UNLOCK");
}

void handleUnlockState() {
  if (millis() - stateTimer >= UNLOCK_DURATION) {
    // Retornar servo para posicao fechada
    servoMotor.write(0);
    
    digitalWrite(LED_BLUE_PIN, LOW);
    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Acesso fechado");
    
    delay(1000);
    enterIdleState();
  } else {
    // Mostrar tempo restante
    unsigned long timeLeft = (UNLOCK_DURATION - (millis() - stateTimer)) / 1000;
    lcd.setCursor(0, 1);
    lcd.print("Tempo: ");
    lcd.print(timeLeft);
    lcd.print("s    ");
  }
}
$abcdeabcde151015202530fghijfghij