/*
 * Blisther Motor Control v1.0
 * Projeto único para Arduino Uno - controle de motor de passo via TB6600
 * Com contagem de peças, controle de velocidade, display I2C e EEPROM
 *
 * Autor: Seu Nome
 * Data: 2025
 */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <util/atomic.h>
#include <AccelStepper.h>
// =============================
// CONFIGURAÇÕES DO MOTOR
// =============================
const int pulsoPin = 7;             // 
const int dirPin = 5;
const int enablePin = 6;
const int passosPorRevolucao = 120 * 2; // Exemplo: 240 passos/volta
const int pecasPorVolta = 4;
const long passosPorPeca = passosPorRevolucao / pecasPorVolta;
// Cria o objeto do motor: tipo, STEP, DIR
AccelStepper stepper(AccelStepper::DRIVER, pulsoPin, dirPin);
// =============================
// CONFIGURAÇÕES DE INTERFACE
// =============================
const int botaoPlayPause = A2;
const int botaoNavegacao = A1;
const int botaoEmergencia = A3;
const int ledVerde = 12;
const int ledVermelho = 11;
const int ledAmarelo = 10;
const int potenciometro = A0;
// =============================
// ENDEREÇOS NA EEPROM
// =============================
const int enderecoPecasTotal = 0;
const int enderecoTempoTotal = 4;
const int enderecoFlagPrimeiraVez = 8;
const int enderecoPassosTotalEEPROM = 12;
const int enderecoTempoParcial = 16;
const int enderecoPecasParcial = 20;
// =============================
// VARIÁVEIS GLOBAIS
// =============================
bool estaRodando = false;
unsigned long tempoInicio;
unsigned long tempoTotal = 0;
unsigned long tempoParcial = 0;
unsigned long ultimaAtualizacao = 0;
unsigned long numeroPecasTotal = 0;
unsigned long numeroPecasParcial = 0;
float velocidadeMotor = 0;
bool mostrarHorimetro = false;
bool mostrarPecas = false;
const unsigned long tempoDebounce = 50;
unsigned long ultimaAtualizacaoEEPROM = 0;
bool motorAtivo = false;
unsigned long botaoNavegacaoPressTime = 0;
bool isNavButtonPressed = false;
const unsigned long tempoToqueLongo = 3000;
int resetConfirmationState = 0;
unsigned long resetConfirmationTimer = 0;
const unsigned long resetConfirmationDuration = 2000;
long totalPassosContados = 0;
volatile unsigned long pulseCount = 0;
// Variáveis do motor
float targetRPM = 0.0;
float currentRPM = 0.0;
float oldcurrentRPM = 0.0;
float acceleration = 2.0; // RPM/s padrão
unsigned long lastUpdateTime = 0;
bool running = false;
bool stopping = false;
volatile long stepCounter = 0;
double accumulatedSteps = 0.0;
// Instância do display LCD
LiquidCrystal_I2C lcd(0x27, 16, 2); // Display I2C endereço 0x27
// =============================
// SETUP PRINCIPAL
// =============================
void setup() {
 // pinMode(ENABLE_PIN, OUTPUT);
 // digitalWrite(ENABLE_PIN, LOW); // Habilita o driver
  // Inicializa o display LCD
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("SAVANA MAQUINAS");
  lcd.setCursor(0, 1);
  lcd.print(".....BR-75V.....");
  delay(5000);
  lcd.clear();
  pinMode(botaoPlayPause, INPUT_PULLUP);
  pinMode(botaoNavegacao, INPUT_PULLUP);
  pinMode(botaoEmergencia, INPUT_PULLUP);
  pinMode(ledVerde, OUTPUT);
  pinMode(ledVermelho, OUTPUT);
  pinMode(ledAmarelo, OUTPUT);
  pinMode(potenciometro, INPUT);
  pinMode(dirPin, OUTPUT);
  digitalWrite(enablePin, HIGH); // Desativa driver por padrão
  digitalWrite(dirPin, HIGH);   // Direção inicial horária
  // Verifica se é a primeira vez que roda
  byte primeiraVezFlag;
  EEPROM.get(enderecoFlagPrimeiraVez, primeiraVezFlag);
  if (primeiraVezFlag != 1) {
    numeroPecasTotal = 0;
    tempoTotal = 0;
    tempoParcial = 0;
    numeroPecasParcial = 0;
    totalPassosContados = 0;
    byte flagSet = 1;
    EEPROM.put(enderecoFlagPrimeiraVez, flagSet);
    lcd.setCursor(0, 0);
    lcd.print("Primeira Vez!");
    lcd.setCursor(0, 1);
    lcd.print("Valores Zerados");
    delay(2000);
    lcd.clear();
  } else {
    lerDadosEEPROM();
  }
  ultimaAtualizacao = millis();
  tempoInicio = millis();
  lastUpdateTime = micros();
  //configura accelstteper
  
  setupStepper();
  stepper.runSpeed();
}
// =============================
// LOOP PRINCIPAL
// =============================
void loop() {
  lerBotoes();
  controlarMotor();
  stepper.runSpeed();
  atualizarHorimetro();
  atualizarContadorPecas();
  stepper.runSpeed();
  controlarSinaleiras();
  atualizarLCD();
  runPWM();
  if (millis() - ultimaAtualizacaoEEPROM >= 60000) {
    salvarDadosEEPROM();
    ultimaAtualizacaoEEPROM = millis();
  }
}
// =============================
// Setup DO MOTOR E PWM
// =============================
void setupStepper() {
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, HIGH); // Desativa driver por padrão
  digitalWrite(dirPin, HIGH);   // Direção inicial horária
  // Define a máxima velocidade em passos por segundo
  stepper.setMaxSpeed(1500); 
  // Define a aceleração em passos por segundo²
  //stepper.setAcceleration(50);
  // Define velocidade desejada (em passos/segundo)
  // Para RPM: (passos/volta) * RPM / 60
  // Exemplo: motor de 200 passos/volta -> 200 * 60 RPM / 60 = 200 passos/seg
  currentRPM = 1;
  stepper.setSpeed(2); // equivale a ~60 RPM
  stepper.runSpeed();
}
// =============================
// CONTROLE DO MOTOR E PWM
// =============================
void runPWM() {
  unsigned long currentTime = micros();
  float deltaTime = (currentTime - lastUpdateTime) / 1e6;
//  if (deltaTime>=1){
  if (stopping) {
    currentRPM -= acceleration * deltaTime;
    if (currentRPM <= 0) {
      currentRPM = 0;
      stopping = false;
      stopPWM();
    }
  } else {
    if (currentRPM < targetRPM) {
      currentRPM += acceleration * deltaTime;
      if (currentRPM > targetRPM) currentRPM = targetRPM;
    } else if (currentRPM > targetRPM) {
      currentRPM -= acceleration * deltaTime;
      if (currentRPM < targetRPM) currentRPM = targetRPM;
    }
  }
  if (currentRPM > 0.0) {
    if (currentRPM != oldcurrentRPM ){
      oldcurrentRPM = currentRPM;
      stepper.setSpeed(currentRPM);//(updatePWMFrequency(currentRPM);
    }
     stepper.runSpeed();
    if (!running)  stepper.runSpeed();//setupStepper();
  } else {
    stopPWM();
  }
  if (running) {
    float currentHz = (currentRPM * passosPorRevolucao) / 60.0;
    accumulatedSteps += currentHz * deltaTime;
    while (accumulatedSteps >= 1.0) {
    stepCounter++;
    accumulatedSteps -= 1.0;
     }
  }
  lastUpdateTime = currentTime;
  //}
  stepper.runSpeed();
}
// =============================
// FUNÇÕES DE INTERFACE
// =============================
void atualizarLCD() {
  if (resetConfirmationState != 0 && (millis() - resetConfirmationTimer < resetConfirmationDuration)) {
    if (resetConfirmationState == 1) {
      lcd.setCursor(0, 0);
      lcd.print(" Horas Parcial ");
      lcd.setCursor(0, 1);
      lcd.print("   ZERADO!     ");
    } else if (resetConfirmationState == 2) {
      lcd.setCursor(0, 0);
      lcd.print(" Pecas Parcial ");
      lcd.setCursor(0, 1);
      lcd.print("   ZERADO!     ");
    }
    delay(1000);
    return;
  } else if (resetConfirmationState != 0) {
    resetConfirmationState = 0;
    lcd.clear();
  }
  if (mostrarHorimetro) {
    lcd.setCursor(0, 0);
    String line1 = "HsTot: ";
    line1 += String((float)tempoTotal / 3600000.0, 1);
    while (line1.length() < 16) line1 += " ";
    lcd.print(line1);
    lcd.setCursor(0, 1);
    String line2 = "HsPar: ";
    line2 += String((float)tempoParcial / 3600000.0, 1);
    while (line2.length() < 16) line2 += " ";
    lcd.print(line2);
  } else if (mostrarPecas) {
    lcd.setCursor(0, 0);
    String line1 = "PcsTot: " + String(numeroPecasTotal);
    while (line1.length() < 16) line1 += " ";
    lcd.print(line1);
    lcd.setCursor(0, 1);
    String line2 = "PcsPar: " + String(numeroPecasParcial);
    while (line2.length() < 16) line2 += " ";
    lcd.print(line2);
  } else {
    String line1 = "Ajuste: " + String(velocidadeMotor, 0) + "->" + String(currentRPM * pecasPorVolta, 0);
    while (line1.length() < 16) line1 += " ";
    String line2 = "PCs: " + String(numeroPecasParcial);
    while (line2.length() < 16) line2 += " ";
    lcd.setCursor(0, 0);
    lcd.print(line1);
    lcd.setCursor(0, 1);
    lcd.print(line2);
  }
}
// =============================
// CONTROLE DO MOTOR
// =============================
void controlarMotor() {
  int potValue = analogRead(A0);
  velocidadeMotor = map(potValue >> 3, 0, 127, 00, 800);
  float rpms = velocidadeMotor / pecasPorVolta;
  if (estaRodando && !motorAtivo) {
    digitalWrite(enablePin, LOW); // Ativa driver
    motorAtivo = true;
    setDirection(rpms > 0);
    targetRPM = abs(rpms);
    currentRPM = targetRPM;
  } else if (!estaRodando && motorAtivo) {
    stopPWM();
    digitalWrite(enablePin, HIGH); // Desativa driver
    motorAtivo = false;
  }
  if (estaRodando) {
    targetRPM = abs(rpms);
  }
}
void setDirection(bool clockwise) {
  digitalWrite(dirPin, clockwise ? HIGH : LOW);
}
// =============================
// LEITURA DOS BOTÕES
// =============================
void lerBotoes() {
  if (digitalRead(botaoPlayPause) == LOW) {
    delay(tempoDebounce);
    if (digitalRead(botaoPlayPause) == LOW) {
      estaRodando = !estaRodando;
      if (estaRodando) {
        tempoInicio = millis();
        ultimaAtualizacao = millis();
      } else {
        salvarDadosEEPROM();
      }
      while (digitalRead(botaoPlayPause) == LOW);
    }
  }
  unsigned long currentTime = millis();
  if (digitalRead(botaoNavegacao) == LOW && !isNavButtonPressed) {
    delay(tempoDebounce);
    if (digitalRead(botaoNavegacao) == LOW) {
      botaoNavegacaoPressTime = currentTime;
      isNavButtonPressed = true;
      resetConfirmationState = 0;
    }
  } else if (digitalRead(botaoNavegacao) == HIGH && isNavButtonPressed) {
    isNavButtonPressed = false;
    unsigned long pressDuration = currentTime - botaoNavegacaoPressTime;
    if (pressDuration < tempoToqueLongo) {
      if (!mostrarHorimetro && !mostrarPecas) {
        mostrarHorimetro = true;
        mostrarPecas = false;
      } else if (mostrarHorimetro) {
        mostrarHorimetro = false;
        mostrarPecas = true;
        tempoParcial = 0;
      } else if (mostrarPecas) {
        mostrarHorimetro = false;
        mostrarPecas = false;
      }
    } else {
      if (!estaRodando && (mostrarHorimetro || mostrarPecas)) {
        if (mostrarHorimetro) {
          tempoParcial = 0;
          resetConfirmationState = 1;
        } else if (mostrarPecas) {
          numeroPecasParcial = 0;
          resetConfirmationState = 2;
        }
        resetConfirmationTimer = currentTime;
        salvarDadosEEPROM();
      }
    }
  }
  if (digitalRead(botaoEmergencia) == LOW) {
    delay(tempoDebounce);
    if (digitalRead(botaoEmergencia) == LOW) {
      pararEmergencia();
      while (digitalRead(botaoEmergencia) == LOW);
    }
  }
}
// =============================
// HORÍMETRO
// =============================
void atualizarHorimetro() {
  if (estaRodando) {
    unsigned long now = millis();
    tempoParcial = now - tempoInicio;
    tempoTotal += (now - ultimaAtualizacao);
    ultimaAtualizacao = now;
  } else {
    tempoInicio = millis();
    ultimaAtualizacao = millis();
  }
}
// =============================
// CONTAGEM DE PEÇAS
// =============================
unsigned long getSteps() {
return 0;
}
void atualizarContadorPecas() {
  if (estaRodando) {
    unsigned long steps =  pulseCount;//getSteps();
    unsigned long newPieces = steps / passosPorPeca;
    if (newPieces > numeroPecasTotal) {
      numeroPecasTotal = newPieces;
      numeroPecasParcial++;
    }
  }
}
// =============================
// SINALEIRAS LED
// =============================
void controlarSinaleiras() {
  digitalWrite(ledVerde, estaRodando ? HIGH : LOW);
  digitalWrite(ledAmarelo, !motorAtivo ? HIGH : LOW);
  digitalWrite(ledVermelho, motorAtivo && !estaRodando ? HIGH : LOW);
}
// =============================
// EEPROM
// =============================
void lerDadosEEPROM() {
  EEPROM.get(enderecoPecasTotal, numeroPecasTotal);
  EEPROM.get(enderecoTempoTotal, tempoTotal);
  EEPROM.get(enderecoPassosTotalEEPROM, stepCounter);
  EEPROM.get(enderecoTempoParcial, tempoParcial);
  EEPROM.get(enderecoPecasParcial, numeroPecasParcial);
  numeroPecasTotal = stepCounter / passosPorPeca;
}
void salvarDadosEEPROM() {
  EEPROM.put(enderecoPecasTotal, numeroPecasTotal);
  EEPROM.put(enderecoTempoTotal, tempoTotal);
  EEPROM.put(enderecoPassosTotalEEPROM, stepCounter);
  EEPROM.put(enderecoTempoParcial, tempoParcial);
  EEPROM.put(enderecoPecasParcial, numeroPecasParcial);
}
void  stopPWM(){
   stepper.setSpeed(0); 
}
// =============================
// PARADA DE EMERGÊNCIA
// =============================
void pararEmergencia() {
  stopPWM();
  digitalWrite(enablePin, HIGH); // Desativa driver
  motorAtivo = false;
  estaRodando = false;
  digitalWrite(ledVermelho, HIGH);
  digitalWrite(ledAmarelo, LOW);
  digitalWrite(ledVerde, LOW);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("  EMERGENCIA!   ");
  lcd.setCursor(0, 1);
  lcd.print("  Motor PARADO  ");
  salvarDadosEEPROM();
  while (true) {
    digitalWrite(ledVermelho, LOW);
    delay(100);
    digitalWrite(ledVermelho, HIGH);
    delay(700);
  }
}