#include <LiquidCrystal_I2C.h>
#include <Encoder.h>
//----- DEFINICIÓN DE PINES -----//
#define ENCODER_PIN_A 32 // Ajusta según tu cableado
#define ENCODER_PIN_B 33
#define ENCODER_BTN 25 // Pin del botón (push del encoder)
//----- OBJETOS DE PERIFÉRICOS -----//
LiquidCrystal_I2C lcd(0x27, 16, 2); // Dirección 0x27, 16 columnas y 2 filas
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);
//----- ESTADOS DEL MENÚ -----//
enum MenuState { MENU_PRINCIPAL, SUBMENU, EDITANDO };
MenuState estadoActual = MENU_PRINCIPAL;
//----- MENÚ PRINCIPAL Y SUBMENÚS -----//
const int NUM_OPCIONES = 3;
// Opciones del menú principal
const char* mainMenuOptions[NUM_OPCIONES] = { "configuracion", "Manual", "auto" };
// Submenús para cada opción
const char* submenuConfiguracion[] = { "tiempo espera", "tiempo soplado", "longitud" };
const char* submenuManual[] = { "jog manual", "jog de aire", "jog de corte" };
const char* submenuAuto[] = { "automatico" };
// Cantidad de submenús para cada opción principal
const int submenuCounts[NUM_OPCIONES] = { 3, 3, 1 };
// Agrupamos los submenús en un arreglo de punteros
const char** submenus[NUM_OPCIONES] = { submenuConfiguracion, submenuManual, submenuAuto };
//----- VARIABLES ASOCIADAS A CADA SUBMENÚ -----//
// Se asigna un arreglo de variables para cada opción (tamaño según cantidad de submenús)
int configuracionValues[3] = { 0, 0, 0 };
int manualValues[3] = { 0, 0, 0 };
int autoValues[1] = { 0 };
// Se agrupan en un arreglo de punteros para acceder mediante la opción actual
int* variables[NUM_OPCIONES] = { configuracionValues, manualValues, autoValues };
//----- VARIABLES DE NAVEGACIÓN Y ENCODER -----//
int opcionActual = 0; // Índice de la opción del menú principal (0 a 2)
int subMenuActual = 0; // Índice del submenú según la opción actual
long posEncoderAnterior = 0;
//----- VARIABLES PARA DEBOUNCING DEL BOTÓN -----//
unsigned long ultimoDebounceTime = 0;
const unsigned long DEBOUNCE_DELAY = 50;
int ultimoEstadoBoton = HIGH; // Se asume conexión con pull-up
//----------------- FUNCIONES -----------------//
// Función para actualizar la pantalla LCD según el estado actual
void displayMenu() {
lcd.clear();
switch (estadoActual) {
case MENU_PRINCIPAL:
lcd.setCursor(0, 0);
lcd.print("Menu:");
lcd.setCursor(0, 1);
lcd.print(mainMenuOptions[opcionActual]);
break;
case SUBMENU:
lcd.setCursor(0, 0);
lcd.print(mainMenuOptions[opcionActual]);
lcd.setCursor(0, 1);
lcd.print(submenus[opcionActual][subMenuActual]);
break;
case EDITANDO:
lcd.setCursor(0, 0);
lcd.print("Editar ");
lcd.print(mainMenuOptions[opcionActual]);
lcd.setCursor(0, 1);
lcd.print(submenus[opcionActual][subMenuActual]);
lcd.print(": ");
lcd.print( variables[opcionActual][subMenuActual] );
break;
}
}
// Función para manejar la rotación del encoder
void manejarEncoder() {
long posNueva = encoder.read();
if (posNueva != posEncoderAnterior) {
int delta = posNueva - posEncoderAnterior;
posEncoderAnterior = posNueva;
switch (estadoActual) {
case MENU_PRINCIPAL:
opcionActual += delta;
if (opcionActual < 0) opcionActual = 0;
if (opcionActual >= NUM_OPCIONES) opcionActual = NUM_OPCIONES - 1;
break;
case SUBMENU:
subMenuActual += delta;
if (subMenuActual < 0) subMenuActual = 0;
if (subMenuActual >= submenuCounts[opcionActual])
subMenuActual = submenuCounts[opcionActual] - 1;
break;
case EDITANDO:
// Se modifica el valor asociado al submenú actual
variables[opcionActual][subMenuActual] += delta;
break;
}
}
}
// Función para manejar la pulsación del botón (push del encoder)
void manejarBoton() {
int lecturaBoton = digitalRead(ENCODER_BTN);
// Implementar debouncing
if (lecturaBoton != ultimoEstadoBoton) {
ultimoDebounceTime = millis();
}
if ((millis() - ultimoDebounceTime) > DEBOUNCE_DELAY) {
// Detectar flanco descendente: de HIGH a LOW
if (ultimoEstadoBoton == HIGH && lecturaBoton == LOW) {
// Cambiar de estado según la jerarquía del menú:
switch (estadoActual) {
case MENU_PRINCIPAL:
// Al presionar en el menú principal, se pasa al submenú
estadoActual = SUBMENU;
subMenuActual = 0; // Reinicia el submenú
encoder.write(0);
posEncoderAnterior = 0;
break;
case SUBMENU:
// Desde el submenú, se ingresa en el modo de edición
estadoActual = EDITANDO;
encoder.write( variables[opcionActual][subMenuActual] );
posEncoderAnterior = variables[opcionActual][subMenuActual];
break;
case EDITANDO:
// Al finalizar la edición se vuelve al submenú
estadoActual = SUBMENU;
encoder.write(0);
posEncoderAnterior = 0;
break;
}
}
}
ultimoEstadoBoton = lecturaBoton;
}
//----------------- SETUP Y LOOP -----------------//
void setup() {
// Inicializa la pantalla LCD
lcd.init();
lcd.backlight();
// Configura el pin del botón con pull-up
pinMode(ENCODER_BTN, INPUT_PULLUP);
// Inicializa la posición del encoder
posEncoderAnterior = encoder.read();
// Muestra la pantalla inicial
displayMenu();
}
void loop() {
manejarEncoder();
manejarBoton();
displayMenu();
delay(50); // Retardo para evitar saturación de actualizaciones
}