/*
* 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);
}
}