Problema LCD 2004 + ESP

Buenas noches,

tengo una código para ESP32 que consiste en un sistema de amanecer/anochecer con pantalla 2004 i2c, 6 botones (arriba, abajo, izq., der., Ok e Inspección), DHT21, 2 reles y RTC.

Mi problema viene cuando al cargar el codigo se ve la pantalla como si estuvieran los botones pulsados y no para de moverse el cursor por todo el menú y demás. No se queda en la pantalla principal donde tiene que mostrar la información de datos y el acceso al Menú.
Os cuelgo el código para que veais lo que pasa.
Si pudierais echarme una mano os lo agradecería.

#include <Wire.h>
#include <RTClib.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

#define DHTPIN 4
#define DHTTYPE DHT21

#define BUTTON_PIN 16  // Botón físico principal
#define RELAY1_PIN 17
#define RELAY2_PIN 5
#define LED_STRIP1_PIN 13
#define LED_STRIP2_PIN 12
#define LED_STRIP3_PIN 14
#define LED_STRIP4_PIN 27
#define LED_MOON_PIN 26

// Botones del joystick digital
#define BUTTON_UP_PIN 32
#define BUTTON_DOWN_PIN 33
#define BUTTON_LEFT_PIN 25
#define BUTTON_RIGHT_PIN 23
#define BUTTON_OK_PIN 18

#define NUM_MENU_ITEMS 5  // Define el número de ítems del menú

unsigned long lastDebounceTime = 0;  // Para llevar el tiempo del último cambio
unsigned long lastButtonPress = 0;   // Para almacenar el último momento de pulsación

// Estados de los botones
int lastButtonStateUp = HIGH;
int buttonStateUp = HIGH;

int lastButtonStateDown = HIGH;
int buttonStateDown = HIGH;

int lastButtonStateLeft = HIGH;
int buttonStateLeft = HIGH;

int lastButtonStateRight = HIGH;
int buttonStateRight = HIGH;

int lastButtonStateOK = HIGH;
int buttonStateOK = HIGH;

int lastButtonStateButton = HIGH;
int buttonStateButton = HIGH;


// Constantes
const unsigned long DEBOUNCE_DELAY = 200;
const int EEPROM_ADDRESS = 0;
const char* menuNames[] = { "Hora", "Amanecer", "Anochecer", "Luz Luna", "Salidas Aux." };

// Caracteres personalizados para iconos
byte sunIcon[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b01010,
  0b00000,
  0b00000,
  0b00000,
  0b00000
};

byte dropIcon[8] = {
  0b00100,
  0b01110,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};

byte moonIcon[8] = {
  0b00111,
  0b01100,
  0b11000,
  0b11000,
  0b11000,
  0b01100,
  0b00111,
  0b00000
};

byte thermometerIcon[8] = {
  0b00100,
  0b01010,
  0b01010,
  0b01010,
  0b01010,
  0b01110,
  0b01110,
  0b00100
};

// Inicialización de componentes
DHT dht(DHTPIN, DHTTYPE);
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Estructura para guardar la configuración en la EEPROM
struct Config {
  bool moonLightEnabled;
  int moonLightIntensity;
  bool relay1Enabled;
  bool relay2Enabled;
  int relay1StartHour;
  int relay1EndHour;
  int relay2StartHour;
  int relay2EndHour;
  int dawnDuration;
  int duskDuration;
  int dawnStartHour;
  int dawnStartMinute;
  int duskStartHour;
  int duskStartMinute;
  bool autoAdjustEnabled;
  int autoAdjustMinutes;
  int autoAdjustMaxHour;
};

Config config;

// Definir las variables para los estados
bool isMainScreen = true;  // Variable para saber si estamos en la pantalla principal o en el menú
bool inMenu = false;

// Definir variables para el debounce de cada botón
unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
unsigned long lastDebounceTimeLeft = 0;
unsigned long lastDebounceTimeRight = 0;
unsigned long lastDebounceTimeOK = 0;
unsigned long lastDebounceTimeButton = 0;

// Variables globales
int menuPosition = 0;
int subMenuPosition = 0;
bool inSubMenu = false;
bool screenUpdated = false;
unsigned long lastDHTTime = 0;
unsigned long dhtInterval = 2000;  // Intervalo de 2 segundos
unsigned long lastScreenUpdateTime = 0;
unsigned long screenUpdateInterval = 3000;  // 3 segundos
bool inspectionNight = false;               // Para llevar un control del estado de la inspección nocturna
bool buttonPressed = false;  // Para controlar el estado del botón
bool inspectionModeActive = false;  // Indica si el modo de inspección nocturna está activo

// Variables para la hora, temperatura y humedad
String currentTime = "00:00:00";  // Iniciar como cadena de ejemplo
float temperature = 0.0;          // Temperatura en grados Celsius
float humidity = 0.0;             // Humedad en porcentaje

// Declaraciones de funciones
void loadConfig();
void displayMainScreen(DateTime now, float humidity, float temperature);
void handleMenu();
void controlRelay1(float humidity, DateTime now);
void controlRelay2(DateTime now);
void controlMoonLightLED(DateTime now);
void controlDawnDuskLEDs(DateTime now);
void handleAutoAdjust(DateTime now);
void quickSunrise();
void quickSunset();
void adjustValue(bool increment);
void displaySubMenu();
void displayDateTimeSubMenu();
void displayDawnSubMenu();
void displayDuskSubMenu();
void displayMoonLightSubMenu();
void displayAuxOutputsSubMenu();
void adjustDate(bool increment);
void adjustTime(bool increment);
void adjustDawnSettings(bool increment);
void adjustDuskSettings(bool increment);
void adjustMoonLightSettings(bool increment);
void adjustAuxOutputsSettings(bool increment);
void saveConfig();
void setLEDStripsBrightness(int brightness);
int  getSubMenuSize(int menuPosition);
void updateScreen();
void changeMenuUp();
void changeMenuDown();
void navigateLeft();
void navigateRight();
void selectOption();
void toggleNightInspection();
bool isReturnOptionSelected(int menuPos, int subMenuPos);
void handleSubMenu();
void displayMainMenu();

void setup() {
  Serial.begin(921600);
  dht.begin();
  if (!rtc.begin()) {
    Serial.println("No se pudo encontrar el RTC");
    while (1)
      ;
  }
  if (rtc.lostPower()) {
    Serial.println("RTC perdió energía, ¡vamos a ajustar la hora!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  lcd.init();
  lcd.backlight();
  lcd.createChar(0, sunIcon);          // Crear el carácter personalizado para el sol
  lcd.createChar(1, dropIcon);         // Crear el carácter personalizado para la gota
  lcd.createChar(2, moonIcon);         // Crear el carácter personalizado para la luna
  lcd.createChar(3, thermometerIcon);  // Crear el carácter personalizado para el termómetro
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(RELAY1_PIN, OUTPUT);
  pinMode(RELAY2_PIN, OUTPUT);
  pinMode(LED_STRIP1_PIN, OUTPUT);
  pinMode(LED_STRIP2_PIN, OUTPUT);
  pinMode(LED_STRIP3_PIN, OUTPUT);
  pinMode(LED_STRIP4_PIN, OUTPUT);
  pinMode(LED_MOON_PIN, OUTPUT);
  pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
  pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);
  pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP);
  pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP);
  pinMode(BUTTON_OK_PIN, INPUT_PULLUP);

  // Cargar configuración desde la EEPROM
  loadConfig();

  // Mostrar pantalla principal al arrancar
  updateScreen();
}

void loop() {
  DateTime now = rtc.now();
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  unsigned long currentMillis = millis();

  if (currentMillis - lastDHTTime >= dhtInterval) {
    lastDHTTime = currentMillis;
    float newHumidity = dht.readHumidity();
    float newTemperature = dht.readTemperature();

    // Solo actualizamos los valores si han cambiado
    if (newHumidity != humidity || newTemperature != temperature) {
      humidity = newHumidity;
      temperature = newTemperature;
      screenUpdated = true;  // Indicamos que los datos han cambiado y necesitamos actualizar la pantalla
    }
  }

  // Leemos el estado del botón
  if (digitalRead(BUTTON_PIN) == LOW) {  // El botón está presionado (porque usamos pull-up)
    if (!buttonPressed) {                // Si el botón no estaba presionado antes
      buttonPressed = true;

      if (!inspectionModeActive) {
        // Si el modo de inspección no está activo, hacemos un amanecer rápido
        quickSunrise();
        inspectionModeActive = true;  // Activamos el modo de inspección
      } else {
        // Si el modo de inspección está activo, hacemos un anochecer rápido
        quickSunset();
        inspectionModeActive = false;  // Desactivamos el modo de inspección después del anochecer
      }
    }
  } else {
    buttonPressed = false;  // Reseteamos el estado del botón cuando no está presionado
  }


  // Manejar el menú y submenús
  handleMenu();

  // Control de relés
  controlRelay1(humidity, now);
  controlRelay2(now);

  // Control de luz lunar y amanecer/anochecer
  controlMoonLightLED(now);
  controlDawnDuskLEDs(now);

  // Ajuste automático si está habilitado
  handleAutoAdjust(now);

  handleButtons();

  // Código para actualizar la pantalla y otros procesos
  if (screenUpdated) {
    updateScreen();  // Actualiza la pantalla si es necesario
  }

  delay(100);

  // Depuración de lecturas de botones
  Serial.print("Botón UP: ");
  Serial.println(digitalRead(BUTTON_UP_PIN));
  Serial.print("Botón DOWN: ");
  Serial.println(digitalRead(BUTTON_DOWN_PIN));
  Serial.print("Botón LEFT: ");
  Serial.println(digitalRead(BUTTON_LEFT_PIN));
  Serial.print("Botón RIGHT: ");
  Serial.println(digitalRead(BUTTON_RIGHT_PIN));
  Serial.print("Botón OK: ");
  Serial.println(digitalRead(BUTTON_OK_PIN));

  delay(500);  // Pequeña pausa para evitar saturar el monitor serial
}

void handleButtons() {
  int readingUp = digitalRead(BUTTON_UP_PIN);
  int readingDown = digitalRead(BUTTON_DOWN_PIN);
  int readingLeft = digitalRead(BUTTON_LEFT_PIN);
  int readingRight = digitalRead(BUTTON_RIGHT_PIN);
  int readingOK = digitalRead(BUTTON_OK_PIN);
  int readingInspeccion = digitalRead(BUTTON_PIN);

  // Button UP (Arriba)
  if (readingUp != lastButtonStateUp) {
    lastDebounceTimeUp = millis();  // Se actualiza el tiempo de debounce
  }
  if ((millis() - lastDebounceTimeUp) > DEBOUNCE_DELAY) {
    if (readingUp != buttonStateUp) {
      buttonStateUp = readingUp;  // Actualizamos el estado del botón
      if (buttonStateUp == LOW) {
        changeMenuUp();  // Llamamos a la función que manejará la acción de "Arriba"
      }
    }
  }
  lastButtonStateUp = readingUp;  // Guardamos el estado del botón actual

  // Button DOWN (Abajo)
  if (readingDown != lastButtonStateDown) {
    lastDebounceTimeDown = millis();  // Actualizamos el tiempo de debounce
  }
  if ((millis() - lastDebounceTimeDown) > DEBOUNCE_DELAY) {
    if (readingDown != buttonStateDown) {
      buttonStateDown = readingDown;
      if (buttonStateDown == LOW) {
        changeMenuDown();  // Llamamos a la función que manejará la acción de "Abajo"
      }
    }
  }
  lastButtonStateDown = readingDown;

  // Button LEFT (Izquierda)
  if (readingLeft != lastButtonStateLeft) {
    lastDebounceTimeLeft = millis();
  }
  if ((millis() - lastDebounceTimeLeft) > DEBOUNCE_DELAY) {
    if (readingLeft != buttonStateLeft) {
      buttonStateLeft = readingLeft;
      if (buttonStateLeft == LOW) {
        navigateLeft();  // Acción para "Izquierda"
      }
    }
  }
  lastButtonStateLeft = readingLeft;

  // Button RIGHT (Derecha)
  if (readingRight != lastButtonStateRight) {
    lastDebounceTimeRight = millis();
  }
  if ((millis() - lastDebounceTimeRight) > DEBOUNCE_DELAY) {
    if (readingRight != buttonStateRight) {
      buttonStateRight = readingRight;
      if (buttonStateRight == LOW) {
        navigateRight();  // Acción para "Derecha"
      }
    }
  }
  lastButtonStateRight = readingRight;

  // Button OK
  if (readingOK != lastButtonStateOK) {
    lastDebounceTimeOK = millis();
  }
  if ((millis() - lastDebounceTimeOK) > DEBOUNCE_DELAY) {
    if (readingOK != buttonStateOK) {
      buttonStateOK = readingOK;
      if (buttonStateOK == LOW) {
        selectOption();  // Acción para "OK"
      }
    }
  }
  lastButtonStateOK = readingOK;

  // Button Inspección Nocturna
  if (readingInspeccion != lastButtonStateButton) {
    lastDebounceTimeButton = millis();
  }
  if ((millis() - lastDebounceTimeButton) > DEBOUNCE_DELAY) {
    if (readingInspeccion != buttonStateButton) {
      buttonStateButton = readingInspeccion;
      if (buttonStateButton == LOW) {
        toggleNightInspection();  // Acción para "Inspección Nocturna"
      }
    }
  }
  lastButtonStateButton = readingInspeccion;
}


void changeMenuUp() {
  if (menuPosition > 0) {
    menuPosition--;  // Mueve hacia arriba en el menú
    updateScreen();  // Actualiza la pantalla
  }
}

void changeMenuDown() {
  if (menuPosition < NUM_MENU_ITEMS - 1) {
    menuPosition++;  // Mueve hacia abajo en el menú
    updateScreen();  // Actualiza la pantalla
  }
}

void navigateLeft() {
  subMenuPosition = (subMenuPosition - 1 + getSubMenuSize(menuPosition)) % getSubMenuSize(menuPosition);
  screenUpdated = false;
}

void navigateRight() {
  subMenuPosition = (subMenuPosition + 1) % getSubMenuSize(menuPosition);
  screenUpdated = false;
}

void selectOption() {
  inSubMenu = !inSubMenu;  // Alterna entre el menú principal y el submenú
  if (!inSubMenu) {
    subMenuPosition = 0;  // Reiniciar la posición del submenú al salir
  }
  screenUpdated = false;
}

void toggleNightInspection() {
  inspectionNight = !inspectionNight;
  screenUpdated = false;
}

void updateScreen() {
  unsigned long currentMillis = millis();

  // Si ha pasado el intervalo de actualización de pantalla o si los datos han cambiado
  if (currentMillis - lastScreenUpdateTime >= screenUpdateInterval || screenUpdated) {
    lastScreenUpdateTime = currentMillis;  // Actualiza el tiempo de la última actualización

    // Mostrar información en la pantalla LCD
    DateTime now = rtc.now();
    displayMainScreen(now, humidity, temperature);

    // Restablecer la variable de actualización
    screenUpdated = false;  // Se restablece para evitar actualizaciones repetidas innecesarias
  }
}

void displayMainScreen(DateTime now, float humidity, float temperature) {
  // Limpiar la pantalla antes de actualizar
  lcd.clear();

  // Mostrar la hora en formato HH:MM:SS
  lcd.setCursor(0, 0);
  lcd.print(now.hour());
  lcd.print(":");
  lcd.print(now.minute());
  lcd.print(":");
  lcd.print(now.second());

  // Mostrar la fecha (solo año y mes, por ejemplo)
  lcd.setCursor(0, 1);
  lcd.print(now.year());
  lcd.print("-");
  lcd.print(now.month());
  lcd.print("-");
  lcd.print(now.day());

  // Mostrar la temperatura y humedad
  lcd.setCursor(0, 2);
  lcd.write(byte(3));  // Icono de termómetro
  lcd.print("Temp: ");
  lcd.print(temperature);
  lcd.print(" C");

  lcd.setCursor(0, 3);
  lcd.write(byte(1));  // Icono de gota
  lcd.print("Hum: ");
  lcd.print(humidity);
  lcd.print(" %");

  // Mostrar el acceso al menú
  lcd.setCursor(0, 3);   // Esta es la misma línea de humedad, si quieres un ícono diferente o texto puedes modificar esto
  lcd.setCursor(12, 3);  // Posicionar un poco más a la derecha
  lcd.print("> Menu");   // Indicación de acceso al menú
}

void handleMenu() {
  if (digitalRead(BUTTON_OK_PIN) == LOW) {
    inMenu = !inMenu;  // Cambiar entre la pantalla principal y el menú
    delay(200);        // Pequeña pausa para evitar rebotes
  }

  // Si estamos en el menú, se pueden manejar las opciones del menú
  if (inMenu) {
    if (digitalRead(BUTTON_UP_PIN) == LOW) {
      menuPosition = (menuPosition - 1 + NUM_MENU_ITEMS) % NUM_MENU_ITEMS;
      delay(200);  // Pausa para evitar rebotes
    }

    if (digitalRead(BUTTON_DOWN_PIN) == LOW) {
      menuPosition = (menuPosition + 1) % NUM_MENU_ITEMS;
      delay(200);  // Pausa para evitar rebotes
    }
  }
}

void handleSubMenu() {
  if (digitalRead(BUTTON_LEFT_PIN) == LOW && millis() - lastDebounceTime > DEBOUNCE_DELAY) {
    lastDebounceTime = millis();
    adjustValue(false);     // Ajustar valor hacia abajo
    screenUpdated = false;  // Marcar que la pantalla necesita actualizarse
  }

  if (digitalRead(BUTTON_RIGHT_PIN) == LOW && millis() - lastDebounceTime > DEBOUNCE_DELAY) {
    lastDebounceTime = millis();
    adjustValue(true);      // Ajustar valor hacia arriba
    screenUpdated = false;  // Marcar que la pantalla necesita actualizarse
  }

  displaySubMenu();  // Mostrar el submenú actualizado
}

bool isReturnOptionSelected(uint8_t menu, uint8_t sub) {
  // Si "Volver" es la última opción en cada submenú:
  return sub == getSubMenuSize(menu) - 1;
}

// Definición de la función isReturnOptionSelected
bool isReturnOptionSelected(int menuPos, int subMenuPos) {
  // Lógica que determine si la opción de retorno (volver) ha sido seleccionada
  // Por ejemplo, podemos comprobar si las posiciones del menú coinciden
  if (menuPos == 0 && subMenuPos == 0) {
    return true;
  } else {
    return false;
  }
}

int getSubMenuSize(int menuPosition) {
  switch (menuPosition) {
    case 0: return 2;  // Fecha y Hora
    case 1: return 6;  // Amanecer
    case 2: return 6;  // Anochecer
    case 3: return 2;  // Luz Luna
    case 4: return 6;  // Salidas Auxiliares
    default: return 0;
  }
}

void displayMainMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Menu Principal");

  lcd.setCursor(0, 1);
  lcd.print("1. Amanecer");

  lcd.setCursor(0, 2);
  lcd.print("2. Anochecer");

  lcd.setCursor(0, 3);
  lcd.print("3. Luz Luna");

  lcd.setCursor(0, 4);
  lcd.print("4. Salidas Aux");

  lcd.setCursor(0, 5);
  lcd.print("5. Ajustes");
}


void displaySubMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  switch (menuPosition) {
    case 0:
      lcd.print("Fecha y Hora");
      displayDateTimeSubMenu();
      break;
    case 1:
      lcd.print("Amanecer");
      displayDawnSubMenu();
      break;
    case 2:
      lcd.print("Anochecer");
      displayDuskSubMenu();
      break;
    case 3:
      lcd.print("Luz Luna");
      displayMoonLightSubMenu();
      break;
    case 4:
      lcd.print("Salidas Aux.");
      displayAuxOutputsSubMenu();
      break;
  }
}

void displayDateTimeSubMenu() {
  lcd.setCursor(0, 1);
  switch (subMenuPosition) {
    case 0:
      lcd.print(">Ajustar Fecha");
      break;
    case 1:
      lcd.print(">Ajustar Hora");
      break;
  }
}

void displayDawnSubMenu() {
  lcd.setCursor(0, 1);
  switch (subMenuPosition) {
    case 0:
      lcd.print(">Duracion: ");
      lcd.print(config.dawnDuration);
      lcd.print(" min");
      break;
    case 1:
      lcd.print(">Hora Inicio: ");
      lcd.print(config.dawnStartHour);
      lcd.print(":");
      lcd.print(config.dawnStartMinute);
      break;
    case 2:
      lcd.print(">Auto Ajuste: ");
      lcd.print(config.autoAdjustEnabled ? "Si" : "No");
      break;
    case 3:
      lcd.print(">Minutos Ajuste: ");
      lcd.print(config.autoAdjustMinutes);
      break;
    case 4:
      lcd.print(">Hora Max Ajuste: ");
      lcd.print(config.autoAdjustMaxHour);
      lcd.print(":00");
      break;
    case 5:
      lcd.print(">Activar Ajuste: ");
      lcd.print(config.autoAdjustEnabled ? "Si" : "No");
      break;
  }
}

void displayDuskSubMenu() {
  lcd.setCursor(0, 1);
  switch (subMenuPosition) {
    case 0:
      lcd.print(">Duracion: ");
      lcd.print(config.duskDuration);
      lcd.print(" min");
      break;
    case 1:
      lcd.print(">Hora Inicio: ");
      lcd.print(config.duskStartHour);
      lcd.print(":");
      lcd.print(config.duskStartMinute);
      break;
    case 2:
      lcd.print(">Auto Ajuste: ");
      lcd.print(config.autoAdjustEnabled ? "Si" : "No");
      break;
    case 3:
      lcd.print(">Minutos Ajuste: ");
      lcd.print(config.autoAdjustMinutes);
      break;
    case 4:
      lcd.print(">Hora Max Ajuste: ");
      lcd.print(config.autoAdjustMaxHour);
      lcd.print(":00");
      break;
    case 5:
      lcd.print(">Activar Ajuste: ");
      lcd.print(config.autoAdjustEnabled ? "Si" : "No");
      break;
  }
}

void displayMoonLightSubMenu() {
  lcd.setCursor(0, 1);
  switch (subMenuPosition) {
    case 0:
      lcd.print(">Activar: ");
      lcd.print(config.moonLightEnabled ? "Si" : "No");
      break;
    case 1:
      lcd.print(">Intensidad: ");
      lcd.print(config.moonLightIntensity);
      lcd.print("%");
      break;
  }
}

void displayAuxOutputsSubMenu() {
  lcd.setCursor(0, 1);
  switch (subMenuPosition) {
    case 0:
      lcd.print(">Rele 1 Activar: ");
      lcd.print(config.relay1Enabled ? "Si" : "No");
      break;
    case 1:
      lcd.print(">Rele 1 Inicio: ");
      lcd.print(config.relay1StartHour);
      lcd.print(":00");
      break;
    case 2:
      lcd.print(">Rele 1 Fin: ");
      lcd.print(config.relay1EndHour);
      lcd.print(":00");
      break;
    case 3:
      lcd.print(">Rele 2 Activar: ");
      lcd.print(config.relay2Enabled ? "Si" : "No");
      break;
    case 4:
      lcd.print(">Rele 2 Inicio: ");
      lcd.print(config.relay2StartHour);
      lcd.print(":00");
      break;
    case 5:
      lcd.print(">Rele 2 Fin: ");
      lcd.print(config.relay2EndHour);
      lcd.print(":00");
      break;
  }
}

void adjustValue(bool increment) {
  switch (menuPosition) {
    case 0:  // Fecha y Hora
      if (subMenuPosition == 0) {
        adjustDate(increment);
      } else if (subMenuPosition == 1) {
        adjustTime(increment);
      }
      break;
    case 1:  // Amanecer
      adjustDawnSettings(increment);
      break;
    case 2:  // Anochecer
      adjustDuskSettings(increment);
      break;
    case 3:  // Luz Luna
      adjustMoonLightSettings(increment);
      break;
    case 4:  // Salidas Auxiliares
      adjustAuxOutputsSettings(increment);
      break;
  }
  saveConfig();
}

void adjustDate(bool increment) {
  DateTime now = rtc.now();
  if (increment) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day() + 1, now.hour(), now.minute(), now.second()));
  } else {
    rtc.adjust(DateTime(now.year(), now.month(), now.day() - 1, now.hour(), now.minute(), now.second()));
  }
}

void adjustTime(bool increment) {
  DateTime now = rtc.now();
  if (increment) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute() + 1, now.second()));
  } else {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute() - 1, now.second()));
  }
}

void adjustDawnSettings(bool increment) {
  switch (subMenuPosition) {
    case 0:
      config.dawnDuration += increment ? 1 : -1;
      break;
    case 1:
      config.dawnStartHour = (config.dawnStartHour + (increment ? 1 : -1) + 24) % 24;
      break;
    case 2:
      config.dawnStartMinute = (config.dawnStartMinute + (increment ? 1 : -1) + 60) % 60;
      break;
    case 3:
      config.autoAdjustEnabled = !config.autoAdjustEnabled;
      break;
    case 4:
      config.autoAdjustMinutes += increment ? 1 : -1;
      break;
    case 5:
      config.autoAdjustMaxHour = (config.autoAdjustMaxHour + (increment ? 1 : -1) + 24) % 24;
      break;
  }
}

void adjustDuskSettings(bool increment) {
  switch (subMenuPosition) {
    case 0:
      config.duskDuration += increment ? 1 : -1;
      break;
    case 1:
      config.duskStartHour = (config.duskStartHour + (increment ? 1 : -1) + 24) % 24;
      break;
    case 2:
      config.duskStartMinute = (config.duskStartMinute + (increment ? 1 : -1) + 60) % 60;
      break;
    case 3:
      config.autoAdjustEnabled = !config.autoAdjustEnabled;
      break;
    case 4:
      config.autoAdjustMinutes += increment ? 1 : -1;
      break;
    case 5:
      config.autoAdjustMaxHour = (config.autoAdjustMaxHour + (increment ? 1 : -1) + 24) % 24;
      break;
  }
}

void adjustMoonLightSettings(bool increment) {
  switch (subMenuPosition) {
    case 0:
      config.moonLightEnabled = !config.moonLightEnabled;
      break;
    case 1:
      config.moonLightIntensity += increment ? 1 : -1;
      break;
  }
}

void adjustAuxOutputsSettings(bool increment) {
  switch (subMenuPosition) {
    case 0:
      config.relay1Enabled = !config.relay1Enabled;
      break;
    case 1:
      config.relay1StartHour = (config.relay1StartHour + (increment ? 1 : -1) + 24) % 24;
      break;
    case 2:
      config.relay1EndHour = (config.relay1EndHour + (increment ? 1 : -1) + 24) % 24;
      break;
    case 3:
      config.relay2Enabled = !config.relay2Enabled;
      break;
    case 4:
      config.relay2StartHour = (config.relay2StartHour + (increment ? 1 : -1) + 24) % 24;
      break;
    case 5:
      config.relay2EndHour = (config.relay2EndHour + (increment ? 1 : -1) + 24) % 24;
      break;
  }
}

void quickSunrise() {
  for (int i = 0; i <= 255; i++) {
    setLEDStripsBrightness(i);
    delay(48);  // 2 minutos / 256 pasos
  }
}

void quickSunset() {
  for (int i = 255; i >= 0; i--) {
    setLEDStripsBrightness(i);
    delay(732);  // 30 minutos / 256 pasos
  }
}

void controlRelay1(float humidity, DateTime now) {
  if (config.relay1Enabled) {
    if (humidity >= 65) {
      digitalWrite(RELAY1_PIN, HIGH);  // Activa el relé si la humedad es >= 65%
    } else if (humidity <= 50) {
      digitalWrite(RELAY1_PIN, LOW);  // Desactiva el relé si la humedad es <= 50%
    }

    // Control basado en horario
    if (now.hour() >= config.relay1StartHour && now.hour() < config.relay1EndHour) {
      digitalWrite(RELAY1_PIN, HIGH);  // Activa el relé dentro del horario configurado
    } else {
      digitalWrite(RELAY1_PIN, LOW);  // Desactiva el relé fuera del horario configurado
    }
  } else {
    digitalWrite(RELAY1_PIN, LOW);  // Asegúrate de que el relé esté desactivado si no está habilitado
  }
}

void controlRelay2(DateTime now) {
  if (config.relay2Enabled) {
    if (now.hour() >= config.relay2StartHour && now.hour() < config.relay2EndHour) {
      digitalWrite(RELAY2_PIN, HIGH);  // Activa el relé dentro del horario configurado
    } else {
      digitalWrite(RELAY2_PIN, LOW);  // Desactiva el relé fuera del horario configurado
    }
  } else {
    digitalWrite(RELAY2_PIN, LOW);  // Asegúrate de que el relé esté desactivado si no está habilitado
  }
}

void controlMoonLightLED(DateTime now) {
  if (config.moonLightEnabled) {
    int duskEndMinute = config.duskStartHour * 60 + config.duskStartMinute + config.duskDuration;
    int dawnStartMinute = config.dawnStartHour * 60 + config.dawnStartMinute;
    int currentMinute = now.hour() * 60 + now.minute();

    if (currentMinute >= duskEndMinute - 10 && currentMinute <= duskEndMinute + config.duskDuration + 10) {
      int brightness = map(currentMinute, duskEndMinute - 10, duskEndMinute + config.duskDuration + 10, 0, config.moonLightIntensity);
      analogWrite(LED_MOON_PIN, brightness);
    } else if (currentMinute >= dawnStartMinute - 10 && currentMinute <= dawnStartMinute + config.dawnDuration + 10) {
      int brightness = map(currentMinute, dawnStartMinute + config.dawnDuration + 10, dawnStartMinute - 10, 0, config.moonLightIntensity);
      analogWrite(LED_MOON_PIN, brightness);
    } else {
      analogWrite(LED_MOON_PIN, 0);
    }
  }
}

void controlDawnDuskLEDs(DateTime now) {
  int currentMinute = now.hour() * 60 + now.minute();
  int dawnStartMinute = config.dawnStartHour * 60 + config.dawnStartMinute;
  int duskStartMinute = config.duskStartHour * 60 + config.duskStartMinute;

  if (currentMinute >= dawnStartMinute && currentMinute <= dawnStartMinute + config.dawnDuration) {
    int brightness = map(currentMinute, dawnStartMinute, dawnStartMinute + config.dawnDuration, 0, 255);
    setLEDStripsBrightness(brightness);
  } else if (currentMinute >= duskStartMinute && currentMinute <= duskStartMinute + config.duskDuration) {
    int brightness = map(currentMinute, duskStartMinute, duskStartMinute + config.duskDuration, 255, 0);
    setLEDStripsBrightness(brightness);
  } else {
    setLEDStripsBrightness(255);
  }
}

void setLEDStripsBrightness(int brightness) {
  analogWrite(LED_STRIP1_PIN, brightness);
  analogWrite(LED_STRIP2_PIN, brightness);
  analogWrite(LED_STRIP3_PIN, brightness);
  analogWrite(LED_STRIP4_PIN, brightness);
}

void handleAutoAdjust(DateTime now) {
  if (config.autoAdjustEnabled && now.hour() == 0 && now.minute() == 0) {
    config.dawnStartMinute += config.autoAdjustMinutes;
    config.duskStartMinute += config.autoAdjustMinutes;
    if (config.dawnStartHour * 60 + config.dawnStartMinute >= config.autoAdjustMaxHour * 60) {
      config.dawnStartMinute = config.autoAdjustMaxHour * 60;
    }
    if (config.duskStartHour * 60 + config.duskStartMinute >= config.autoAdjustMaxHour * 60) {
      config.duskStartMinute = config.autoAdjustMaxHour * 60;
    }
    saveConfig();
  }
}

void saveConfig() {
  EEPROM.put(EEPROM_ADDRESS, config);
  if (!EEPROM.commit()) {
    Serial.println("Error al guardar la configuración en la EEPROM");
  }
}

void loadConfig() {
  EEPROM.get(EEPROM_ADDRESS, config);
}

Muestra cómo están cableados los botones. Su código establece todos los pines de botón con una resistencia pullup = HIGH (botón no presionado)

¿Podrías mostrar cómo tienes los botones conectados? Con INPUT_PULLUP cada botón debe ir de la entrada a GND. Si están al revés o hay ruido, el LCD creera que están siempre pulsados. Prueba este mini código solo para leer uno y ver si responde bien antes de añadir el resto:

pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
Serial.begin(115200);
void loop() {
  Serial.println(digitalRead(BUTTON_UP_PIN));
  delay(200);
}

Hola a todos, los botones los tengo conectados con un pin al GND y otro al pin asignado en el ESP32. No les pongo resistencias al utilizar las internas.
Un saludo.