#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
const int stepPin = 32; // Pin para el pulso (STEP)
const int dirPin = 33; // Pin para la dirección (DIR)
const int buttonPin = 35; // GPIO donde está conectado el interruptor de cero
int buttonState = 0; // Variable que almacena el estado del interruptor de punto cero (Hall Switch)
// Crea una instancia de AccelStepper para driver MDA860E
AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);
// define numero de filas
const uint8_t ROWS = 4;
// define numero de columnas
const uint8_t COLS = 4;
// define la distribucion de teclas
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
/* // pines correspondientes a las filas
uint8_t colPins[COLS] = { 17, 16, 4, 2 };
// pines correspondientes a las columnas
uint8_t rowPins[ROWS] = { 23, 19, 18, 5};
*/
uint8_t colPins[COLS] = { 2, 0, 4, 16 };
// pines correspondientes a las columnas
uint8_t rowPins[ROWS] = { 17, 19, 18, 5};
// crea objeto con los prametros creados previamente
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 20, 4);
enum Screen //Estados posibles de las pantallas
{
IntroScreen,
MainMenu,
MenuAngle,
MenuDiscreet,
SetToZero
};
Screen currentScreen = Screen::IntroScreen;
void setup() {
Serial.begin(9600);
Wire.begin();
lcd.init();
lcd.backlight();
// Configura la velocidad máxima y la aceleración
stepper.setMaxSpeed(1500);
stepper.setSpeed(1000);
stepper.setAcceleration(50);
pinMode(buttonPin, INPUT); //inicializa el pin del interruptor como entrada
//keypad.setDebounceTime(250); // Default debounce is 50 mS
}
void loop() {
lcd.clear();
switch(currentScreen)
{
case IntroScreen:
renderIntroScreen();
break;
case MainMenu:
renderMainMenu();
break;
case MenuAngle:
renderMenuAngle();
break;
case MenuDiscreet:
renderMenuDiscreet();
break;
case SetToZero:
renderSetToZero();
break;
}
}
void renderIntroScreen()
{
lcd.print("Control de Corona"); // Muestra Presentación
lcd.setCursor(0, 1);
lcd.print("Ver:075-23-09-24");
lcd.setCursor(0, 2);
lcd.print("Prueba de cero");
delay(2000); //Pausa para ver la versión en el display
currentScreen = Screen::MainMenu;
}
void renderMainMenu()
{
lcd.print("Ingrese una opcion");
lcd.setCursor(0, 1);
lcd.print("A=Avance a Angulo");
lcd.setCursor(0, 2);
lcd.print("B=Avance Discreto");
lcd.setCursor(0, 3);
lcd.print("C=Set To Zero");
char menuSelect = readMenuFromKeypad(); //Espera que se ingrese la opción desde el teclado
switch (menuSelect) {
case 'A':
currentScreen = Screen::MenuAngle;
break;
case 'B':
currentScreen = Screen::MenuDiscreet;
break;
case 'C':
currentScreen = Screen::SetToZero;
break;
}
}
String angle;
void renderMenuAngle()
{
lcd.setCursor(0, 0);
lcd.print("Ingrese Angulo");
lcd.setCursor(0, 1);
lcd.print("Ang ingresado:" + angle);
lcd.setCursor(0, 2);
lcd.print("C= Clear #=Enter");
lcd.setCursor(0, 3);
lcd.print("*= Menu Anterior");
char option = readMenuFromKeypad();
int angleDegrees;
switch(option)
{
case 'C':
angle = "";
break;
case '*':
angle = "";
currentScreen = Screen::MainMenu;
break;
case '#':
angleDegrees = angle.toInt();
angleDegrees = angleDegrees % 360;
rotateMotor(angleDegrees);
angle = "";
break;
case 'A':
break;
case 'B':
break;
case 'D':
break;
default:
angle = angle + option;
}
}
int stepCount = 0;
void renderSetToZero()
{
digitalWrite(dirPin, HIGH); //define dirección del giro del motor
if (stepCount < 200) //limita a un solo giro
{
// las siguientes cuatro lineas avanzan un paso
digitalWrite(stepPin, HIGH);
delayMicroseconds(500);
digitalWrite(stepPin, LOW);
delayMicroseconds(500);
stepCount = stepCount + 1;
// lee el estado del boton
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) currentScreen = Screen::MainMenu;
}
else {
stepCount = 0;
currentScreen = Screen::MainMenu;
}
}
float angleDegrees = stepper.currentPosition();
void renderMenuDiscreet()
{
angleDegrees = stepper.currentPosition();
Serial.print("angleDegrees0= ");
Serial.println(angleDegrees);
delay(500);
lcd.setCursor(0, 0);
lcd.print("Ingrese Opcion");
lcd.setCursor(0, 1);
lcd.print("A Avance --->>");
lcd.setCursor(0,2);
lcd.print("B Avance <<---");
lcd.setCursor(0, 3);
lcd.print("*=Menu Ant");
char option = readMenuFromKeypad();
switch(option)
{
case '*':
angle = "";
currentScreen = Screen::MainMenu;
break;
case '#':
break;
case 'A':
Serial.print("angleDegrees1= ");
Serial.println(angleDegrees);
delay(1000);
angleDegrees = angleDegrees *1.8 + 10.0; //PARA MOTOR 200 pasos Wokwi***********
//angleDegrees = angleDegrees *0.04737 + 10.0; //PARA MOTOR NEMA******************
//angleDegrees = angleDegrees *0.003347 + 10.0; //PARA GOETHE**********************
Serial.print("angleDegrees2= ");
Serial.println(angleDegrees);
rotateMotor(angleDegrees);
Serial.print("angleDegrees3= ");
Serial.println(angleDegrees);
delay(1000);
break;
case 'B':
Serial.print("angleDegrees1= ");
Serial.println(angleDegrees);
delay(1000);
angleDegrees = angleDegrees *1.8 - 10.0; //PARA MOTOR 200 pasos Wokwi***********
//angleDegrees = angleDegrees *0.04737 - 10.0; //PARA MOTOR NEMA******************
//angleDegrees = angleDegrees *0.003347 - 10.0; //PARA GOETHE**********************
Serial.print("angleDegrees2= ");
Serial.println(angleDegrees);
rotateMotor(angleDegrees);
Serial.print("angleDegrees3= ");
Serial.println(angleDegrees);
delay(1000);
break;
case 'C':
break;
case 'D':
break;
default:
angle = angle + option;
}
}
void rotateMotor(int angle)
{
// Calcula los pasos necesarios para el ángulo ingresado
long stepsNeeded = 0; // pasos necesarios para girar el angulo ingresado por camino mas corto
long currentsteps = stepper.currentPosition(); // Obtiene la posición actual;
Serial.print("CurrentPosition en rotateMotor= ");
Serial.println(currentsteps);
//*********************************************************************************
long totalSteps = 200; //***Para prueba en wokwi ****
//long totalSteps = 107555; //484/9*5*400 Para telescopio Goethe
//long totalSteps = 7600; //Para motor NEMA Enrique
//*********************************************************************************
long targetsteps = calculateStepsForAngle(angle, totalSteps); // calculo de pasos necesarios para girar el angulo ingresado
// Muestra el resultado en el display
lcd.clear();
lcd.print("Angulo: ");
lcd.print(angle);
lcd.print("\337"); //Imprime el símbolo de grado °
// Mueve la corona al ángulo deseado
if ((targetsteps - currentsteps) > (totalSteps / 2)) {
stepsNeeded = (targetsteps -totalSteps - currentsteps);
} else {
stepsNeeded = targetsteps;
}
stepper.moveTo(stepsNeeded);
// Run to target position with set speed and acceleration/deceleration:
stepper.runToPosition();
lcd.setCursor(0, 1);
lcd.print("Pasos: ");
lcd.print(stepsNeeded);
Serial.print ("pasos target: ");
Serial.print (targetsteps);
Serial.print (" pasos needed: ");
Serial.print (stepsNeeded);
Serial.print (" currentPosition: ");
Serial.println (stepper.currentPosition());
// Espera un tiempo para mostrar la posición final (opcional)
delay(1000); // Puedes ajustar el tiempo según tus necesidades
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pos. final: ");
lcd.print(stepper.currentPosition());
lcd.setCursor(0, 1);
lcd.print("Angulo final: ");
lcd.print(angle);
lcd.print("\337"); //Imprime el símbolo de grado °
delay(2000);
}
// Función para leer desde el teclado la opción de menú
char readMenuFromKeypad() {
String input = "";
char key = 0;
while (input.length() < 1) {
key = keypad.getKey();
if (key) {
input += key;
delay(100); // Pequeña pausa para evitar lecturas duplicadas
}
}
return key;
}
// Función para calcular los pasos necesarios para un ángulo dado
long calculateStepsForAngle(int angulo, long totalSteps) {
// Calcula los pasos necesarios para el ángulo deseado
// (360 grados = totalSteps pasos)
return map(angulo, 0, 360, 0, totalSteps);
}