#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Dirección I2C y tamaño de la pantalla (ajusta según tu configuración)
int posFilaCursor = 0;
int posColumnaCursor = 0;
// Estructura para representar un elemento del menú
struct MenuItem {
String nombre;
void (*accion)(); // Puntero a la función de acción
MenuItem* padre; // Puntero al elemento padre
MenuItem* primerHijo; // Puntero al primer hijo
MenuItem* siguiente; // Puntero al siguiente elemento
MenuItem* anterior; // Puntero al elemento anterior (nuevo)
MenuItem(String _nombre, void (*_accion)(), MenuItem* _padre = nullptr)
: nombre(_nombre), accion(_accion), padre(_padre), primerHijo(nullptr), siguiente(nullptr), anterior(nullptr) {}
};
// Función para escribir en la pantalla LCD en una posición específica
void escribirLCD(int columna, int fila, String mensaje) {
lcd.setCursor(columna, fila); //posicionar cursor
lcd.print(mensaje.c_str()); // Convierte el String a una cadena de caracteres
}
// Función para mover el cursor en la pantalla LCD
void moverCursor(){
if (posFilaCursor == 0){
escribirLCD(posColumnaCursor, 1, " "); ///borrar posicion anterior
escribirLCD(posColumnaCursor, posFilaCursor, ">");
}
else if (posFilaCursor == 1){
escribirLCD(posColumnaCursor, 0, " ");///borrar posicion anterior
escribirLCD(posColumnaCursor, posFilaCursor, ">");
}
Serial.print("Fila: ");
Serial.println(posFilaCursor);
Serial.print("Columna: ");
Serial.println(posColumnaCursor);
}
//Funciones de cada opcion
void accionPorDefecto() {
Serial.println("Acción por defecto.");
}
void activarMonitoreo() {
Serial.println("Activando monitereo.");
}
void activarModoManual() {
Serial.println("Activando modo manual.");
}
void modificarSenalControl() {
Serial.println("Modificando señal de control.");
}
void labviewControl() {
Serial.println("Modificando señal de control con Labview.");
}
void activarModoAutomatico() {
Serial.println("Activando modo automático.");
}
void modificarParametrosControl() {
Serial.println("Modificando parámetros de control.");
}
void modificarKP() {
Serial.println("Modificando el KP.");
}
void modificarKI() {
Serial.println("Modificando el KI.");
}
void modificarKD() {
Serial.println("Modificando el KD.");
}
void modificarSP() {
Serial.println("Modificando el SP.");
}
void labviewControlParametros() {
Serial.println("Modificando parámetros de control y set point con Labview.");
}
/////////////// Funcion especial
void volverAlMenuPrincipal() {
Serial.println("Volviendo al menú principal.");
}
// Función para mostrar el menú en la pantalla LCD
void mostrarMenuNext(MenuItem* menu) {
int posFila = 0;
MenuItem* actual = menu;
if (actual->siguiente != nullptr){
lcd.clear();
while (actual != nullptr && posFila < 2) {
Serial.println(actual->nombre);
escribirLCD(1, posFila, actual->nombre);
actual = actual->siguiente;
posFila++;
delay(100);
}
Serial.println("--------------");
posFilaCursor = 0; // actializar posicion del crusor
}
}
void mostrarMenuPrev(MenuItem* menu) {
int posFila = 0;
MenuItem* actual = menu -> anterior;
if (actual != nullptr){
lcd.clear();
while (actual != nullptr && posFila < 2) {
Serial.println(actual->nombre);
escribirLCD(1, posFila, actual->nombre);
actual = actual->siguiente;
posFila++;
delay(100);
}
Serial.println("--------------");
posFilaCursor = 1; // actializar posicion del crusor
}
}
// Menú principal
MenuItem menuPrincipal("Principal", &accionPorDefecto);
// Función para ejecutar la acción correspondiente al elemento del menú seleccionado
MenuItem ejecutarAccion(MenuItem* menu) {
MenuItem* actual = menu;
if (actual != nullptr && actual->accion != nullptr) {
actual->accion(); //Para opciones son hijos, ni opcion retorno
if(actual->primerHijo != nullptr){ /// si se selecciono una opcion padre
actual = actual->primerHijo;
mostrarMenuNext(actual);
posFilaCursor = 0;
}
else if(actual->accion == volverAlMenuPrincipal){ //Para opciones de retorno
actual = actual->padre;
mostrarMenuNext(actual->primerHijo);
posFilaCursor = 0;
return *actual->primerHijo; //actualizar pantalla
}
return *actual; //no se modifica las opciones
} else {
Serial.println("Opción no válida.");
}
}
// Función para limpiar el buffer serial
void limpiarBufferSerial() {
while (Serial.available() > 0) {
Serial.read();
}
}
// Construir la estructura del menú
MenuItem manMode("Man Mode", &activarModoManual);
MenuItem signalControl("Signal Control", &modificarSenalControl);
MenuItem labviewControlOption("Labview Control", &labviewControl);
MenuItem returnOption("Return", &volverAlMenuPrincipal);
MenuItem autoMode("Auto Mode", &activarModoAutomatico);
MenuItem parametersControl("Parameters Control", &modificarParametrosControl);
MenuItem kpOption("KP", &modificarKP);
MenuItem kiOption("KI", &modificarKI);
MenuItem kdOption("KD", &modificarKD);
MenuItem returnOptionPara("Return", &volverAlMenuPrincipal);
MenuItem spOption("SP", &modificarSP);
MenuItem labviewControlParameters("Labview Control", &labviewControlParametros);
MenuItem returnOptionAuto("Return", &volverAlMenuPrincipal);
MenuItem monitoreo("Monitoreo", &activarMonitoreo);
//Estructura temporal para almacenar la opcion que estamos seleccionando
MenuItem temporal("null", nullptr, nullptr);
void setup() {
Serial.begin(9600);
// Configuración del LCD
lcd.init();
lcd.backlight();
//Relaciones entre estructuras
//MENU PRINCIPAL
menuPrincipal.primerHijo = &manMode;
manMode.siguiente = &autoMode;
autoMode.anterior = &manMode; //relacion anterior
autoMode.siguiente = &monitoreo;
monitoreo.anterior = &autoMode;
//MENU MAN MODE
manMode.primerHijo = &signalControl;
signalControl.siguiente = &labviewControlOption;
labviewControlOption.anterior = &signalControl; //relacion anterior
labviewControlOption.siguiente = &returnOption;
returnOption.anterior = &labviewControlOption; //relacion anterior
returnOption.padre = &menuPrincipal; // relacion con padre, para retorno
//MENU AUTO MODE
autoMode.primerHijo = ¶metersControl;
parametersControl.siguiente = &spOption;
spOption.anterior = ¶metersControl; //relacion anterior
spOption.siguiente = &labviewControlParameters;
labviewControlParameters.anterior = &spOption; //relacion anterior
labviewControlParameters.siguiente = &returnOptionAuto;
returnOptionAuto.anterior = &labviewControlParameters; //relacion anterior
returnOptionAuto.padre = &menuPrincipal; // relacion con padre, para retorno
//MENU PARAMETERS CONTROL
parametersControl.primerHijo = &kpOption;
kpOption.siguiente = &kiOption;
kiOption.anterior = &kpOption; //relacion anterior
kiOption.siguiente = &kdOption;
kdOption.anterior = &kiOption; //relacion anterior
kdOption.siguiente = &returnOptionPara;
returnOptionPara.anterior = &kdOption; //relacion anterior
returnOptionPara.padre = &autoMode;// relacion con padre, para retorno
Serial.println("Bienvenido");
temporal = *menuPrincipal.primerHijo;
mostrarMenuNext(&temporal);
escribirLCD(posColumnaCursor, posFilaCursor, ">");
}
void loop() {
limpiarBufferSerial();
escribirLCD(posColumnaCursor, posFilaCursor, ">");
if (Serial.available() > 0) {
char entrada = Serial.read();
Serial.println(entrada);
if (entrada == 'w') {
// Realizar acción cuando se recibe la letra 'w'
if (posFilaCursor == 0){
mostrarMenuPrev(&temporal); // actualizar pantalla
Serial.print("Opcion seleccionada: ");
Serial.println(temporal.nombre);
}
else {
posFilaCursor = 0;
if (temporal.anterior != nullptr){
temporal = *temporal.anterior;
Serial.print("Opcion seleccionada: ");
Serial.println(temporal.nombre);
}
}
moverCursor();
} else if (entrada == 's') {
// Realizar acción cuando se recibe la letra 's'
if (posFilaCursor == 1){
mostrarMenuNext(&temporal); // actualizar pantalla
Serial.print("Opcion seleccionada: ");
Serial.println(temporal.nombre);
}
else {
posFilaCursor = 1;
if (temporal.siguiente != nullptr){
temporal = *temporal.siguiente;
Serial.print("Opcion seleccionada: ");
Serial.println(temporal.nombre);
}
}
moverCursor();
} else if (entrada == 'f') {
// Realizar acción cuando se recibe la letra 'f'
temporal = ejecutarAccion(&temporal);
Serial.print("Opcion seleccionada: ");
Serial.println(temporal.nombre);
moverCursor();
}
}
}