/*
  RPMStepper com Controle por Potenciômetro e Display OLED
  
  Este programa permite controlar a velocidade de um motor de passo através
  de um potenciômetro conectado ao pino A0 e exibe as informações atuais
  em um display OLED SSD1306.
  
  Características:
  - Controle de velocidade (0-100 RPM) via potenciômetro
  - Exibição da velocidade atual e direção no display OLED
  - Botão para alternar direção (conectado ao pino 2)
  - Botão para parada de emergência (conectado ao pino 7)
*/
#include "RPMStepper.h"
#include <Wire.h>
#include <Adafruit_GFX.h>     // Biblioteca gráfica core
#include <Adafruit_SSD1306.h> // Biblioteca específica para display SSD1306
// Definições do display OLED
#define SCREEN_WIDTH 128      // Largura do OLED em pixels
#define SCREEN_HEIGHT 64      // Altura do OLED em pixels
#define OLED_RESET    -1      // Pino de reset (-1 se compartilhar o reset do Arduino)
#define SCREEN_ADDRESS 0x3C   // Endereço I2C do display (pode ser 0x3D para alguns displays)
// Definições dos pinos do motor
#define PULSE_PIN  3  // Pino conectado ao STEP do driver
#define DIR_PIN    4  // Pino conectado ao DIR do driver
#define ENABLE_PIN 6  // Pino conectado ao ENABLE do driver
// Definições dos pinos de controle
#define POT_PIN     A0 // Potenciômetro para controle de velocidade
#define DIR_BTN_PIN 2  // Botão para alternar direção
#define STOP_BTN_PIN 8 // Botão para parada de emergência
// Passos por revolução do motor
#define STEPS_PER_REV 200
// RPM máximo (valor máximo do potenciômetro)
#define MAX_RPM 100
// Cria os objetos
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RPMStepper motor(PULSE_PIN, DIR_PIN, ENABLE_PIN, STEPS_PER_REV);
// Variáveis globais para controle
bool motorEnabled = true;      // Estado de ativação do motor
float targetRPM = 0;           // RPM alvo baseado no potenciômetro
bool isClockwise = true;       // Direção do motor
unsigned long lastPotRead = 0; // Último momento de leitura do potenciômetro
unsigned long lastDisplayUpdate = 0; // Último momento de atualização do display
// Variáveis para debounce dos botões
bool lastDirBtnState = HIGH;
bool lastStopBtnState = HIGH;
unsigned long lastDirBtnDebounce = 0;
unsigned long lastStopBtnDebounce = 0;
unsigned long debounceDelay = 50;  // Tempo de debounce em ms
void setup() {
  Serial.begin(115200);
  Serial.println("Iniciando RPMStepper com Controle OLED");
  
  // Configuração dos pinos dos botões
  pinMode(DIR_BTN_PIN, INPUT_PULLUP);  // Botão de direção com pull-up interno
  pinMode(STOP_BTN_PIN, INPUT_PULLUP); // Botão de parada com pull-up interno
  
  // Inicializa o display OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("Erro ao inicializar SSD1306"));
    for(;;); // Loop infinito em caso de falha
  }
  
  // Configurações iniciais do display
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(F("Inicializando..."));
  display.display();
  delay(1000);
  
  // Configura o motor
  motor.setAcceleration(30);  // Aceleração de 30 RPM por segundo
  motor.enable();             // Ativa o motor
  
  // Exibe tela inicial
  updateDisplay();
}
void loop() {
  // Lê o potenciômetro a cada 100ms para evitar flutuações
  if (millis() - lastPotRead > 100) {
    int potValue = analogRead(POT_PIN);
    // Mapeia o valor do potenciômetro (0-1023) para RPM (0-MAX_RPM)
    targetRPM = map(potValue, 0, 1023, 0, MAX_RPM * 100) / 100.0;
    motor.setRPM(targetRPM);
    lastPotRead = millis();
  }
  
  // Verifica o botão de direção com debounce
  int dirBtnReading = digitalRead(DIR_BTN_PIN);
  if (dirBtnReading != lastDirBtnState) {
    lastDirBtnDebounce = millis();
  }
  
  if ((millis() - lastDirBtnDebounce) > debounceDelay) {
    if (dirBtnReading == LOW && lastDirBtnState == HIGH) {
      // Botão de direção foi pressionado
      isClockwise = !isClockwise;  // Inverte a direção
      motor.setDirection(isClockwise);
      Serial.print("Direção alterada para: ");
      Serial.println(isClockwise ? "Horário" : "Anti-horário");
    }
  }
  lastDirBtnState = dirBtnReading;
  
  // Verifica o botão de parada de emergência com debounce
  int stopBtnReading = digitalRead(STOP_BTN_PIN);
  if (stopBtnReading != lastStopBtnState) {
    lastStopBtnDebounce = millis();
  }
  
 
    if (digitalRead(STOP_BTN_PIN)) {
      // Botão de parada foi pressionado
      if (motorEnabled) {
        Serial.println("Parada de emergência acionada!");
        motor.emergencyStop();
        motorEnabled = false;
      } else {
        Serial.println("Motor reativado!");
        motor.enable();
        motorEnabled = true;
      }
    }
  
  lastStopBtnState = stopBtnReading;
  
  // Executa o motor (deve ser chamado continuamente)
  motor.run();
  
  // Atualiza o display a cada 200ms
  if (millis() - lastDisplayUpdate > 200) {
    updateDisplay();
    lastDisplayUpdate = millis();
  }
}
// Função para atualizar as informações no display OLED
void updateDisplay() {
  display.clearDisplay();
  
  // Título
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println(F("RPMStepper Controller"));
  display.drawLine(0, 9, display.width(), 9, SSD1306_WHITE);
  
  // Informações do Motor
  display.setTextSize(1);
  display.setCursor(0, 12);
  display.print(F("Status: "));
  if (motorEnabled) {
    display.println(motor.getCurrentRPM() > 0 ? F("RODANDO") : F("PARADO"));
  } else {
    display.println(F("DESATIVADO"));
  }
  
  // RPM Atual
  display.setTextSize(2);
  display.setCursor(0, 24);
  display.print(motor.getCurrentRPM(), 1);
  display.print(F(" RPM"));
  
  // RPM Alvo
  display.setTextSize(1);
  display.setCursor(0, 42);
  display.print(F("Alvo: "));
  display.print(targetRPM, 1);
  display.println(F(" RPM"));
  
  // Direção
  display.setCursor(0, 52);
  display.print(F("Direcao: "));
  display.println(isClockwise ? F("HORARIO") : F("ANTI-HOR"));
  
  // Desenha uma pequena barra de progresso para RPM
  int barWidth = map(int(motor.getCurrentRPM()), 0, MAX_RPM, 0, SCREEN_WIDTH - 2);
  display.drawRect(0, SCREEN_HEIGHT - 7, SCREEN_WIDTH, 7, SSD1306_WHITE);
  if (barWidth > 0) {
    display.fillRect(1, SCREEN_HEIGHT - 6, barWidth, 5, SSD1306_WHITE);
  }
  
  display.display();
}